23 | $endif$
24 |
25 | $body$
26 |
27 | $for(include-after)$
28 | $include-after$
29 | $endfor$
30 |
--------------------------------------------------------------------------------
/blogdown.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 | ProjectId: 7de02ffc-a200-4611-bc07-18b265ee1be3
3 |
4 | RestoreWorkspace: Default
5 | SaveWorkspace: Default
6 | AlwaysSaveHistory: Default
7 |
8 | EnableCodeIndexing: Yes
9 | UseSpacesForTab: Yes
10 | NumSpacesForTab: 2
11 | Encoding: UTF-8
12 |
13 | RnwWeave: knitr
14 | LaTeX: XeLaTeX
15 |
16 | AutoAppendNewline: Yes
17 | StripTrailingWhitespace: Yes
18 |
19 | BuildType: Package
20 | PackageInstallArgs: -v && Rscript -e "Rd2roxygen::rab(build=F,install=T)"
21 | PackageBuildArgs: -v && Rscript -e "Rd2roxygen::rab(build=T,install=F)"
22 | PackageCheckArgs: --as-cran
23 |
--------------------------------------------------------------------------------
/tests/test-cran/test-render.R:
--------------------------------------------------------------------------------
1 | library(testit)
2 |
3 | assert('run_pandoc() find when Pandoc needs to convert', {
4 | (run_pandoc(c("nothing special", "requiring render")) %==% FALSE)
5 | (run_pandoc(c("```{=html}", "using raw block content", "```")) %==% TRUE)
6 | (run_pandoc(c("With inline raw `this works too`{=html}")) %==% TRUE)
7 | (run_pandoc(c("Using bib", "references:")) %==% TRUE)
8 | (run_pandoc(c("Using bib", "bibliography: test.bib")) %==% TRUE)
9 | opts = options(blogdown.markdown.format = "gfm")
10 | (run_pandoc(c("nothing special", "but option is set")) %==% TRUE)
11 | options(opts)
12 | })
13 |
14 |
--------------------------------------------------------------------------------
/docs/book.bib:
--------------------------------------------------------------------------------
1 | @Book{xie2015,
2 | title = {Dynamic Documents with {R} and knitr},
3 | author = {Yihui Xie},
4 | publisher = {Chapman and Hall/CRC},
5 | address = {Boca Raton, Florida},
6 | year = {2015},
7 | edition = {2nd},
8 | note = {ISBN 978-1498716963},
9 | url = {http://yihui.org/knitr/},
10 | }
11 | @Book{xie2016,
12 | title = {bookdown: Authoring Books and Technical Documents with {R} Markdown},
13 | author = {Yihui Xie},
14 | publisher = {Chapman and Hall/CRC},
15 | address = {Boca Raton, Florida},
16 | year = {2016},
17 | note = {ISBN 978-1138700109},
18 | url = {https://github.com/rstudio/bookdown},
19 | }
20 |
--------------------------------------------------------------------------------
/pkgdown/templates/in-header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 | {{#includes}}{{{in_header}}}{{/includes}}
15 |
--------------------------------------------------------------------------------
/tests/test-cran/test-addin.R:
--------------------------------------------------------------------------------
1 | assert('quote_poem() adds > to the beginning of every line, and two trailing spaces to every line', {
2 |
3 | (quote_poem(" ") %==% " ")
4 |
5 | (quote_poem("some text.") %==% '> some text.')
6 |
7 | (quote_poem("some text.\nsome more text.\neven more text.")) %==%
8 | "> some text. \n> some more text. \n> even more text."
9 |
10 | (quote_poem("some text. \nsome more text. \neven more text.")) %==%
11 | "> some text. \n> some more text. \n> even more text."
12 |
13 | (quote_poem("some text. \nsome more text. \n\neven more text.")) %==%
14 | "> some text. \n> some more text.\n>\n> even more text."
15 |
16 | })
17 |
--------------------------------------------------------------------------------
/man/config_Rprofile.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{config_Rprofile}
4 | \alias{config_Rprofile}
5 | \title{Create or modify the \file{.Rprofile} file for a website project}
6 | \usage{
7 | config_Rprofile()
8 | }
9 | \value{
10 | As a side-effect, the file \file{.Rprofile} is created or modified.
11 | }
12 | \description{
13 | If the file \file{.Rprofile} does not exist in the current directory, copy
14 | the file from the \file{resources} directory of \pkg{blogdown}. If the option
15 | \code{blogdown.hugo.version} is not found in this file, append
16 | \code{options(blogdown.hugo.version = "VERSION")} to it, where \code{VERSION}
17 | is obtained from \code{\link{hugo_version}()}.
18 | }
19 |
--------------------------------------------------------------------------------
/docs/_render.R:
--------------------------------------------------------------------------------
1 | quiet = "--quiet" %in% commandArgs(FALSE)
2 | formats = commandArgs(TRUE)
3 |
4 | src = (function() {
5 | attr(body(sys.function()), 'srcfile')
6 | })()$filename
7 | if (is.null(src) || src == '') src = '.'
8 | owd = setwd(dirname(src))
9 |
10 | # provide default formats if necessary
11 | if (length(formats) == 0) formats = c(
12 | 'bookdown::pdf_book', 'bookdown::epub_book', 'bookdown::gitbook'
13 | )
14 | # render the book to all formats unless they are specified via command-line args
15 | for (fmt in formats) {
16 | cmd = sprintf("bookdown::render_book('index.Rmd', '%s', quiet = %s)", fmt, quiet)
17 | res = xfun::Rscript(c('-e', shQuote(cmd)))
18 | if (res != 0) stop('Failed to compile the book to ', fmt)
19 | }
20 |
21 | setwd(owd)
22 |
--------------------------------------------------------------------------------
/man/blogdown.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/package.R
3 | \name{blogdown}
4 | \alias{blogdown}
5 | \alias{blogdown-package}
6 | \title{The \pkg{blogdown} package}
7 | \description{
8 | The comprehensive documentation of this package is the book \bold{blogdown:
9 | Creating Websites with R Markdown}
10 | (\url{https://bookdown.org/yihui/blogdown/}). You are expected to read at
11 | least the first chapter. If you are really busy or do not care about an
12 | introduction to \pkg{blogdown} (e.g., you are very familiar with creating
13 | websites), set your working directory to an empty directory, and run
14 | \code{blogdown::\link{new_site}()} to get started right away.
15 | }
16 | \examples{
17 | if (interactive()) blogdown::new_site()
18 | }
19 |
--------------------------------------------------------------------------------
/pkgdown/assets/snippets/snippets.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | function loadSnippet(snippet, mode) {
4 | mode = mode || "markdown";
5 | $("#" + snippet).addClass("snippet");
6 | var editor = ace.edit(snippet);
7 | editor.setHighlightActiveLine(false);
8 | editor.setShowPrintMargin(false);
9 | editor.setReadOnly(true);
10 | editor.setShowFoldWidgets(false);
11 | editor.renderer.setDisplayIndentGuides(false);
12 | editor.setTheme("ace/theme/textmate");
13 | editor.$blockScrolling = Infinity;
14 | editor.session.setMode("ace/mode/" + mode);
15 | editor.session.getSelection().clearSelection();
16 |
17 | $.get("snippets/" + snippet + ".md", function(data) {
18 | editor.setValue(data, -1);
19 | editor.setOptions({
20 | maxLines: editor.session.getLength()
21 | });
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | To compile this book, open `index.Rmd` in the RStudio IDE, install the **blogdown** package from Github, and click the RStudio Addin "Preview Book". You should be able to preview the HTML version of the book.
2 |
3 | It is trickier to compile it to PDF. You need to install LaTeX. I recommend [TinyTeX](https://yihui.org/tinytex/):
4 |
5 | ```r
6 | tinytex::install_tinytex()
7 | ```
8 |
9 | Then download and install three fonts from [Google Fonts](https://fonts.google.com/?query=source&selection.family=Alegreya|Alegreya+SC|Source+Code+Pro): Alegreya, Alegreya SC, and Source Code Pro. Run `make pdf` (if `make` is available) or `Rscript _render.R "bookdown::pdf_book"` in the `docs/` directory. Install LaTeX packages if missing. If you're using a Mac with OS X 10.8 or later, install [XQuartz](https://www.xquartz.org).
10 |
--------------------------------------------------------------------------------
/inst/CITATION:
--------------------------------------------------------------------------------
1 | year = sub('.*(2[[:digit:]]{3})-.*', '\\1', meta$Date, perl = TRUE)
2 | vers = paste('R package version', meta$Version)
3 | if (length(year) == 0) year = format(Sys.Date(), '%Y')
4 |
5 | bibentry(
6 | 'Manual',
7 | title = paste(meta$Package, meta$Title, sep =": "),
8 | author = Filter(function(p) 'aut' %in% p$role, as.person(meta$Author)),
9 | year = year,
10 | note = vers,
11 | url = strsplit(meta$URL, ',')[[1]][1]
12 | )
13 |
14 | bibentry(
15 | 'Book',
16 | title = 'blogdown: Creating Websites with {R} Markdown',
17 | author = as.person('Yihui Xie [aut], Alison Presmanes Hill [aut], Amber Thomas [aut]'),
18 | publisher = 'Chapman and Hall/CRC',
19 | address = 'Boca Raton, Florida',
20 | year = '2017',
21 | isbn = '978-0815363729',
22 | url = 'https://bookdown.org/yihui/blogdown/'
23 | )
24 |
--------------------------------------------------------------------------------
/man/build_dir.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{build_dir}
4 | \alias{build_dir}
5 | \title{Build all Rmd files under a directory}
6 | \usage{
7 | build_dir(dir = ".", force = FALSE, ignore = "[.]Rproj$")
8 | }
9 | \arguments{
10 | \item{dir}{A directory path.}
11 |
12 | \item{force}{Whether to force building all Rmd files. By default, an Rmd file
13 | is built only if it is newer than its output file(s).}
14 |
15 | \item{ignore}{A regular expression to match output filenames that should be
16 | ignored when testing if the modification time of the Rmd source file is
17 | newer than its output files.}
18 | }
19 | \description{
20 | List all Rmd files recursively under a directory, and compile them using
21 | \code{\link[rmarkdown:render]{rmarkdown::render()}}.
22 | }
23 |
--------------------------------------------------------------------------------
/docs/WIP.html:
--------------------------------------------------------------------------------
1 |
2 |
A note from the authors: Some of the information and instructions in this book are now out of date because of changes to Hugo and the blogdown package.
3 | If you have suggestions for improving this book, please file an issue in our GitHub repository.
4 | Thanks for your patience while we work to update the book, and please stay tuned for the revised version!
13 | download: [pdf, epub]
14 | edit: https://github.com/rstudio/blogdown/edit/main/docs/%s
15 | sharing:
16 | github: yes
17 | facebook: no
18 | bookdown::pdf_book:
19 | includes:
20 | in_header: latex/preamble.tex
21 | before_body: latex/before_body.tex
22 | after_body: latex/after_body.tex
23 | keep_tex: yes
24 | dev: "cairo_pdf"
25 | latex_engine: xelatex
26 | citation_package: natbib
27 | template: null
28 | pandoc_args: --top-level-division=chapter
29 | toc_depth: 3
30 | toc_unnumbered: no
31 | toc_appendix: yes
32 | quote_footer: ["\\VA{", "}{}"]
33 | bookdown::epub_book:
34 | stylesheet: css/style.css
35 |
--------------------------------------------------------------------------------
/inst/resources/2020-12-01-r-rmarkdown.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Hello R Markdown"
3 | author: "Frida Gomam"
4 | date: 2020-12-01T21:13:14-05:00
5 | categories: ["R"]
6 | tags: ["R Markdown", "plot", "regression"]
7 | ---
8 |
9 | ```{r setup, include=FALSE}
10 | knitr::opts_chunk$set(collapse = TRUE)
11 | ```
12 |
13 | # R Markdown
14 |
15 | This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see .
16 |
17 | You can embed an R code chunk like this:
18 |
19 | ```{r cars}
20 | summary(cars)
21 | fit <- lm(dist ~ speed, data = cars)
22 | fit
23 | ```
24 |
25 | # Including Plots
26 |
27 | You can also embed plots. See Figure \@ref(fig:pie) for example:
28 |
29 | ```{r pie, fig.cap='A fancy pie chart.', tidy=FALSE}
30 | par(mar = c(0, 1, 0, 1))
31 | pie(
32 | c(280, 60, 20),
33 | c('Sky', 'Sunny side of pyramid', 'Shady side of pyramid'),
34 | col = c('#0292D8', '#F7EA39', '#C4B632'),
35 | init.angle = -50, border = NA
36 | )
37 | ```
38 |
--------------------------------------------------------------------------------
/man/dep_path.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{dep_path}
4 | \alias{dep_path}
5 | \title{A helper function to return a dependency path name}
6 | \usage{
7 | dep_path(default = knitr::opts_chunk$get("fig.path"))
8 | }
9 | \arguments{
10 | \item{default}{Return this default value when this function is called outside
11 | of a \pkg{knitr} code chunk.}
12 | }
13 | \value{
14 | A character string of the \code{default} value (outside \pkg{knitr}),
15 | or a path consisting of the \pkg{knitr} figure path appended by the current
16 | chunk label.
17 | }
18 | \description{
19 | In most cases, \pkg{blogdown} can process images and HTML widgets
20 | automatically generated from code chunks (they will be moved to the
21 | \code{static/} folder by default), but it may fail to recognize dependency
22 | files generated to other paths. This function returns a path that you can use
23 | for your output files, so that \pkg{blogdown} knows that they should be be
24 | processed, too. It is designed to be used in a \pkg{knitr} code chunk.
25 | }
26 |
--------------------------------------------------------------------------------
/man/hugo_installers.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/install.R
3 | \name{hugo_installers}
4 | \alias{hugo_installers}
5 | \title{Available Hugo installers of a version}
6 | \usage{
7 | hugo_installers(version = "latest")
8 | }
9 | \arguments{
10 | \item{version}{A version number. The default is to automatically detect the
11 | latest version. Versions before v0.17 are not supported.}
12 | }
13 | \value{
14 | A data frame containing columns \code{os} (operating system),
15 | \code{arch} (architecture), and \code{extended} (extended version or not).
16 | If your R version is lower than 4.1.0, a character vector of the installer
17 | filenames will be returned instead.
18 | }
19 | \description{
20 | Given a version number, return the information of available installers. If
21 | \code{\link{install_hugo}()} fails, you may run this function to check the
22 | available installers and obtain their \code{os}/\code{arch} info.
23 | }
24 | \examples{\dontshow{if (interactive()) withAutoprint(\{ # examplesIf}
25 | blogdown::hugo_installers()
26 | blogdown::hugo_installers("0.89.0")
27 | blogdown::hugo_installers("0.17")
28 | \dontshow{\}) # examplesIf}
29 | }
30 |
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | p.caption {
2 | color: #777;
3 | margin-top: 10px;
4 | }
5 | p code {
6 | white-space: inherit;
7 | }
8 | pre {
9 | word-break: normal;
10 | word-wrap: normal;
11 | }
12 | pre code {
13 | white-space: inherit;
14 | }
15 | p.flushright {
16 | text-align: right;
17 | }
18 | blockquote > p:last-child {
19 | text-align: right;
20 | }
21 | blockquote > p:first-child {
22 | text-align: inherit;
23 | }
24 | .header-section-number {
25 | padding-right: .2em;
26 | font-weight: 500;
27 | }
28 | .level1 .header-section-number {
29 | display: inline-block;
30 | border-bottom: 3px solid;
31 | }
32 | .level1 h1 {
33 | border-bottom: 1px solid;
34 | }
35 | h1, h2, h3, h4, h5, h6 {
36 | font-weight: normal;
37 | }
38 | h1.title {
39 | font-weight: 700;
40 | }
41 | .book .book-body .page-wrapper .page-inner section.normal strong {
42 | font-weight: 600;
43 | }
44 |
45 | .caution {
46 | border: 4px #096B7263;
47 | border-style: dashed solid;
48 | padding: 1em;
49 | margin: 1em 0;
50 | padding-left: 100px;
51 | background-size: 70px;
52 | background-repeat: no-repeat;
53 | background-position: 15px center;
54 | min-height: 120px;
55 | color: #096B72;
56 | background-color: #e6f3fc80;
57 | background-image: url("../images/caution.png");
58 | }
59 |
--------------------------------------------------------------------------------
/inst/rstudio/templates/project/skeleton.dcf:
--------------------------------------------------------------------------------
1 | Title: Website using blogdown
2 | Binding: blogdown_skeleton
3 | Subtitle: Create a new website using Hugo and blogdown
4 | Caption: Create a new website using Hugo and blogdown
5 | Icon: hugo-logo.png
6 |
7 | Parameter: theme
8 | Widget: TextInput
9 | Label: Hugo theme
10 | Default: yihui/hugo-lithium
11 |
12 | Parameter: format
13 | Widget: CheckboxInput
14 | Label: Convert the site config file to YAML
15 | Default: On
16 |
17 | Parameter: to_yaml
18 | Widget: CheckboxInput
19 | Label: Convert all post metadata to YAML
20 | Default: On
21 |
22 | Parameter: sample
23 | Widget: CheckboxInput
24 | Label: Add sample blog posts
25 | Default: On
26 |
27 | Parameter: theme_example
28 | Widget: CheckboxInput
29 | Label: Add the example site from the theme
30 | Default: On
31 |
32 | Parameter: empty_dirs
33 | Widget: CheckboxInput
34 | Label: Keep empty directories
35 | Default: Off
36 | Position: right
37 |
38 | Parameter: netlify
39 | Widget: CheckboxInput
40 | Label: Create netlify.toml
41 | Default: On
42 | Position: right
43 |
44 | Parameter: .Rprofile
45 | Widget: CheckboxInput
46 | Label: Create .Rprofile
47 | Default: On
48 | Position: right
49 |
50 | Parameter: install_hugo
51 | Widget: CheckboxInput
52 | Label: Install Hugo if not installed
53 | Default: On
54 | Position: right
55 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Feature request"
3 | about: Suggest an idea for this package
4 | title: '[FR]'
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 |
29 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to blogdown
2 |
3 | We welcome contributions to the **blogdown** package.
4 |
5 | You can contribute in many ways:
6 |
7 | * By opening issues to give feedback and share ideas.
8 | * By fixing typos in documentations
9 | * By submitting Pull Request (PR) to fix some opened issues
10 | * By submitting Pull Request (PR) to suggest some new features. (It is considered good practice to open issues before to discuss ideas)
11 |
12 | ## To submit a contribution using a Pull Request:
13 |
14 | 1. [Fork](https://github.com/rstudio/blogdown/fork) the repository and make your changes in a new branch specific to the PR. It is ok to edit a file in this repository using the `Edit` button on Github if the change is simple enough.
15 |
16 | 2. For significant changes (e.g not required for fixing typos), ensure that you have signed the [individual](https://www.rstudio.com/wp-content/uploads/2014/06/rstudioindividualcontributoragreement.pdf) or [corporate](https://www.rstudio.com/wp-content/uploads/2014/06/rstudiocorporatecontributoragreement.pdf) contributor agreement as appropriate. You can send the signed copy to .
17 |
18 | 3. Submit the [pull request](https://help.github.com/articles/using-pull-requests). It is ok to submit as draft if you are still working on it but would like some feedback from us. It always good to share in the open that you are working on it.
19 |
20 | We'll try to be as responsive as possible in reviewing and accepting pull requests. Appreciate your contributions very much!
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Bug report"
3 | about: Report an error or unexpected behavior you saw while using this package
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 |
37 |
--------------------------------------------------------------------------------
/man/find_yaml.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{find_yaml}
4 | \alias{find_yaml}
5 | \alias{find_tags}
6 | \alias{find_categories}
7 | \alias{count_yaml}
8 | \title{Find posts containing the specified metadata}
9 | \usage{
10 | find_yaml(field = character(), value = character(), open = FALSE)
11 |
12 | find_tags(value = character(), open = FALSE)
13 |
14 | find_categories(value = character(), open = FALSE)
15 |
16 | count_yaml(fields = c("categories", "tags"), sort_by_count = TRUE)
17 | }
18 | \arguments{
19 | \item{field, fields}{A character vector of YAML field names.}
20 |
21 | \item{value}{A vector of the field values to be matched.}
22 |
23 | \item{open}{Whether to open the matched files automatically.}
24 |
25 | \item{sort_by_count}{Whether to sort the frequency tables by counts.}
26 | }
27 | \value{
28 | \code{find_yaml()} returns a character vector of filenames;
29 | \code{count_yaml()} returns a list of frequency tables.
30 | }
31 | \description{
32 | Given a YAML field name, find the (R) Markdown files that contain this field
33 | and its value contains any of the specified values. Functions
34 | \code{find_tags()} and \code{find_categories()} are wrappers of
35 | \code{find_yaml()} with \code{field = 'tags'} and \code{field =
36 | 'categories'}, respectively; \code{count_fields()} returns the frequency
37 | tables of the specified YAML fields, such as the counts of tags and
38 | categories.
39 | }
40 | \examples{
41 | library(blogdown)
42 | find_tags(c("time-series", "support vector machine"))
43 | find_categories("Statistics")
44 |
45 | count_yaml(sort_by_count = FALSE)
46 | }
47 |
--------------------------------------------------------------------------------
/vignettes/articles/examples.yml:
--------------------------------------------------------------------------------
1 | - href: https://metadocencia.netlify.app/
2 | source: https://github.com/MetaDocencia/SitioWeb
3 | title: MetaDocencia
4 | img: images/metadocencia.netlify.png
5 | showcase: yes
6 | - href: https://mariadermit.netlify.app/
7 | source: https://github.com/demar01/mariadermit
8 | title: English site
9 | img: images/mariadermit.netlify.png
10 | showcase: yes
11 | - href: https://shinydevseries.com/
12 | source: https://github.com/shinydevseries/shinydevseries_site
13 | title: Shiny Developer Series
14 | img: images/shinydevseries.png
15 | showcase: yes
16 | - href: https://r-podcast.org/
17 | source: https://github.com/rbind/r-podcast
18 | title: The R-Podcast
19 | img: images/r-podcast.png
20 | showcase: yes
21 | - href: https://isabella-b.com/
22 | source: https://github.com/isabellabenabaye/isabella-b.com
23 | title: Isabella Benabaye
24 | img: images/isabella-b.png
25 | showcase: yes
26 | - href: https://www.tidymodels.org/
27 | source: https://github.com/tidymodels/tidymodels.org
28 | title: Tidymodels
29 | img: images/www.tidymodels.png
30 | showcase: yes
31 | - href: https://livefreeordichotomize.com/
32 | source: https://github.com/LFOD/real-blog
33 | title: Live Free or Dichotomize - Live Free or Dichotomize
34 | img: images/livefreeordichotomize.png
35 | showcase: yes
36 | - href: https://hugo-apero-docs.netlify.app/
37 | source: https://github.com/hugo-apero/hugo-apero-docs
38 | title: Hugo Apéro
39 | img: images/hugo-apero-docs.netlify.png
40 | showcase: yes
41 | - href: https://prose.yihui.org/
42 | source: https://github.com/yihui/hugo-prose/tree/master/exampleSite
43 | title: Home | Hugo Prose
44 | img: images/prose.yihui.png
45 | showcase: yes
46 |
--------------------------------------------------------------------------------
/tests/test-cran/test-clean.R:
--------------------------------------------------------------------------------
1 | library(testit)
2 |
3 | assert('remove_extra_empty_lines() replaces two or more empty lines with one', {
4 |
5 | x0 = 'a line with some new lines.\n\n and some text.'
6 | x1 = c('a line with some new lines.', '', '', '', ' and some text.')
7 | x2 = x1[-2]
8 | x3 = x1[-(2:3)]
9 |
10 | (remove_extra_empty_lines(x = x1) %==% x0)
11 | (remove_extra_empty_lines(x = x2) %==% x0)
12 | (remove_extra_empty_lines(x = x3) %==% x0)
13 |
14 | })
15 |
16 |
17 | assert('process_bare_urls() replaces [url](url) with ', {
18 |
19 | x4 = '[url](url)'
20 | x5 = 'some text before [url](url) and after'
21 |
22 | (process_bare_urls(x = x4) %==% '')
23 | (process_bare_urls(x = x5) %==% 'some text before and after')
24 |
25 | })
26 |
27 |
28 | assert('normalize_chars() converts curly quotes to straight quotes', {
29 |
30 | x6 = intToUtf8(8216:8217)
31 | x7 = intToUtf8(8220:8221)
32 | x8 = intToUtf8(8230)
33 | x9 = intToUtf8(160)
34 |
35 | (normalize_chars(x = x6) %==% "''")
36 | (normalize_chars(x = x7) %==% '""')
37 | (normalize_chars(x = x8) %==% '...')
38 | (normalize_chars(x = x9) %==% ' ')
39 |
40 | })
41 |
42 |
43 | assert('remove_highlight_tags() cleans up code blocks syntax highlighted by Pandoc', {
44 |
45 | x10 = ' some code'
46 | x11 = ' some span'
47 |
48 | (remove_highlight_tags(x = x10) %==% ' some code')
49 | (remove_highlight_tags(x = x11) %==% ' some span')
50 |
51 | })
52 |
53 |
54 | assert('fix_img_tags() converts to ', {
55 |
56 | x12 = ''
57 | x13 = 'text before and after'
58 |
59 | (fix_img_tags(x = x12) %==% '')
60 | (fix_img_tags(x = x13) %==% 'text before and after')
61 |
62 | })
63 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | export(build_dir)
4 | export(build_site)
5 | export(bundle_site)
6 | export(check_config)
7 | export(check_content)
8 | export(check_gitignore)
9 | export(check_hugo)
10 | export(check_netlify)
11 | export(check_site)
12 | export(check_vercel)
13 | export(clean_duplicates)
14 | export(config_Rprofile)
15 | export(config_netlify)
16 | export(config_vercel)
17 | export(count_yaml)
18 | export(dep_path)
19 | export(edit_draft)
20 | export(filter_md5sum)
21 | export(filter_newfile)
22 | export(filter_timestamp)
23 | export(find_categories)
24 | export(find_hugo)
25 | export(find_tags)
26 | export(find_yaml)
27 | export(html_page)
28 | export(hugo_available)
29 | export(hugo_build)
30 | export(hugo_cmd)
31 | export(hugo_convert)
32 | export(hugo_installers)
33 | export(hugo_server)
34 | export(hugo_version)
35 | export(install_hugo)
36 | export(install_theme)
37 | export(new_content)
38 | export(new_post)
39 | export(new_site)
40 | export(read_toml)
41 | export(remove_hugo)
42 | export(serve_site)
43 | export(shortcode)
44 | export(shortcode_close)
45 | export(shortcode_html)
46 | export(shortcode_open)
47 | export(shortcodes)
48 | export(stop_server)
49 | export(toml2yaml)
50 | export(update_hugo)
51 | export(write_toml)
52 | export(yaml2toml)
53 | import(stats)
54 | import(utils)
55 | importFrom(xfun,del_empty_dir)
56 | importFrom(xfun,dir_create)
57 | importFrom(xfun,dir_exists)
58 | importFrom(xfun,existing_files)
59 | importFrom(xfun,exit_call)
60 | importFrom(xfun,file_exists)
61 | importFrom(xfun,file_ext)
62 | importFrom(xfun,in_dir)
63 | importFrom(xfun,is_macos)
64 | importFrom(xfun,is_windows)
65 | importFrom(xfun,msg_cat)
66 | importFrom(xfun,proc_kill)
67 | importFrom(xfun,read_utf8)
68 | importFrom(xfun,set_envvar)
69 | importFrom(xfun,write_utf8)
70 |
--------------------------------------------------------------------------------
/R/clean.R:
--------------------------------------------------------------------------------
1 | # these functions are mainly for cleaning up Markdown files generated by Exitwp
2 | # with the XML file exported from WordPress
3 |
4 | # a wrapper function to read a file as UTF-8, process the text, and write back
5 | process_file = function(f, FUN, x = read_utf8(f)) {
6 | xfun::process_file(f, FUN, x)
7 | }
8 |
9 | # replace three or more \n with two, i.e. two or more empty lines with one
10 | remove_extra_empty_lines = function(...) xfun::process_file(..., fun = function(x) {
11 | x = paste(gsub('\\s+$', '', x), collapse = '\n')
12 | trim_ws(gsub('\n{3,}', '\n\n', x))
13 | })
14 |
15 | # replace [url](url) with
16 | process_bare_urls = function(...) xfun::process_file(..., fun = function(x) {
17 | gsub('\\[([^]]+)]\\(\\1/?\\)', '<\\1>', x)
18 | })
19 |
20 | normalize_chars = function(...) xfun::process_file(..., fun = function(x) {
21 | # curly single and double quotes to straight quotes
22 | x = gsub(paste0('[', intToUtf8(8216:8217), ']'), "'", x)
23 | x = gsub(paste0('[', intToUtf8(8220:8221), ']'), '"', x)
24 | x = gsub(intToUtf8(8230), '...', x) # ellipses
25 | x = gsub(intToUtf8(160), ' ', x) # zero-width space
26 | x
27 | })
28 |
29 | # clean up code blocks that have been syntax highlighted by Pandoc
30 | remove_highlight_tags = function(...) xfun::process_file(..., fun = function(x) {
31 | clean = function(x) {
32 | # remove the tags
33 | x = gsub('^(\\s+)(.*)', '\\1\\3', x)
34 | x = gsub('\\s*$', '', x)
35 | # remove
36 | x = gsub('?span([^>])*>', '', x)
37 | x
38 | }
39 | # only process lines that are indented by at least 4 spaces
40 | i = grep('^( {4,}.*)', x)
41 | x[i] = clean(x[i])
42 | x
43 | })
44 |
45 | # to
46 | fix_img_tags = function(...) xfun::process_file(..., fun = function(x) {
47 | gsub('>', ' />', x)
48 | })
49 |
--------------------------------------------------------------------------------
/man/bundle_site.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/hugo.R
3 | \name{bundle_site}
4 | \alias{bundle_site}
5 | \title{Convert post files to leaf bundles}
6 | \usage{
7 | bundle_site(dir = site_root(), output)
8 | }
9 | \arguments{
10 | \item{dir}{The root directory of the website project (should contain a
11 | \file{content/} folder).}
12 |
13 | \item{output}{The output directory. If not provided, a suffix \file{-bundle}
14 | is added to the website root directory name. For example, the default
15 | output directory for the site under \file{~/Documents/test} is
16 | \file{~/Documents/test-bundle}. You can specify the output directory to be
17 | identical to the website root directory, so files will be moved within the
18 | same directory, but please remember that you will not be able to undo
19 | \code{bundle_site()}. You should modify the website in place \emph{only if
20 | you have a backup for this directory or it is under version control}.}
21 | }
22 | \description{
23 | For a post with the path \file{content/path/to/my-post.md}, it will be moved
24 | to \file{content/path/to/my-post/index.md}, so it becomes the index file of a
25 | leaf bundle of Hugo. This also applies to files with extensions \file{.Rmd}
26 | and \file{.Rmarkdown}.
27 | }
28 | \note{
29 | This function only moves (R) Markdown source files. If these files use
30 | resource files under the \file{static/} folder, these resources will not be
31 | moved into the \file{content/} folder. You need to manually move them, and
32 | adjust their paths in the (R) Markdown source files accordingly.
33 | }
34 | \examples{
35 | \dontrun{
36 | blogdown::bundle_site(".", "../new-site/")
37 | blogdown::bundle_site(".", ".") # move files within the current working directory
38 | }
39 | }
40 | \references{
41 | Learn more about Hugo's leaf bundles at
42 | \url{https://gohugo.io/content-management/page-bundles/}.
43 | }
44 |
--------------------------------------------------------------------------------
/docs/latex/preamble.tex:
--------------------------------------------------------------------------------
1 | \usepackage{booktabs}
2 | \usepackage{longtable}
3 | \usepackage[bf,singlelinecheck=off]{caption}
4 |
5 | \usepackage{Alegreya}
6 | \usepackage[scale=.7]{sourcecodepro}
7 |
8 | \usepackage{framed,color}
9 | \definecolor{shadecolor}{RGB}{248,248,248}
10 |
11 | \renewcommand{\textfraction}{0.05}
12 | \renewcommand{\topfraction}{0.8}
13 | \renewcommand{\bottomfraction}{0.8}
14 | \renewcommand{\floatpagefraction}{0.75}
15 |
16 | \renewenvironment{quote}{\begin{VF}}{\end{VF}}
17 | \let\oldhref\href
18 | \renewcommand{\href}[2]{#2\footnote{\url{#1}}}
19 |
20 | \ifxetex
21 | \usepackage{letltxmacro}
22 | \setlength{\XeTeXLinkMargin}{1pt}
23 | \LetLtxMacro\SavedIncludeGraphics\includegraphics
24 | \def\includegraphics#1#{% #1 catches optional stuff (star/opt. arg.)
25 | \IncludeGraphicsAux{#1}%
26 | }%
27 | \newcommand*{\IncludeGraphicsAux}[2]{%
28 | \XeTeXLinkBox{%
29 | \SavedIncludeGraphics#1{#2}%
30 | }%
31 | }%
32 | \fi
33 |
34 | \makeatletter
35 | \newenvironment{kframe}{%
36 | \medskip{}
37 | \setlength{\fboxsep}{.8em}
38 | \def\at@end@of@kframe{}%
39 | \ifinner\ifhmode%
40 | \def\at@end@of@kframe{\end{minipage}}%
41 | \begin{minipage}{\columnwidth}%
42 | \fi\fi%
43 | \def\FrameCommand##1{\hskip\@totalleftmargin \hskip-\fboxsep
44 | \colorbox{shadecolor}{##1}\hskip-\fboxsep
45 | % There is no \\@totalrightmargin, so:
46 | \hskip-\linewidth \hskip-\@totalleftmargin \hskip\columnwidth}%
47 | \MakeFramed {\advance\hsize-\width
48 | \@totalleftmargin\z@ \linewidth\hsize
49 | \@setminipage}}%
50 | {\par\unskip\endMakeFramed%
51 | \at@end@of@kframe}
52 | \makeatother
53 |
54 | \renewenvironment{Shaded}{\begin{kframe}}{\end{kframe}}
55 |
56 | \usepackage{makeidx}
57 | \makeindex
58 |
59 | \urlstyle{tt}
60 |
61 | \usepackage{amsthm}
62 | \makeatletter
63 | \def\thm@space@setup{%
64 | \thm@preskip=8pt plus 2pt minus 4pt
65 | \thm@postskip=\thm@preskip
66 | }
67 | \makeatother
68 |
69 | \frontmatter
70 |
--------------------------------------------------------------------------------
/man/install_theme.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/hugo.R
3 | \name{install_theme}
4 | \alias{install_theme}
5 | \title{Install a Hugo theme from Github}
6 | \usage{
7 | install_theme(
8 | theme,
9 | hostname = "github.com",
10 | theme_example = FALSE,
11 | update_config = TRUE,
12 | force = FALSE,
13 | update_hugo = TRUE
14 | )
15 | }
16 | \arguments{
17 | \item{theme}{A Hugo theme on Github (a character string of the form
18 | \code{user/repo}, and you can optionally specify a GIT branch or tag name
19 | after \code{@}, i.e. \code{theme} can be of the form
20 | \code{user/repo@branch}). You can also specify a full URL to the zip file
21 | or tarball of the theme. If \code{theme = NA}, no themes will be installed,
22 | and you have to manually install a theme.}
23 |
24 | \item{hostname}{Where to find the theme. Defaults to \code{github.com};
25 | specify if you wish to use an instance of GitHub Enterprise. You can also
26 | specify the full URL of the zip file or tarball in \code{theme}, in which
27 | case this argument is ignored.}
28 |
29 | \item{theme_example}{Whether to copy the example in the \file{exampleSite}
30 | directory if it exists in the theme. Not all themes provide example sites.}
31 |
32 | \item{update_config}{Whether to update the \code{theme} option in the site
33 | configurations.}
34 |
35 | \item{force}{Whether to override the existing theme of the same name. If you
36 | have made changes to this existing theme, your changes will be lost when
37 | \code{force = TRUE}! Please consider backing up the theme by renaming it
38 | before you try \code{force = TRUE}.}
39 |
40 | \item{update_hugo}{Whether to automatically update Hugo if the theme requires
41 | a higher version of Hugo than the existing version in your system.}
42 | }
43 | \description{
44 | Download the specified theme from Github and install to the \file{themes}
45 | directory. Available themes are listed at \url{https://themes.gohugo.io}.
46 | }
47 |
--------------------------------------------------------------------------------
/R/package.R:
--------------------------------------------------------------------------------
1 | #' The \pkg{blogdown} package
2 | #'
3 | #' The comprehensive documentation of this package is the book \bold{blogdown:
4 | #' Creating Websites with R Markdown}
5 | #' (\url{https://bookdown.org/yihui/blogdown/}). You are expected to read at
6 | #' least the first chapter. If you are really busy or do not care about an
7 | #' introduction to \pkg{blogdown} (e.g., you are very familiar with creating
8 | #' websites), set your working directory to an empty directory, and run
9 | #' \code{blogdown::\link{new_site}()} to get started right away.
10 | #' @name blogdown
11 | #' @aliases blogdown-package
12 | #' @import utils
13 | #' @import stats
14 | #' @importFrom xfun in_dir read_utf8 write_utf8 is_windows is_macos
15 | #' file_exists dir_exists file_ext msg_cat dir_create del_empty_dir proc_kill
16 | #' set_envvar exit_call existing_files
17 | #' @examples if (interactive()) blogdown::new_site()
18 | NULL
19 |
20 | with_ext = function(...) xfun::with_ext(...)
21 | fetch_yaml = function(f) bookdown:::fetch_yaml(read_utf8(f))
22 |
23 | `%n%` = knitr:::`%n%`
24 |
25 | blogdown_skeleton = function(path, ...) {
26 | opts = options(blogdown.open_sample = FALSE); on.exit(options(opts), add = TRUE)
27 | new_site(dir = path, ..., serve = FALSE)
28 | }
29 |
30 | .onLoad = function(libname, pkgname) {
31 | # stop all servers when the package is unloaded or R session is ended
32 | reg.finalizer(asNamespace(pkgname), function(e) {
33 | opts$set(quitting = TRUE); on.exit(opts$set(quitting = NULL), add = TRUE)
34 | stop_server()
35 | }, onexit = TRUE)
36 |
37 | # initialize some important global options, so RStudio could autocomplete
38 | # options(); I can't set them to NULL directly because options(foo = NULL)
39 | # would *remove* the option 'foo', so RStudio won't be able to recognize
40 | # option names (I have to set them to I(NA) instead); I hate this ugly hack
41 | if (interactive()) for (i in names(.options)) {
42 | if (is.null(getOption(i))) options(.options[i])
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/man/config_netlify.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{config_netlify}
4 | \alias{config_netlify}
5 | \title{Create the configuration (file) for Netlify}
6 | \usage{
7 | config_netlify(output = "netlify.toml", new_config = list())
8 | }
9 | \arguments{
10 | \item{output}{Path to the output file, or \code{NULL}. If the file exists and
11 | the R session is interactive, you will be prompted to decide whether to
12 | overwrite the file.}
13 |
14 | \item{new_config}{If any default configuration does not apply to your site,
15 | you may provide a list of configurations to override the default. For
16 | example, if you want to use Hugo v0.25.1, you may use \code{new_config =
17 | list(build = list(environment = list(HUGO_VERSION = '0.25.1')))}.}
18 | }
19 | \value{
20 | If \code{output = NULL}, a character vector of TOML data representing
21 | the configurations (which you can preview and decide whether to write it to
22 | a file), otherwise the TOML data is written to a file.
23 | }
24 | \description{
25 | This function provides some default configurations for a Huge website to be
26 | built via Hugo and deployed on Netlify. It sets the build command for the
27 | production and preview contexts, respectively (for preview contexts such as
28 | \samp{deploy-preview}, the command will build future posts). It also sets the
29 | publish directory according to your setting in Hugo's config file (if it
30 | exists, otherwise it will be the default \file{public} directory). The Hugo
31 | version is set to the current version of Hugo found on your computer.
32 | }
33 | \examples{
34 | blogdown::config_netlify(output = NULL) # default data
35 |
36 | # change the publish dir to 'docs/'
37 | blogdown::config_netlify(NULL, list(build = list(publish = "docs")))
38 | }
39 | \references{
40 | See Netlify's documentation on the configuration file
41 | \file{netlify.toml} for the possible settings:
42 | \url{https://docs.netlify.com/configure-builds/file-based-configuration/}
43 | }
44 |
--------------------------------------------------------------------------------
/R/addin.R:
--------------------------------------------------------------------------------
1 | source_addin = function(file) in_root(sys.source(
2 | pkg_file('scripts', file), envir = new.env(parent = globalenv()),
3 | keep.source = FALSE
4 | ))
5 |
6 | new_post_addin = function() {
7 | if (generator() == 'hugo') find_hugo()
8 | source_addin('new_post.R')
9 | }
10 |
11 | update_meta_addin = function() source_addin('update_meta.R')
12 |
13 | insert_image_addin = function() {
14 | # when the addin is not used inside a site project
15 | if (xfun::try_error(site_root())) {
16 | old = opts$get()
17 | opts$set(site_root = I('.'))
18 | on.exit(opts$restore(old), add = TRUE)
19 | }
20 | source_addin('insert_image.R')
21 | }
22 |
23 | # use touch to update the timestamp of a file if available (not on Windows);
24 | # otherwise modify a file, undo it, and save it
25 | touch_file_rstudio = function() {
26 | ctx = rstudioapi::getSourceEditorContext()
27 | if (!file.exists(ctx$path)) stop('The current document has not been saved yet.')
28 | p = normalizePath(ctx$path); mtime = function() file.info(p)[, 'mtime']
29 | m = mtime()
30 | on.exit(if (!identical(m, m2 <- mtime())) message(
31 | 'The modification time of "', p, '" has been updated from ', m, ' to ', m2
32 | ), add = TRUE)
33 | touch_file(p)
34 | }
35 |
36 | touch_file = function(path, time = Sys.time()) Sys.setFileTime(path, time)
37 |
38 | # add > to the beginning of every line, and two trailing spaces to every line
39 | quote_poem = function(x) {
40 | x = paste(x, collapse = '\n')
41 | if (grepl('^\\s*$', x)) return(x)
42 | x = gsub(' *\n', ' \n', x)
43 | x = unlist(strsplit(x, '( *\n){2,}'))
44 | x = gsub('(^| \n)', '\\1> ', x)
45 | x = paste(x, collapse = '\n>\n')
46 | gsub(' \n> $', '\n', x)
47 | }
48 |
49 | quote_poem_addin = function() {
50 | ctx = rstudioapi::getSourceEditorContext()
51 | sel = ctx$selection[[1]]
52 | if (sel$text == '') {
53 | message('Please select some text in the editor first.')
54 | return()
55 | }
56 | rstudioapi::modifyRange(sel$range, quote_poem(sel$text))
57 | }
58 |
--------------------------------------------------------------------------------
/man/filter_newfile.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{filter_newfile}
4 | \alias{filter_newfile}
5 | \alias{filter_timestamp}
6 | \alias{filter_md5sum}
7 | \title{Look for files that have been possibly modified or out-of-date}
8 | \usage{
9 | filter_newfile(files)
10 |
11 | filter_timestamp(files)
12 |
13 | filter_md5sum(files, db = "blogdown/md5sum.txt")
14 | }
15 | \arguments{
16 | \item{files}{A vector of file paths.}
17 |
18 | \item{db}{Path to the database file.}
19 | }
20 | \value{
21 | The filtered file paths.
22 | }
23 | \description{
24 | Filter files by checking if their modification times or MD5 checksums have
25 | changed.
26 | }
27 | \details{
28 | The function \code{filter_newfile()} returns paths of source files that do
29 | not have corresponding output files, e.g., an \file{.Rmd} file that doesn't
30 | have the \file{.html} output file.
31 |
32 | The function \code{filter_timestamp()} compares the modification time of an
33 | Rmd file with that of its output file, and returns the path of a file if it
34 | is newer than its output file by \code{N} seconds (or if the output file does
35 | not exist), where \code{N} is obtained from the R global option
36 | \code{blogdown.time_diff}. By default, \code{N = 0}. You may change it via
37 | \code{options()}, e.g., \code{options(blogdown.time_diff = 5)} means an Rmd
38 | file will be returned when its modification time at least 5 seconds newer
39 | than its output file's modification time.
40 |
41 | The function \code{filter_md5sum()} reads the MD5 checksums of files from a
42 | database (a tab-separated text file), and returns the files of which the
43 | checksums have changed. If the database does not exist, write the checksums
44 | of files to it, otherwise update the checksums after the changed files have
45 | been identified. When a file is modified, its MD5 checksum is very likely to
46 | change.
47 |
48 | These functions can be used to determine which Rmd files to be rebuilt in a
49 | \pkg{blogdown} website. See \code{\link{build_site}()} for more information.
50 | }
51 |
--------------------------------------------------------------------------------
/man/html_page.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/format.R
3 | \name{html_page}
4 | \alias{html_page}
5 | \title{An R Markdown output format for \pkg{blogdown} web pages}
6 | \usage{
7 | html_page(
8 | ...,
9 | number_sections = FALSE,
10 | self_contained = FALSE,
11 | highlight = NULL,
12 | template = NULL,
13 | pandoc_args = c("-M", "link-citations=true", "--preserve-tabs"),
14 | keep_md = FALSE,
15 | pre_knit = NULL,
16 | post_processor = NULL
17 | )
18 | }
19 | \arguments{
20 | \item{..., number_sections, self_contained, highlight, template, pandoc_args}{Arguments passed to \code{bookdown::html_document2()} (note the option \code{theme}
21 | is not supported and set to \code{NULL} internally, and when \code{template = NULL},
22 | a default template in \pkg{blogdown} will be used).}
23 |
24 | \item{keep_md, pre_knit, post_processor}{Passed to \link[rmarkdown:output_format]{rmarkdown::output_format}.}
25 | }
26 | \description{
27 | This function is a simple wrapper of \code{\link[bookdown:html_document2]{bookdown::html_document2()}} with
28 | different default arguments, and more importantly, a special HTML template
29 | designed only for \pkg{blogdown} to render R Markdown to HTML pages that can
30 | be processed by Hugo.
31 | }
32 | \details{
33 | The HTML output is not a complete HTML document, and only meaningful to
34 | \pkg{blogdown} (it will be post-processed to render valid HTML pages). The
35 | only purpose of this output format is for users to change options in YAML.
36 |
37 | The fact that it is based on \pkg{bookdown} means most \pkg{bookdown}
38 | features are supported, such as numbering and cross-referencing
39 | figures/tables.
40 | }
41 | \note{
42 | Do not use a custom template unless you understand how the default
43 | template actually works (see the \pkg{blogdown} book).
44 |
45 | The argument \code{highlight} does not support the value \code{"textmate"}, and the
46 | argument \code{template} does not support the value \code{"default"}.
47 | }
48 | \references{
49 | See Chapter 2 of the \pkg{bookdown} book for the Markdown syntax:
50 | \url{https://bookdown.org/yihui/bookdown}. See the \pkg{blogdown} book for full
51 | details: \url{https://bookdown.org/yihui/blogdown}.
52 | }
53 |
--------------------------------------------------------------------------------
/.github/workflows/R-CMD-check.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches: main
4 | paths-ignore:
5 | - 'docs/**'
6 | - '.github/ISSUE_TEMPLATE'
7 | - '.github/workflows/bookdown.yaml'
8 | - '_pkgdown.yml'
9 | - 'pkgdown/**'
10 | - '.github/workflows/pkgdown.yaml'
11 | pull_request:
12 | branches: main
13 | paths-ignore:
14 | - 'docs/**'
15 | - '.github/ISSUE_TEMPLATE'
16 | - '.github/workflows/bookdown.yaml'
17 | - '_pkgdown.yml'
18 | - 'pkgdown/**'
19 | - '.github/workflows/pkgdown.yaml'
20 | schedule:
21 | - cron: '0 10 * * 1'
22 |
23 | name: R-CMD-check
24 |
25 | jobs:
26 | R-CMD-check:
27 | runs-on: ${{ matrix.config.os }}
28 |
29 | name: ${{ matrix.config.os }} (${{ matrix.config.r }})
30 |
31 | strategy:
32 | fail-fast: false
33 | matrix:
34 | config:
35 | - {os: windows-latest, r: 'release'}
36 | - {os: macos-latest, r: 'release'}
37 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
38 | - {os: ubuntu-latest, r: 'release'}
39 | - {os: ubuntu-latest, r: 'oldrel'}
40 |
41 | env:
42 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
43 | R_KEEP_PKG_SOURCE: yes
44 |
45 | steps:
46 | - uses: actions/checkout@HEAD
47 |
48 | - uses: r-lib/actions/setup-pandoc@HEAD
49 |
50 | - uses: r-lib/actions/setup-r@HEAD
51 | with:
52 | r-version: ${{ matrix.config.r }}
53 | http-user-agent: ${{ matrix.config.http-user-agent }}
54 | use-public-rspm: true
55 |
56 | - uses: r-lib/actions/setup-r-dependencies@HEAD
57 | with:
58 | extra-packages: any::rcmdcheck
59 | needs: check
60 |
61 | - name: Install Hugo using blogdown
62 | run: |
63 | pak::local_install()
64 | blogdown::install_hugo()
65 | blogdown::hugo_version()
66 | shell: Rscript {0}
67 |
68 | - uses: r-lib/actions/check-r-package@HEAD
69 |
70 | - name: Test coverage
71 | if: success() && runner.os == 'Linux' && matrix.config.r == 'release'
72 | run: |
73 | pak::pkg_install('covr')
74 | covr::codecov()
75 | shell: Rscript {0}
76 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches: [main, master]
4 | pull_request:
5 | branches: [main, master]
6 | release:
7 | types: [published]
8 | workflow_dispatch:
9 |
10 | name: pkgdown
11 |
12 | jobs:
13 | pkgdown:
14 | if: ${{ github.event_name == 'push' || startsWith(github.head_ref, 'pkgdown/') }}
15 | runs-on: ubuntu-latest
16 | # Only restrict concurrency for non-PR jobs
17 | concurrency:
18 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
19 | env:
20 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
21 | steps:
22 | - uses: actions/checkout@HEAD
23 |
24 | - uses: r-lib/actions/setup-pandoc@HEAD
25 |
26 | - uses: r-lib/actions/setup-r@HEAD
27 | with:
28 | use-public-rspm: true
29 |
30 | - uses: r-lib/actions/setup-r-dependencies@HEAD
31 | with:
32 | extra-packages: any::pkgdown, local::.
33 | needs: website
34 |
35 | - name: Install optipng
36 | # required to optimize images
37 | run: |
38 | sudo apt-get update -y
39 | sudo apt-get install -y optipng
40 |
41 | - name: Cache some pkgdown assets
42 | uses: actions/cache@HEAD
43 | with:
44 | path: |
45 | vignettes/articles/images/*.png
46 | key: 1-${{ hashFiles('vignettes/articles/examples.yml') }}
47 |
48 | - name: Build pkgdown site
49 | run: pkgdown::build_site(new_process = FALSE, install = FALSE)
50 | shell: Rscript {0}
51 |
52 | - name: Deploy to Netlify
53 | id: netlify-deploy
54 | uses: nwtgck/actions-netlify@v2
55 | with:
56 | publish-dir: 'reference'
57 | production-branch: main
58 | github-token: ${{ secrets.GITHUB_TOKEN }}
59 | deploy-message:
60 | 'Deploy from GHA: ${{ github.event.head_commit.message }} (${{ github.sha }})'
61 | enable-pull-request-comment: false
62 | enable-commit-comment: false
63 | enable-commit-status: true
64 | alias: deploy-preview-${{ github.event.number }}
65 | env:
66 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
67 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
68 |
--------------------------------------------------------------------------------
/man/find_hugo.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/install.R
3 | \name{find_hugo}
4 | \alias{find_hugo}
5 | \alias{remove_hugo}
6 | \title{Find or remove the Hugo executable}
7 | \usage{
8 | find_hugo(version = getOption("blogdown.hugo.version"), quiet = FALSE)
9 |
10 | remove_hugo(version = getOption("blogdown.hugo.version"), force = FALSE)
11 | }
12 | \arguments{
13 | \item{version}{The expected version number, e.g., \code{'0.25.1'}. If
14 | \code{NULL}, it will try to find/remove the maximum possible version. If
15 | \code{'all'}, find/remove all possible versions. In an interactive R
16 | session when \code{version} is not provided, \code{remove_hugo()} will list
17 | all installed versions of Hugo, and you can select which versions to
18 | remove.}
19 |
20 | \item{quiet}{Whether to signal a message when two versions of Hugo are found:
21 | one is found on the system \var{PATH} variable, and one is installed by
22 | \code{\link{install_hugo}()}.}
23 |
24 | \item{force}{By default, \code{remove_hugo()} only removes Hugo installed via
25 | \code{\link{install_hugo}()}. For \code{force = TRUE}, it will try to
26 | remove any Hugo executables found via \code{find_hugo()}.}
27 | }
28 | \value{
29 | For \code{find_hugo()}, it returns the path to the Hugo executable if
30 | found, otherwise it will signal an error, with a hint on how to install
31 | (the required version of) Hugo. If Hugo is found via the environment
32 | variable \var{PATH}, only the base name of the path is returned (you may
33 | use \code{\link{Sys.which}('hugo')} to obtain the full path).
34 |
35 | If \code{version = 'all'}, return the paths of all versions of Hugo
36 | installed.
37 | }
38 | \description{
39 | Search for Hugo in a series of possible installation directories (see
40 | \code{\link{install_hugo}()} for these directories) with \code{find_hugo()},
41 | or remove the Hugo executable(s) found with \code{remove_hugo()}.
42 | }
43 | \details{
44 | If your website depends on a specific version of Hugo, we strongly recommend
45 | that you set \code{options(blogdown.hugo.version = )} to the version number
46 | you desire in the file \code{.Rprofile} in the root directory of the website
47 | project, so that \pkg{blogdown} can try to find the right version of Hugo
48 | before it builds or serves the website. You can use the function
49 | \code{\link{config_Rprofile}()} to do this automatically.
50 | }
51 |
--------------------------------------------------------------------------------
/man/read_toml.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{read_toml}
4 | \alias{read_toml}
5 | \alias{write_toml}
6 | \alias{toml2yaml}
7 | \alias{yaml2toml}
8 | \title{Read and write TOML data (Tom's Obvious Markup Language)}
9 | \usage{
10 | read_toml(file, x = read_utf8(file), strict = TRUE)
11 |
12 | write_toml(x, output = NULL)
13 |
14 | toml2yaml(file, output = NULL)
15 |
16 | yaml2toml(file, output = NULL)
17 | }
18 | \arguments{
19 | \item{file}{Path to an input (TOML or YAML) file.}
20 |
21 | \item{x}{For \code{read_toml()}, the TOML data as a character vector (it is read
22 | from \code{file} by default; if provided, \code{file} will be ignored). For
23 | \code{write_toml()}, an R object to be converted to TOML.}
24 |
25 | \item{strict}{Whether to try \pkg{RcppTOML} and Hugo only (i.e., not to use
26 | the naive parser). If \code{FALSE}, only the naive parser is used (this is not
27 | recommended, unless you are sure your TOML data is really simple).}
28 |
29 | \item{output}{Path to an output file. If \code{NULL}, the TOML data is
30 | returned, otherwise the data is written to the specified file.}
31 | }
32 | \value{
33 | For \code{read_toml()}, an R object. For \code{write_toml()}, \code{toml2yaml()},
34 | and \code{yaml2toml()}, a character vector (marked by \code{\link[xfun:raw_string]{xfun::raw_string()}}) of
35 | the TOML/YAML data if \code{output = NULL}, otherwise the TOML/YAML data is
36 | written to the output file.
37 | }
38 | \description{
39 | The function \code{read_toml()} reads TOML data from a file or a character vector,
40 | and the function \code{write_toml()} converts an R object to TOML.
41 | }
42 | \details{
43 | For \code{read_toml()}, it first tries to use the R package \pkg{RcppTOML} to read
44 | the TOML data. If \pkg{RcppTOML} is not available, it uses Hugo to convert
45 | the TOML data to YAML, and reads the YAML data via the R package \pkg{yaml}.
46 | If Hugo is not available, it falls back to a naive parser, which is only able
47 | to parse top-level fields in the TOML data, and it only supports character,
48 | logical, and numeric (including integer) scalars.
49 |
50 | For \code{write_toml()}, it converts an R object to YAML via the R package
51 | \pkg{yaml}, and uses Hugo to convert the YAML data to TOML.
52 | }
53 | \examples{
54 | \dontrun{
55 | v = blogdown::read_toml(x = c("a = 1", "b = true", "c = \"Hello\"", "d = [1, 2]"))
56 | v
57 | blogdown::write_toml(v)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/man/serve_site.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/serve.R
3 | \name{serve_site}
4 | \alias{serve_site}
5 | \alias{stop_server}
6 | \title{Live preview a site}
7 | \usage{
8 | serve_site(..., .site_dir = NULL)
9 |
10 | stop_server()
11 | }
12 | \arguments{
13 | \item{...}{Arguments passed to \code{servr::\link[servr]{server_config}()}
14 | (only arguments \code{host}, \code{port}, \code{browser}, \code{daemon},
15 | and \code{interval} are supported).}
16 |
17 | \item{.site_dir}{Directory to search for site configuration file. It defaults
18 | to \code{getwd()}, and can also be specified via the global option
19 | \code{blogdown.site_root}.}
20 | }
21 | \description{
22 | The function \code{serve_site()} executes the server command of a static site
23 | generator (e.g., \command{hugo server} or \command{jekyll server}) to start a
24 | local web server, which watches for changes in the site, rebuilds the site if
25 | necessary, and refreshes the web page automatically; \code{stop_server()}
26 | stops the web server.
27 | }
28 | \details{
29 | By default, the server also watches for changes in R Markdown files, and
30 | recompile them automatically if they are modified. This means they will be
31 | automatically recompiled once you save them. If you do not like this
32 | behavior, you may set \code{options(blogdown.knit.on_save = FALSE)} (ideally
33 | in your \file{.Rprofile}). When this feature is disabled, you will have to
34 | manually compile Rmd documents, e.g., by clicking the Knit button in RStudio.
35 |
36 | The site generator is defined by the global R option
37 | \code{blogdown.generator}, with the default being \code{'hugo'}. You may use
38 | other site generators including \code{jekyll} and \code{hexo}, e.g.,
39 | \code{options(blogdown.generator = 'jekyll')}. You can define command-line
40 | arguments to be passed to the server of the site generator via the global R
41 | option \code{blogdown.X.server}, where \code{X} is \code{hugo},
42 | \code{jekyll}, or \code{hexo}. The default for Hugo is
43 | \code{options(blogdown.hugo.server = c('-D', '-F', '--navigateToChanged'))}
44 | (see the documentation of Hugo server at
45 | \url{https://gohugo.io/commands/hugo_server/} for the meaning of these
46 | arguments).
47 | }
48 | \note{
49 | For the Hugo server, the argument \command{--navigateToChanged} is used
50 | by default, which means when you edit and save a source file, Hugo will
51 | automatically navigate the web browser to the page corresponding to this
52 | source file (if the page exists).
53 | }
54 |
--------------------------------------------------------------------------------
/pkgdown/assets/ace-1.2.3/theme-textmate.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)})
--------------------------------------------------------------------------------
/.github/workflows/bookdown.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - master
5 | - main
6 | paths:
7 | - 'docs/**'
8 | - '.github/workflows/bookdown.yaml'
9 | pull_request:
10 | branches: [main, master]
11 | paths:
12 | - 'docs/**'
13 | - '.github/workflows/bookdown.yaml'
14 | workflow_dispatch:
15 | inputs:
16 | publish:
17 | description: 'publish the book to github pages for connect cloud deployment'
18 | required: false
19 | default: false
20 | type: boolean
21 |
22 | name: Build and deploy book
23 |
24 | jobs:
25 | build:
26 | runs-on: macOS-latest
27 | env:
28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
29 | R_KNITR_OPTIONS: "knitr.chunk.tidy=TRUE"
30 | steps:
31 | - name: Checkout repo
32 | uses: actions/checkout@HEAD
33 |
34 | - name: Setup R
35 | uses: r-lib/actions/setup-r@HEAD
36 |
37 | - name: Install Pandoc
38 | uses: r-lib/actions/setup-pandoc@HEAD
39 | with:
40 | pandoc-version: '2.11.4'
41 |
42 | - name: Install TinyTeX
43 | uses: r-lib/actions/setup-tinytex@HEAD
44 | env:
45 | # install full prebuilt version
46 | TINYTEX_INSTALLER: TinyTeX
47 |
48 | - name: Install OS dependencies
49 | run: |
50 | brew install --cask xquartz
51 | brew install --cask calibre
52 |
53 | - uses: r-lib/actions/setup-r-dependencies@HEAD
54 | with:
55 | extra-packages: local::.
56 | needs: book
57 |
58 | - name: Cache bookdown results
59 | uses: actions/cache@HEAD
60 | with:
61 | path: docs/_bookdown_files
62 | key: bookdown-1-${{ hashFiles('docs/*Rmd') }}
63 | restore-keys: bookdown-1-
64 |
65 | - name: Build all book
66 | run: make -C docs all
67 |
68 | - name: Deploy Gitbook to gh-pages
69 | if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
70 | uses: JamesIves/github-pages-deploy-action@v4
71 | with:
72 | branch: gh-pages
73 | folder: docs/_book
74 | clean: true
75 | single-commit: true
76 | dry-run: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.publish == 'false' || github.event.inputs.publish == false)) || false }}
77 |
78 | - name: Upload book folder for debug
79 | if: failure()
80 | uses: actions/upload-artifact@main
81 | with:
82 | name: book-dir
83 | path: docs
84 |
--------------------------------------------------------------------------------
/man/check_site.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/check.R
3 | \name{check_site}
4 | \alias{check_site}
5 | \alias{check_config}
6 | \alias{check_gitignore}
7 | \alias{check_hugo}
8 | \alias{check_netlify}
9 | \alias{check_vercel}
10 | \alias{check_content}
11 | \title{Provide diagnostics for a website project}
12 | \usage{
13 | check_site()
14 |
15 | check_config()
16 |
17 | check_gitignore()
18 |
19 | check_hugo()
20 |
21 | check_netlify()
22 |
23 | check_vercel()
24 |
25 | check_content()
26 | }
27 | \description{
28 | The function \code{check_site()} runs all \code{check_*()} functions on this
29 | page against a website project. See \sQuote{Details} for what each
30 | \code{check_*()} function does.
31 | }
32 | \details{
33 | \code{check_config()} checks the configuration file
34 | (\file{config.yaml} or \file{config.toml}) for settings such as
35 | \code{baseURL} and \code{ignoreFiles}.
36 |
37 | \code{check_gitignore()} checks if necessary files are incorrectly
38 | ignored in GIT.
39 |
40 | \code{check_hugo()} checks possible problems with the Hugo
41 | installation and version.
42 |
43 | \code{check_netlify()} checks the Hugo version specification and the
44 | publish directory in the Netlify config file \file{netlify.toml}.
45 | Specifically, it will check if the local Hugo version matches the version
46 | specified in \file{netlify.toml} (in the environment variable
47 | \var{HUGO_VERSION}), and if the \var{publish} setting in
48 | \file{netlify.toml} matches the \var{publishDir} setting in Hugo's config
49 | file (if it is set).
50 |
51 | \code{check_vercel()} checks if the Hugo version specified in
52 | \file{vercel.json} (if it exists) matches the Hugo version used in the
53 | current system.
54 |
55 | \code{check_content()} checks for possible problems in the content
56 | files. First, it checks for the validity of YAML metadata of all posts.
57 | Then it searches for posts with future dates and draft posts, and lists
58 | them if found (such posts appear in the local preview by default, but will
59 | be ignored by default when building the site). Then it checks for R
60 | Markdown posts that have not been rendered, or have output files older than
61 | the source files, and plain Markdown posts that have \file{.html} output
62 | files (which they should not have). At last, it detects \file{.html} files
63 | that seem to be generated by clicking the Knit button in RStudio with
64 | \pkg{blogdown} < v0.21. Such \file{.html} files should be deleted, since
65 | the Knit button only works with \pkg{blogdown} >= v0.21.
66 | }
67 |
--------------------------------------------------------------------------------
/vignettes/articles/examples.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Example sites"
3 | resource_files:
4 | - images/
5 | ---
6 |
7 | ```{r, include = FALSE, eval=FALSE}
8 | # To run to update the examples
9 |
10 | library(dplyr)
11 | library(purrr)
12 | library(rvest)
13 |
14 | # list of sites
15 | blogs <- tribble(
16 | ~ site, ~ source,
17 | "https://metadocencia.netlify.app/", "https://github.com/MetaDocencia/SitioWeb",
18 | "https://mariadermit.netlify.app/", "https://github.com/demar01/mariadermit",
19 | "https://shinydevseries.com/", "https://github.com/shinydevseries/shinydevseries_site",
20 | "https://r-podcast.org/", "https://github.com/rbind/r-podcast",
21 | "https://isabella-b.com/", "https://github.com/isabellabenabaye/isabella-b.com",
22 | # "https://robjhyndman.com/", "https://github.com/rbind/robjhyndman.com",
23 | "https://www.tidymodels.org/", "https://github.com/tidymodels/tidymodels.org",
24 | "https://livefreeordichotomize.com/", "https://github.com/LFOD/real-blog",
25 | "https://hugo-apero-docs.netlify.app/", "https://github.com/hugo-apero/hugo-apero-docs",
26 | "https://prose.yihui.org/", "https://github.com/yihui/hugo-prose/tree/master/exampleSite"
27 | )
28 |
29 | # get the title
30 | blogs <- blogs %>%
31 | mutate(
32 | title = map_chr(site, ~{
33 | read_html(.x) %>%
34 | html_node("title") %>%
35 | html_text()
36 | }),
37 | title = stringr::str_trim(title)
38 | )
39 |
40 | # takes screenshot
41 | blogs <- blogs %>%
42 | mutate(
43 | img = xfun::with_ext(
44 | paste("images", urltools::domain(blogs$site), sep = "/"), "png")
45 | )
46 |
47 | # export to YAML
48 | blogs %>%
49 | rename(href = "site") %>%
50 | mutate(showcase = TRUE) %>%
51 | purrr::pmap(purrr::lift_ld(as.list)) %>%
52 | yaml::write_yaml("examples.yml")
53 | ```
54 |
55 | ```{r, include = FALSE}
56 | # we use as few new dependencies as possible
57 | blogs <- rmarkdown:::yaml_load_file("examples.yml")
58 | blogs <- do.call(rbind, lapply(blogs, function(x) data.frame(site = x$href, img = x$img)))
59 | # remotes::install_github("rstudio/webshot2")
60 | img_exists <- file.exists(blogs$img)
61 | if (any(!img_exists)) {
62 | need_screenshot <- blogs[!img_exists, ]
63 | purrr::pwalk(need_screenshot, function(site, img) {
64 | message("Screenshoting ", site)
65 | res <- webshot2::webshot(site, img, cliprect = "viewport")
66 | # optimize image - require optipng
67 | webshot2::shrink(res)
68 | })
69 | }
70 | ```
71 |
72 | The examples below illustrate the use of **blogdown** for making websites and blogs. You can also find a list of examples at
73 |
74 | ```{r, echo=FALSE}
75 | quillt::examples(yml = "examples.yml")
76 | ```
77 |
--------------------------------------------------------------------------------
/R/site.R:
--------------------------------------------------------------------------------
1 | blogdown_site = function(input, ...) {
2 | # set the site root dir
3 | opts$set(site_root = input)
4 |
5 | # start serving the site when a blogdown project is opened in RStudio
6 | if (interactive() && get_option('blogdown.serve_site.startup', FALSE)) try({
7 | if (!isTRUE(opts$get('startup'))) {
8 | rstudioapi::sendToConsole('blogdown:::preview_site(startup = TRUE)')
9 | opts$set(startup = TRUE) # don't send the above code again in this session
10 | }
11 | })
12 |
13 | output_dir = publish_dir()
14 | render = function(input_file, output_format, envir, quiet, ...) {
15 | # input_file is NULL when render the whole site, and is a file path when
16 | # rendering a single file (by clicking the Knit button)
17 | if (!is.null(input_file)) xfun::in_dir(input, {
18 | # set a global option
19 | opts$set(render_one = TRUE); on.exit(opts$set(render_one = NULL), add = TRUE)
20 | input_file = rel_path(input_file)
21 | # when knitting a file not in the project root, RStudio starts R from the
22 | # dir of the file instead of the root, hence .Rprofile is ignored (#562)
23 | if (dirname(input_file) != '.') source_profile(input, globalenv())
24 | # only build R Markdown files (no need to build plain .md files)
25 | if (grepl(rmd_pattern, input_file))
26 | build_site(TRUE, run_hugo = FALSE, build_rmd = input_file)
27 | # run serve_site() to preview the site if the server has not been started
28 | if (get_option('blogdown.knit.serve_site', Sys.getenv('BLOGDOWN_SERVING_DIR') == '')) {
29 | if (interactive()) preview_site() else tryCatch(
30 | rstudioapi::sendToConsole('blogdown:::preview_site()', echo = FALSE),
31 | error = function(e) {}
32 | )
33 | }
34 | }) else {
35 | build_site(relativeURLs = if (parent_call('rsconnect::deploySite')) TRUE)
36 | if (!quiet) message(
37 | "\n==> The site has been generated to the directory '", output_dir, "'.\n\n",
38 | "** Note that normally you cannot just open the .html files in this directory ",
39 | "to view them in a browser. This directory need to be served before you can ",
40 | "preview web pages correctly (e.g., you may deploy the folder to a web server). ",
41 | "Alternatively, blogdown::serve_site() gives you a local preview of the site.\n"
42 | )
43 | }
44 | }
45 |
46 | # return site generator
47 | list(
48 | name = basename(getwd()),
49 | output_dir = output_dir,
50 | render = render,
51 | subdirs = TRUE,
52 | clean = function() {
53 | x = c('blogdown', output_dir, clean_targets())
54 | x[file.exists(x)]
55 | }
56 | )
57 | }
58 |
59 | clean_targets = function() {
60 | rmds = list_rmds()
61 | files = by_products(rmds, c('.html', '.markdown'))
62 | c(files, 'static/rmarkdown-libs', list_files(
63 | 'static', '.+_files$', include.dirs = TRUE
64 | ))
65 | }
66 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Type: Package
2 | Package: blogdown
3 | Title: Create Blogs and Websites with R Markdown
4 | Version: 1.22.1
5 | Authors@R: c(
6 | person("Yihui", "Xie", role = c("aut", "cre"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")),
7 | person("Christophe", "Dervieux", role = "aut", email = "cderv@posit.co", comment = c(ORCID = "0000-0003-4474-2498")),
8 | person("Alison", "Presmanes Hill", role = "aut", comment = c(ORCID = "0000-0002-8082-1890")),
9 | # Contributors, ordered alphabetically
10 | person("Amber", "Thomas", role = "ctb"),
11 | person("Beilei", "Bian", role = "ctb"),
12 | person("Brandon ", "Greenwell", role = "ctb"),
13 | person("Brian", "Barkley", role = "ctb"),
14 | person("Deependra", "Dhakal", role = "ctb"),
15 | person("Eric", "Nantz", role = "ctb"),
16 | person("Forest", "Fang", role = "ctb"),
17 | person("Garrick", "Aden-Buie", role = "ctb"),
18 | person("Hiroaki", "Yutani", role = "ctb"),
19 | person("Ian", "Lyttle", role = "ctb"),
20 | person("Jake", "Barlow", role = "ctb"),
21 | person("James", "Balamuta", role = "ctb"),
22 | person("JJ", "Allaire", role = "ctb"),
23 | person("Jon", "Calder", role = "ctb"),
24 | person("Jozef", "Hajnala", role = "ctb"),
25 | person("Juan Manuel", "Vazquez", role = "ctb"),
26 | person("Kevin", "Ushey", role = "ctb"),
27 | person("Leonardo", "Collado-Torres", role = "ctb"),
28 | person("Maëlle", "Salmon", role = "ctb"),
29 | person("Maria Paula", "Caldas", role = "ctb"),
30 | person("Nicolas", "Roelandt", role = "ctb"),
31 | person("Oliver", "Madsen", role = "ctb", email = "oliver.p.madsen@gmail.com"),
32 | person("Raniere", "Silva", role = "ctb"),
33 | person("TC", "Zhang", role = "ctb"),
34 | person("Xianying", "Tan", role = "ctb"),
35 | person(given = "Posit Software, PBC", role = c("cph", "fnd")),
36 | person()
37 | )
38 | Description: Write blog posts and web pages in R Markdown. This package
39 | supports the static site generator 'Hugo' () best,
40 | and it also supports 'Jekyll' () and 'Hexo'
41 | ().
42 | License: GPL-3
43 | URL: https://github.com/rstudio/blogdown,
44 | https://pkgs.rstudio.com/blogdown/
45 | BugReports: https://github.com/rstudio/blogdown/issues
46 | Depends: R (>= 3.5.0)
47 | Imports:
48 | bookdown (>= 0.22),
49 | htmltools,
50 | httpuv (>= 1.4.0),
51 | jsonlite,
52 | knitr (>= 1.25),
53 | later,
54 | rmarkdown (>= 2.8),
55 | servr (>= 0.21),
56 | xfun (>= 0.34),
57 | yaml (>= 2.1.19)
58 | Suggests:
59 | miniUI,
60 | processx,
61 | rstudioapi,
62 | shiny,
63 | stringr,
64 | testit,
65 | tools,
66 | whoami
67 | Config/Needs/website: pkgdown, tidyverse/tidytemplate, rstudio/quillt, rstudio/webshot2
68 | Encoding: UTF-8
69 | RoxygenNote: 7.3.3
70 | SystemRequirements: Hugo () and Pandoc ()
71 |
--------------------------------------------------------------------------------
/inst/scripts/update_meta.R:
--------------------------------------------------------------------------------
1 | tags = htmltools::tags
2 | txt_input = function(..., width = '100%') shiny::textInput(..., width = width)
3 | sel_input = function(...) shiny::selectizeInput(
4 | ..., width = '100%', multiple = TRUE, options = list(create = TRUE)
5 | )
6 | meta = blogdown:::collect_yaml()
7 |
8 | ctxt = rstudioapi::getSourceEditorContext(); txt = ctxt$contents
9 | if (length(ctxt$selection) == 0) stop(
10 | 'The document seems to be in the visual editor. Please put the cursor in YAML ',
11 | 'or switch to the source editor.', call. = FALSE
12 | )
13 | res = blogdown:::split_yaml_body(txt); yml = res$yaml_list; rng = res$yaml_range
14 | if (length(rng) != 2) stop('Cannot find YAML metadata in the current document.', call. = FALSE)
15 | rstudioapi::setSelectionRanges(list(c(rng[1] + 1, 1, rng[2], 1)))
16 | slct = rstudioapi::getSourceEditorContext()$selection[[1]]
17 |
18 | if (length(yml) == 0) yml = list()
19 | yml = blogdown:::filter_list(yml)
20 | if (is.null(yml[['title']])) yml$title = ''
21 | if (is.null(yml[['author']])) yml$author = blogdown:::get_author()
22 | if (is.null(yml[['date']])) yml$date = Sys.Date()
23 |
24 | shiny::runGadget(
25 | miniUI::miniPage(miniUI::miniContentPanel(
26 | txt_input('title', 'Title', yml[['title']], placeholder = 'Post Title'),
27 | shiny::fillRow(
28 | txt_input('author', 'Author', yml[['author']], width = '99%'),
29 | shiny::dateInput('date', 'Date', yml[['date']], width = '99%'),
30 | height = '80px'
31 | ),
32 | shiny::checkboxInput(
33 | 'rename', 'Rename file if the date is changed',
34 | blogdown:::get_option('blogdown.rename_file', FALSE)
35 | ),
36 | sel_input(
37 | 'cat', 'Categories', blogdown:::sort2(unique(c(yml[['categories']], meta$categories))),
38 | selected = yml[['categories']]
39 | ),
40 | sel_input(
41 | 'tag', 'Tags', blogdown:::sort2(unique(c(yml[['tags']], meta$tags))),
42 | selected = yml[['tags']]
43 | ),
44 | shiny::fillRow(tags$div(), height = '20px'),
45 | miniUI::gadgetTitleBar(NULL)
46 | )),
47 | server = function(input, output) {
48 | shiny::observeEvent(input$done, {
49 | seq_keys = Filter(function(key) {
50 | identical(attr(yml[[key]], 'yml_type'), 'seq')
51 | }, names(yml))
52 | seq_keys = unique(c(seq_keys, 'categories', 'tags'))
53 |
54 | res = list(
55 | title = input$title, author = input$author, date = format(input$date),
56 | categories = input$cat, tags = input$tag
57 | )
58 | yml = c(res, yml[setdiff(names(yml), names(res))])
59 | for (i in seq_keys) yml[[i]] = if (length(yml[[i]]) > 0) as.list(yml[[i]])
60 | if (!blogdown:::get_option('blogdown.yaml.empty', TRUE)) yml = blogdown:::filter_list(yml)
61 | rstudioapi::modifyRange(
62 | slct$range, blogdown:::as.yaml(yml, .trim_ws = FALSE)
63 | )
64 | if (input$rename) {
65 | rstudioapi::documentSave()
66 | p = ctxt$path; p2 = blogdown:::date_filename(p, res$date, replace = TRUE)
67 | b = if (basename(p) == basename(p2)) {
68 | file.rename(dirname(p), dirname(p2))
69 | } else file.rename(p, p2)
70 | if (b) rstudioapi::navigateToFile(p2)
71 | }
72 | shiny::stopApp()
73 | })
74 | shiny::observeEvent(input$cancel, {
75 | shiny::stopApp()
76 | })
77 | },
78 | stopOnCancel = FALSE,
79 | viewer = shiny::dialogViewer('Update YAML metadata', 500, 450)
80 | )
81 |
--------------------------------------------------------------------------------
/man/shortcode.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/hugo.R
3 | \name{shortcode}
4 | \alias{shortcode}
5 | \alias{shortcode_html}
6 | \alias{shortcodes}
7 | \alias{shortcode_open}
8 | \alias{shortcode_close}
9 | \title{Helper functions to write Hugo shortcodes using the R syntax}
10 | \usage{
11 | shortcode(.name, ..., .content = NULL, .type = "markdown")
12 |
13 | shortcode_html(...)
14 |
15 | shortcodes(..., .sep = "\\n")
16 |
17 | shortcode_open(...)
18 |
19 | shortcode_close(...)
20 | }
21 | \arguments{
22 | \item{.name}{The name of the shortcode.}
23 |
24 | \item{...}{All arguments of the shortcode (either all named, or all unnamed).
25 | The \code{...} arguments of all other functions are passed to
26 | \code{shortcode()}.}
27 |
28 | \item{.content}{The inner content for the shortcode.}
29 |
30 | \item{.type}{The type of the shortcode: \code{markdown} or \code{html}.}
31 |
32 | \item{.sep}{The separator between two shortcodes (by default, a newline).}
33 | }
34 | \value{
35 | A character string wrapped in \code{htmltools::HTML()};
36 | \code{shortcode()} returns a string of the form \code{{{\% name args \%}}},
37 | and \code{shortcode_html()} returns \code{{{< name args >}}}.
38 | }
39 | \description{
40 | These functions return Hugo shortcodes with the shortcode name and arguments
41 | you specify. The closing shortcode will be added only if the inner content is
42 | not empty. The function \code{shortcode_html()} is essentially
43 | \code{shortcode(.type = 'html')}. The function \code{shortcodes()} is a
44 | vectorized version of \code{shortcode()}. The paired functions
45 | \code{shortcode_open()} and \code{shortcode_close()} provide an alternative
46 | method to open and close shortcodes, which allows inner content be processed
47 | safely by Pandoc (e.g., citation keys in the content).
48 | }
49 | \details{
50 | These functions can be used in either \pkg{knitr} inline R expressions or
51 | code chunks. The returned character string is wrapped in
52 | \code{htmltools::\link[htmltools]{HTML}()}, so \pkg{rmarkdown} will protect
53 | it from the Pandoc conversion. You cannot simply write \code{{{< shortcode
54 | >}}} in R Markdown, because Pandoc is not aware of Hugo shortcodes, and may
55 | convert special characters so that Hugo can no longer recognize the
56 | shortcodes (e.g. \code{<} will be converted to \code{<}).
57 |
58 | If your document is pure Markdown, you can use the Hugo syntax to write
59 | shortcodes, and there is no need to call these R functions.
60 | }
61 | \note{
62 | Since Hugo v0.60, Hugo has switched its default Markdown rendering
63 | engine to Goldmark. One consequence is that shortcodes may fail to render.
64 | You may enable the \code{unsafe} option in the configuration file:
65 | \url{https://gohugo.io/getting-started/configuration-markup/#goldmark}.
66 | }
67 | \examples{
68 | library(blogdown)
69 |
70 | shortcode("tweet", user = "SanDiegoZoo", id = "1453110110599868418")
71 | # multiple tweets (id's are fake)
72 | shortcodes("tweet", user = "SanDiegoZoo", id = as.character(1:5))
73 | shortcode("figure", src = "/images/foo.png", alt = "A nice figure")
74 | shortcode("highlight", "bash", .content = "echo hello world;")
75 |
76 | shortcode_html("myshortcode", .content = "My shortcode.")
77 |
78 | shortcode_open("figure", src = "/images/foo.png")
79 | # This inner text will be *processed* by Pandoc, @Smith2006
80 | shortcode_close("figure")
81 | }
82 | \references{
83 | \url{https://gohugo.io/extras/shortcodes/}
84 | }
85 |
--------------------------------------------------------------------------------
/R/format.R:
--------------------------------------------------------------------------------
1 | #' An R Markdown output format for \pkg{blogdown} web pages
2 | #'
3 | #' This function is a simple wrapper of [bookdown::html_document2()] with
4 | #' different default arguments, and more importantly, a special HTML template
5 | #' designed only for \pkg{blogdown} to render R Markdown to HTML pages that can
6 | #' be processed by Hugo.
7 | #'
8 | #' The HTML output is not a complete HTML document, and only meaningful to
9 | #' \pkg{blogdown} (it will be post-processed to render valid HTML pages). The
10 | #' only purpose of this output format is for users to change options in YAML.
11 | #'
12 | #' The fact that it is based on \pkg{bookdown} means most \pkg{bookdown}
13 | #' features are supported, such as numbering and cross-referencing
14 | #' figures/tables.
15 | #'
16 | #' @param ...,number_sections,self_contained,highlight,template,pandoc_args
17 | #' Arguments passed to `bookdown::html_document2()` (note the option `theme`
18 | #' is not supported and set to `NULL` internally, and when `template = NULL`,
19 | #' a default template in \pkg{blogdown} will be used).
20 | #' @param keep_md,pre_knit,post_processor Passed to [rmarkdown::output_format].
21 | #'
22 | #' @note Do not use a custom template unless you understand how the default
23 | #' template actually works (see the \pkg{blogdown} book).
24 | #'
25 | #' The argument `highlight` does not support the value `"textmate"`, and the
26 | #' argument `template` does not support the value `"default"`.
27 | #' @references See Chapter 2 of the \pkg{bookdown} book for the Markdown syntax:
28 | #' . See the \pkg{blogdown} book for full
29 | #' details: .
30 | #' @export
31 | #' @md
32 | html_page = function(
33 | ..., number_sections = FALSE, self_contained = FALSE, highlight = NULL,
34 | template = NULL, pandoc_args = c('-M', 'link-citations=true', '--preserve-tabs'),
35 | keep_md = FALSE, pre_knit = NULL, post_processor = NULL
36 | ) {
37 | if (identical(template, 'default')) stop(
38 | 'blogdown::html_page() does not support template = "default"'
39 | )
40 | if (identical(highlight, 'textmate')) stop(
41 | 'blogdown::html_page() does not support highlight = "textmate"'
42 | )
43 | if (is.character(pre_knit))
44 | pre_knit = eval(parse(text = pre_knit))
45 | if (is.character(post <- post_processor))
46 | post = eval(parse(text = post_processor))
47 | post_processor = function(metadata, input, output, ...) {
48 | if (is.function(post)) output = post(metadata, input, output, ...)
49 | # the output .html file contains no YAML metadata; need to prepend from .md
50 | if (grepl('[.]html~', output) && file_exists(f <- with_ext(output, '.knit.md~'))) {
51 | prepend_yaml(f, output, callback = function(s) {
52 | if (!getOption('blogdown.draft.output', FALSE)) return(s)
53 | if (length(s) < 2 || length(grep('^draft: ', s)) > 0) return(s)
54 | append(s, 'draft: true', 1)
55 | })
56 | }
57 | output
58 | }
59 | rmarkdown::output_format(
60 | knitr = NULL,
61 | pandoc = NULL,
62 | clean_supporting = self_contained,
63 | keep_md = keep_md,
64 | pre_knit = pre_knit,
65 | post_processor = post_processor,
66 | base_format = bookdown::html_document2(
67 | ..., number_sections = number_sections, theme = NULL,
68 | self_contained = self_contained, highlight = highlight,
69 | pandoc_args = pandoc_args,
70 | template = template %n% pkg_file('resources', 'template-minimal.html')
71 | )
72 | )
73 | }
74 |
--------------------------------------------------------------------------------
/_pkgdown.yml:
--------------------------------------------------------------------------------
1 | destination: reference
2 |
3 | # website will be referenced on https://pkgs.rstudio.com/
4 | # Open a PR in https://github.com/rstudio/pkgs.rstudio.com
5 | url: https://pkgs.rstudio.com/blogdown/
6 |
7 | template:
8 | package: tidytemplate
9 | bootstrap: 5
10 | bslib:
11 | primary: "#096B72"
12 | navbar-background: "#e6f3fc"
13 | trailing_slash_redirect: true
14 | opengraph:
15 | image:
16 | src: https://bookdown.org/yihui/blogdown/images/logo.png
17 | alt: "blogdown package"
18 | twitter:
19 | creator: "@rstudio"
20 | card: summary
21 |
22 | home:
23 | links:
24 | - text: Learn more about R Markdown
25 | href: "https://rmarkdown.rstudio.com"
26 |
27 | # custom footer for rmarkdown ecosystem
28 | footer:
29 | structure:
30 | left: [rmd]
31 | right: [developed_by, p, built_with]
32 | components:
33 | p: "\n\n"
34 | rmd: |
35 | **blogdown** is a part of the **R Markdown** ecosystem of packages for creating
36 | computational documents in R. Learn more at
37 | [rmarkdown.rstudio.com](https://rmarkdown.rstudio.com/).
38 |
39 | # structure of website themed for R Markdown ecosystem
40 | navbar:
41 | structure:
42 | left: [intro, examples, articles, reference, news]
43 | components:
44 | examples:
45 | text: Examples
46 | href: articles/examples.html
47 |
48 | # Add articles menu using
49 | # https://pkgdown.r-lib.org/dev/reference/build_articles.html#index-and-navbar
50 | articles:
51 | - title: Get Started
52 | desc: |
53 | Start here to know how to learn **blogdown**
54 | contents:
55 | - blogdown
56 | - title: Example
57 | desc: Gallery of examples
58 | contents:
59 | - articles/examples
60 |
61 | news:
62 | releases:
63 | - text: "Version 1.6"
64 | href: https://yihui.org/en/2021/11/blogdown-v1-6/
65 | - text: "Version 1.0"
66 | href: https://posit.co/blog/blogdown-v1-0/
67 | - text: "Version 0.1"
68 | href: https://posit.co/blog/announcing-blogdown/
69 |
70 | reference:
71 | - title: Output formats
72 | desc: >
73 | If you use files with the `.Rmd` extension, the default output format is
74 | `blogdown::html_page`, which uses Pandoc to render. You may also use files with the `.Rmarkdown` file extension, which
75 | may be knit to `.markdown` files to be processed by Hugo's markdown renderer.
76 | contents:
77 | - html_page
78 |
79 | - title: Creating new websites & content
80 | contents:
81 | - new_site
82 | - install_theme
83 | - new_post
84 | - new_content
85 | - -starts_with("hugo")
86 |
87 | - title: Previewing and rendering websites
88 | contents:
89 | - serve_site
90 | - build_site
91 |
92 | - title: Managing website configurations
93 | contents:
94 | - starts_with("config")
95 |
96 | - title: Checking blogdown projects
97 | contents:
98 | - starts_with("check")
99 |
100 | - title: Working with Hugo
101 | desc: >
102 | These functions are helpers to work with Hugo and its features.
103 | contents:
104 | - ends_with("hugo")
105 | - starts_with("hugo")
106 | - starts_with("shortcode")
107 | - bundle_site
108 | - find_yaml
109 | - read_toml
110 | - -starts_with("check_")
111 |
112 | - title: Helper functions
113 | desc: These functions are utility functions when working with blogdown.
114 | contents:
115 | - build_dir
116 | - clean_duplicates
117 | - dep_path
118 | - starts_with("filter")
119 |
120 | - title: The blogdown package
121 | desc: ~
122 | contents:
123 | - blogdown-package
124 |
--------------------------------------------------------------------------------
/vignettes/blogdown.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Learn blogdown"
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{Learn blogdown}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 | ## User Guide
11 |
12 |
13 |
14 | Written by Yihui Xie, the package author, [blogdown: Creating Websites with R Markdown](https://bookdown.org/yihui/blogdown/) introduces the R package and how to use it. The book is published by Chapman & Hall/CRC, and you can read it online for free.
15 |
16 | The book is structured into 5 parts to guide the reader into the use of the R package **blogdown** to create and manage websites:
17 |
18 | ```{r, results='asis', echo = FALSE, eval = FALSE}
19 | # run this to update the content below
20 | xfun::pkg_attach2("xml2")
21 | html <- read_html("https://bookdown.org/yihui/blogdown/")
22 | chapters <- xml_find_all(html, "//li[@class='chapter']")
23 | first_level <- chapters[which(purrr::map_lgl(xml_attr(chapters, 'data-level'), ~ grepl('^\\d+$', .x)))]
24 | titles <- xml_text(xml_find_all(first_level, "a"))
25 | titles <- gsub("^(\\d+)", "\\1.", titles)
26 | titles <- gsub("^(.*) \\([*])$", "\\1", titles)
27 | url <- file.path("https://bookdown.org/yihui/blogdown", xml_attr(first_level, "data-path"))
28 | formatted <- sprintf("* [%s](%s)", titles, url)
29 | cat(formatted, sep = "\n")
30 | ```
31 |
32 |
33 | * [1. Get Started](https://bookdown.org/yihui/blogdown/get-started.html) introduces the package and how to get started using it. This is the chapter every user is expected to read.
34 | * [2. Hugo](https://bookdown.org/yihui/blogdown/hugo.html) introduces [Hugo](https://gohugo.io), the static site generator on which blogdown is based. It will act as a guide to those who are just getting started with Hugo.
35 | * [3. Deployment](https://bookdown.org/yihui/blogdown/deployment.html) explains different methods to deploy your static website online.
36 | * [4. Migration](https://bookdown.org/yihui/blogdown/migration.html) explains how to migrate from other framework to a Hugo website.
37 | * [5. Other Generators](https://bookdown.org/yihui/blogdown/other-generators.html) explains how to use **blogdown** with a custom build method.
38 |
39 |
40 | **A note from the authors**: Some of the information and instructions in this book are now out of date because of changes to Hugo and the recent **blogdown** package. Most of the book is still valid but some chapters may have less accurate informations
41 |
42 | ## About R Markdown
43 |
44 |
45 |
46 | If you are new to **R Markdown**, we recommend you start with [R Markdown: The Definitive Guide](https://bookdown.org/yihui/rmarkdown/) to get an overview. [Part I](https://bookdown.org/yihui/rmarkdown/installation.html) introduces how to install the relevant packages, and provides an overview of R Markdown, including the possible output formats, the Markdown syntax, the R code chunk syntax, and how to use other languages in R Markdown.
47 |
48 | Next, the chapter on [_Websites_](https://bookdown.org/yihui/rmarkdown/websites.html) will help orient you to how the **blogdown** package allows you to use R Markdown to create websites.
49 |
50 | ## Going further with examples
51 |
52 | Look at the [Examples](articles/examples.html) page.
53 |
--------------------------------------------------------------------------------
/docs/10-experience.Rmd:
--------------------------------------------------------------------------------
1 | # Personal Experience
2 |
3 | I started blogging at blogchina.com in 2005, moved to blog.com.cn, then MSN Space, and finally purchased my own domain `yihui.org` and a virtual host. I first used a PHP application named Bo-Blog, then switched to WordPress, and then Jekyll. Finally I moved to Hugo. Although I have moved several times, all my posts have been preserved, and you can still see my first post in Chinese in 2005. I often try my best not to introduce broken links (which lead to the 404 page) every time I change the backend of my website. When it is too hard to preserve the original links of certain pages, I will redirect the broken URLs to the new URLs. That is why it is important for your system to support redirections, and in particular, 301 redirections (Netlify does a nice job here). Here are some of my redirection rules: . For example, `http://yihui.org/en/feed/` was the RSS feed of my old WordPress and Jekyll blogs in English, and Hugo generates the RSS feed to `/en/index.xml` instead, so I need to redirect `/en/feed/` to `/en/index.xml`.
4 |
5 | Google has provided several tools to help you know more information about your website. For example, [Google Search Console](https://search.google.com/search-console) can show you which pages give users bad page experience on mobile and desktop. I use these tools frequently by myself.
6 |
7 | I firmly believe in the value of writing. Over the years, I have written more than 1000 posts in Chinese and English. Some are long, and most are short. The total size of these text files is about 5 Mb. In retrospect, most posts are probably not valuable to general readers (some are random thoughts, and some are my rants), but I feel I benefitted a lot from writing in two aspects:
8 |
9 | 1. If I sit down and focus on writing a small topic for a while, I often feel my thoughts will become clearer. A major difference between writing and talking is that you can always reorganize things and revise them when writing. I do not think writing on social media counts. 140 characters may well be thoughtful, but I feel there is so much chaos there. It is hard to lay out systematic thoughts only through short messages, and these quick messages are often quickly forgotten.
10 |
11 | 1. I know some bloggers are very much against comments, so they do not open comments to the public. I have not had a very negative experience with comments yet. On the contrary, I constantly find inspirations from comments. For example, [I was thinking](https://yihui.org/en/2013/04/travis-ci-for-r/) if it was possible to automatically check R packages on the cloud through Travis CI. At that time (April 2013), I believe not many people in the R community had started using Travis CI, although I'm not sure if I was the first person experimenting with this idea. I felt Travis CI could be promising, but it did not support R back then. Someone named Vincent Arel-Bundock (I still do not know him) told me a hack in a comment, which suddenly lit up my mind and I quickly figured out a solution. In October 2013, Craig Citro started more solid work on the R support on Travis CI. I do not know if he saw my blog post. Anyway, I think Travis CI has made substantial impact on R package developers, which is a great thing for the R community.
12 |
13 | Yet another relatively small benefit is that I often go to my own posts to learn some technical stuff that I have forgotten. For example, I find it difficult to remember the syntax of different types of zero-width assertions in Perl-like regular expressions: `(?=...)`, `(?!...)`, `(?<=...)`, and `(? New File -> R Markdown`.
34 |
35 | You are strongly recommended to go through the documentation of **knitr** chunk options and Pandoc's manual at least once to have an idea of all possibilities. The basics of Markdown are simple enough, but there are many less well-known features in Pandoc's Markdown, too. As we mentioned in Section \@ref(output-format), **blogdown**'s output format is based on **bookdown** [@R-bookdown], which contains several other Markdown extensions, such as numbered equations and theorem environments, and you need to read Chapter 2 of the **bookdown** book [@xie2016] to learn more about these features.
36 |
37 | You can find an R Markdown cheat sheet and a reference guide at https://posit.co/resources/cheatsheets/, which can be handy after you are more familiar with R Markdown.
38 |
39 | With R Markdown, you only need to maintain the source documents; all output pages can be automatically generated from source documents. This makes it much easier to maintain a website, especially when the website is related to data analysis or statistical computing and graphics. When the source code is updated (e.g., the model or data is changed), your web pages can be updated accordingly and automatically. There is no need to run the code separately and cut-and-paste again. Besides the convenience, you gain reproducibility at the same time.
40 |
--------------------------------------------------------------------------------
/inst/scripts/new_post.R:
--------------------------------------------------------------------------------
1 | tags = htmltools::tags
2 | txt_input = function(..., width = '100%') shiny::textInput(..., width = width)
3 | sel_input = function(...) shiny::selectizeInput(
4 | ..., width = '98%', multiple = TRUE, options = list(create = TRUE)
5 | )
6 | meta = blogdown:::collect_yaml()
7 | lang = blogdown:::get_lang()
8 | adir = blogdown:::archetypes()
9 |
10 | shiny::runGadget(
11 | miniUI::miniPage(miniUI::miniContentPanel(
12 | txt_input('title', 'Title', placeholder = 'Post Title'),
13 | shiny::fillRow(
14 | txt_input('author', 'Author', blogdown:::get_author(), width = '98%'),
15 | shiny::dateInput('date', 'Date', Sys.Date(), width = '98%'),
16 | shiny::selectizeInput(
17 | 'subdir', 'Subdirectory', blogdown:::get_subdirs(),
18 | selected = getOption('blogdown.subdir', 'post'),
19 | width = '98%', multiple = FALSE,
20 | options = list(create = TRUE, placeholder = '(optional)')
21 | ),
22 | height = '70px'
23 | ),
24 | shiny::fillRow(
25 | sel_input('cat', 'Categories', meta$categories),
26 | sel_input('tag', 'Tags', meta$tags),
27 | shiny::selectInput(
28 | 'kind', 'Archetype', width = '98%',
29 | choices = unique(c('', adir))
30 | ),
31 | height = '70px'
32 | ),
33 | shiny::fillRow(
34 | txt_input('file', 'Filename', '', 'automatically generated (edit if you want)'),
35 | height = '70px'
36 | ),
37 | if (is.null(lang)) {
38 | shiny::fillRow(txt_input('slug', 'Slug', '', '(optional)'), height = '70px')
39 | } else {
40 | shiny::fillRow(
41 | txt_input('slug', 'Slug', '', '(optional)', width = '98%'),
42 | txt_input('lang', 'Language', lang, width = '98%'),
43 | height = '70px'
44 | )
45 | },
46 | shiny::fillRow(
47 | shiny::radioButtons(
48 | 'format', 'Format', inline = TRUE,
49 | c('Markdown' = '.md', 'R Markdown (.Rmd)' = '.Rmd', 'R Markdown (.Rmarkdown)' = '.Rmarkdown'),
50 | selected = getOption('blogdown.ext', '.md')
51 | ),
52 | height = '70px'
53 | ),
54 | miniUI::gadgetTitleBar(NULL)
55 | )),
56 | server = function(input, output, session) {
57 | empty_title = shiny::reactive(grepl('^\\s*$', input$title))
58 | shiny::observe({
59 | shiny::updateTextInput(
60 | session, 'slug',
61 | placeholder = if (empty_title()) '(optional)' else blogdown:::dash_filename(input$title)
62 | )
63 | })
64 | # update subdir in according to the title
65 | if (is.function(subdir_fun <- getOption('blogdown.subdir_fun'))) shiny::observe({
66 | sub2 = subdir_fun(input$title)
67 | shiny::updateSelectizeInput(session, 'subdir', selected = sub2, choices = unique(c(
68 | sub2, blogdown:::get_subdirs()
69 | )))
70 | })
71 | shiny::observe({
72 | # calculate file path
73 | if (grepl('^\\s*$', slug <- input$slug)) slug = blogdown:::dash_filename(input$title)
74 | shiny::updateTextInput(
75 | session, 'file', value = blogdown:::post_filename(
76 | slug, input$subdir, shiny::isolate(input$format), input$date, input$lang
77 | )
78 | )
79 | })
80 | shiny::observeEvent(input$format, {
81 | f = input$file
82 | if (f != '') shiny::updateTextInput(
83 | session, 'file', value = xfun::with_ext(f, input$format)
84 | )
85 | }, ignoreInit = TRUE)
86 | shiny::observeEvent(input$done, {
87 | if (grepl('^\\s*$', input$file)) return(
88 | warning('The filename is empty!', call. = FALSE)
89 | )
90 | options(blogdown.author = input$author) # remember the author name
91 | blogdown::new_post(
92 | input$title, author = input$author, ext = input$format,
93 | categories = input$cat, tags = input$tag,
94 | file = gsub('[-[:space:]]+', '-', input$file),
95 | slug = if (input$slug != '') input$slug, subdir = input$subdir,
96 | date = input$date, kind = xfun::sans_ext(input$kind)
97 | )
98 | shiny::stopApp()
99 | })
100 | shiny::observeEvent(input$cancel, {
101 | shiny::stopApp()
102 | })
103 | },
104 | stopOnCancel = FALSE, viewer = shiny::dialogViewer('New Post', height = 500)
105 | )
106 |
--------------------------------------------------------------------------------
/man/build_site.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/render.R
3 | \name{build_site}
4 | \alias{build_site}
5 | \title{Build a website}
6 | \usage{
7 | build_site(local = FALSE, run_hugo = TRUE, build_rmd = FALSE, ...)
8 | }
9 | \arguments{
10 | \item{local}{Whether to build the website locally. This argument is passed to
11 | \code{\link{hugo_build}()}, and \code{local = TRUE} is mainly for serving
12 | the site locally via \code{\link{serve_site}()}.}
13 |
14 | \item{run_hugo}{Whether to run \code{hugo_build()} after R Markdown files are
15 | compiled.}
16 |
17 | \item{build_rmd}{Whether to (re)build R Markdown files. By default, they are
18 | not built. See \sQuote{Details} for how \code{build_rmd = TRUE} works.
19 | Alternatively, it can take a vector of file paths, which means these files
20 | are to be (re)built. Or you can provide a function that takes a vector of
21 | paths of all R Markdown files under the \file{content/} directory, and
22 | returns a vector of paths of files to be built, e.g., \code{build_rmd =
23 | blogdown::filter_timestamp}. A few aliases are currently provided for such
24 | functions: \code{build_rmd = 'newfile'} is equivalent to \code{build_rmd =
25 | blogdown::filter_newfile}, \code{build_rmd = 'timestamp'} is equivalent to
26 | \code{build_rmd = blogdown::filter_timestamp}, and \code{build_rmd =
27 | 'md5sum'} is equivalent to \code{build_rmd = blogdown::filter_md5sum}.}
28 |
29 | \item{...}{Other arguments to be passed to \code{\link{hugo_build}()}.}
30 | }
31 | \description{
32 | Build the site through Hugo, and optionally (re)build R Markdown files.
33 | }
34 | \details{
35 | You can use \code{\link{serve_site}()} to preview your website locally, and
36 | \code{build_site()} to build the site for publishing. However, if you use a
37 | web publishing service like Netlify, you do not need to build the site
38 | locally, but can build it on the cloud. See Section 1.7 of the \pkg{blogdown}
39 | book for more information:
40 | \url{https://bookdown.org/yihui/blogdown/workflow.html}.
41 |
42 | For R Markdown posts, there are a few possible rendering methods: \code{html}
43 | (the default), \code{markdown}, and \code{custom}. The method can be set in
44 | the global option \code{blogdown.method} (usually in the
45 | \file{\link{.Rprofile}} file), e.g., \code{options(blogdown.method =
46 | "custom")}.
47 |
48 | For the \code{html} method, \file{.Rmd} posts are rendered to \file{.html}
49 | via \code{rmarkdown::\link[rmarkdown]{render}()}, which means Markdown is
50 | processed through Pandoc. For the \code{markdown} method, \file{.Rmd} is
51 | rendered to \file{.md}, which will typically be rendered to HTML later by the
52 | site generator such as Hugo.
53 |
54 | For all rendering methods, a custom R script \file{R/build.R} will be
55 | executed if you have provided it under the root directory of the website
56 | (e.g. you can compile Rmd to Markdown through
57 | \code{knitr::\link[knitr]{knit}()} and build the site via
58 | \code{\link{hugo_cmd}()}). The \code{custom} method means it is entirely up
59 | to this R script how a website is rendered. The script is executed via
60 | command line \command{Rscript "R/build.R"}, which means it is executed in a
61 | separate R session. The value of the argument \code{local} is passed to the
62 | command line (you can retrieve the command-line arguments via
63 | \code{\link{commandArgs}(TRUE)}). For other rendering methods, the R script
64 | \file{R/build2.R} (if exists) will be executed after Hugo has built the site.
65 | This can be useful if you want to post-process the site.
66 |
67 | When \code{build_rmd = TRUE}, all Rmd files will be (re)built. You can set
68 | the global option \code{blogdown.files_filter} to a function to determine
69 | which Rmd files to build when \code{build_rmd = TRUE}. This function takes a
70 | vector of Rmd file paths, and should return a subset of these paths to be
71 | built. By default, \code{options(blogdown.files_filter = \link{identity}}.
72 | You can use \code{blogdown::\link{filter_newfile}}, which means to build new
73 | Rmd files that have not been built before, or
74 | \code{blogdown::\link{filter_timestamp}} to build Rmd files if their time
75 | stamps (modification time) are newer than their output files, or
76 | \code{blogdown::\link{filter_md5sum}}, which is more robust in determining if
77 | an Rmd file has been modified (hence needs to be rebuilt).
78 | }
79 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # blogdown
2 |
3 |
4 | [](https://github.com/rstudio/blogdown/actions/workflows/R-CMD-check.yaml)
5 | [](https://CRAN.R-project.org/package=blogdown)
6 | [](https://app.codecov.io/gh/rstudio/blogdown?branch=main)
7 |
8 |
9 | The goal of the blogdown package is to provide a powerful and customizable website output format for [R Markdown](https://rmarkdown.rstudio.com/). Use dynamic R Markdown documents to build webpages featuring:
10 |
11 | + R code (or other programming languages that [knitr](https://yihui.org/knitr/) supports),
12 |
13 | + automatically rendered output such as graphics, tables, analysis results, and HTML widgets, and
14 |
15 | + technical writing elements such as citations, footnotes, and LaTeX math, enabled by the [bookdown package](https://pkgs.rstudio.com/bookdown/).
16 |
17 | By default, blogdown uses [Hugo](https://gohugo.io), a popular open-source static website generator, which provides a fast and flexible way to build your site content to be shared online. Other website generators like Jekyll and Hexo are also supported.
18 |
19 | A useful feature of blogdown sites, compared to other R Markdown-based [websites](https://bookdown.org/yihui/rmarkdown/rmarkdown-site.html), is that you may organize your website content (including R Markdown files) within subdirectories. This makes blogdown a good solution not just for blogging or sites about R — it can also be used to create general-purpose websites to communicate about data science, statistics, data visualization, programming, or education.
20 |
21 | ## Book
22 |
23 |
24 |
25 | ## Installation
26 |
27 | You can install the package via CRAN as follows:
28 |
29 | ```r
30 | install.packages('blogdown')
31 | ```
32 |
33 | If you want to use the development version of the **blogdown** package, you can install the package from GitHub via the [**remotes** package](https://remotes.r-lib.org):
34 |
35 | ```r
36 | remotes::install_github('rstudio/blogdown')
37 | ```
38 | ## Usage
39 |
40 | You may create a new site via the function `blogdown::new_site()` under an _empty_ directory. It will create a skeleton site, download a Hugo theme from Github, add some sample content, launch a web browser and you will see the new site. The sample blog post `hello-world.Rmd` should be opened automatically, and you can edit it. The website will be automatically rebuilt and the page will be refreshed after you save the file.
41 |
42 | If you use RStudio, you can create a new RStudio project for your website from the menu `File -> New Project -> New Directory -> Website using blogdown`.
43 |
44 | The function `blogdown::serve_site()` may be the most frequently used function in this package. It builds the website, loads it into your web browser, and automatically refreshes the browser when you update the Markdown or R Markdown files. Do not use the command line `hugo server` to build or serve the site. It only understands plain Markdown files, and cannot build R Markdown.
45 |
46 | You may not be satisfied with the default site created from `new_site()`. There are two things you may want to do after your first successful experiment with **blogdown**:
47 |
48 | 1. Pick a Hugo theme that you like from https://themes.gohugo.io. All you need is its Github user and repository name, to be passed to the `theme` argument of `new_site()`.
49 | 2. Add more content (pages or posts), or migrate your existing website.
50 |
51 | ## Getting help
52 |
53 | There are two main places to get help:
54 |
55 | 1. The [RStudio community](https://community.rstudio.com/tags/c/R-Markdown/10/blogdown) is a friendly place to ask any questions about **blogdown**. Be sure to use the `blogdown` tag.
56 |
57 | 1. [Stack Overflow](https://stackoverflow.com/questions/tagged/blogdown) is a great source of answers to common **blogdown** questions. Use the tags [`[r][blogdown]`](https://stackoverflow.com/questions/tagged/blogdown+r) if you ask a question.
58 |
59 | ## Code of Conduct
60 |
61 | Please note that the blogdown project is released with a [Contributor Code of Conduct](https://pkgs.rstudio.com/blogdown/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms.
62 |
--------------------------------------------------------------------------------
/pkgdown/assets/ace-1.2.3/mode-r.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/tex_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=function(e){e||(e="text"),this.$rules={start:[{token:"comment",regex:"%.*$"},{token:e,regex:"\\\\[$&%#\\{\\}]"},{token:"keyword",regex:"\\\\(?:documentclass|usepackage|newcounter|setcounter|addtocounter|value|arabic|stepcounter|newenvironment|renewenvironment|ref|vref|eqref|pageref|label|cite[a-zA-Z]*|tag|begin|end|bibitem)\\b",next:"nospell"},{token:"keyword",regex:"\\\\(?:[a-zA-z0-9]+|[^a-zA-z0-9])"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])}]"},{token:e,regex:"\\s+"}],nospell:[{token:"comment",regex:"%.*$",next:"start"},{token:"nospell."+e,regex:"\\\\[$&%#\\{\\}]"},{token:"keyword",regex:"\\\\(?:documentclass|usepackage|newcounter|setcounter|addtocounter|value|arabic|stepcounter|newenvironment|renewenvironment|ref|vref|eqref|pageref|label|cite[a-zA-Z]*|tag|begin|end|bibitem)\\b"},{token:"keyword",regex:"\\\\(?:[a-zA-z0-9]+|[^a-zA-z0-9])",next:"start"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])]"},{token:"paren.keyword.operator",regex:"}",next:"start"},{token:"nospell."+e,regex:"\\s+"},{token:"nospell."+e,regex:"\\w+"}]}};r.inherits(o,s),t.TexHighlightRules=o}),ace.define("ace/mode/r_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules","ace/mode/tex_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=e("./tex_highlight_rules").TexHighlightRules,u=function(){var e=i.arrayToMap("function|if|in|break|next|repeat|else|for|return|switch|while|try|tryCatch|stop|warning|require|library|attach|detach|source|setMethod|setGeneric|setGroupGeneric|setClass".split("|")),t=i.arrayToMap("NULL|NA|TRUE|FALSE|T|F|Inf|NaN|NA_integer_|NA_real_|NA_character_|NA_complex_".split("|"));this.$rules={start:[{token:"comment.sectionhead",regex:"#+(?!').*(?:----|====|####)\\s*$"},{token:"comment",regex:"#+'",next:"rd-start"},{token:"comment",regex:"#.*$"},{token:"string",regex:'["]',next:"qqstring"},{token:"string",regex:"[']",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+[Li]?\\b"},{token:"constant.numeric",regex:"\\d+L\\b"},{token:"constant.numeric",regex:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b"},{token:"constant.numeric",regex:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b"},{token:"constant.language.boolean",regex:"(?:TRUE|FALSE|T|F)\\b"},{token:"identifier",regex:"`.*?`"},{onMatch:function(n){return e[n]?"keyword":t[n]?"constant.language":n=="..."||n.match(/^\.\.\d+$/)?"variable.language":"identifier"},regex:"[a-zA-Z.][a-zA-Z0-9._]*\\b"},{token:"keyword.operator",regex:"%%|>=|<=|==|!=|\\->|<\\-|\\|\\||&&|=|\\+|\\-|\\*|/|\\^|>|<|!|&|\\||~|\\$|:"},{token:"keyword.operator",regex:"%.*?%"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]};var n=(new o("comment")).getRules();for(var r=0;r) is a software engineer at Posit Software, PBC (). He earned his PhD from the Department of Statistics, Iowa State University. He is interested in interactive statistical graphics and statistical computing. As an active R user, he has authored several R packages, such as **knitr**, **bookdown**, **blogdown**, **xaringan**, **animation**, **DT**, **tufte**, **formatR**, **fun**, **mime**, **highr**, **servr**, and **Rd2roxygen**, among which the **animation** package won the 2009 John M. Chambers Statistical Software Award (ASA). He also co-authored a few other R packages, including **shiny**, **rmarkdown**, and **leaflet**.
12 |
13 | In 2006, he founded the Capital of Statistics (), which has grown into a large online community on statistics in China. He initiated the Chinese R conference in 2008, and has been involved in organizing R conferences in China since then. During his PhD training at Iowa State University, he won the Vince Sposito Statistical Computing Award (2011) and the Snedecor Award (2012) in the Department of Statistics.
14 |
15 | He occasionally rants on Twitter (https://twitter.com/xieyihui), and most of the time you can find him on GitHub (https://github.com/yihui).
16 |
17 | He enjoys spicy food as much as classical Chinese literature.
18 |
19 | ## Amber Thomas {-}
20 |
21 | Amber Thomas () is a data journalist and "maker" at the online publication of visual essays: The Pudding (). Her educational background, however, was in quite a different field altogether: marine biology. She has a bachelor's degree in marine biology and chemistry from Roger Williams University and a master's degree in marine sciences from the University of New England. Throughout her academic and professional career as a marine biologist, she realized that she had a love of data analysis, visualization, and storytelling and thus, she switched career paths to something a bit more data focused.
22 |
23 | While looking for work, she began conducting personal projects to expand her knowledge of R's inner workings. She decided to put all of her projects in a single place online (so that she could be discovered, naturally) and after lots of searching, she stumbled upon an early release of the **blogdown** package. She was hooked right away and spent a few days setting up her personal website and writing a tutorial on how she did it. You can find that tutorial and some of her other projects and musings on her blogdown site.
24 |
25 | When she is not crunching numbers and trying to stay on top of her email inbox, Amber is usually getting some fresh Seattle air or cuddling with her dog, Sherlock. If you are looking for her in the digital world, try .
26 |
27 | ## Alison Presmanes Hill {-}
28 |
29 | Alison () is a professor of pediatrics at Oregon Health and Science University's (OHSU) Center for Spoken Language Understanding in Portland, Oregon. Alison earned her PhD in developmental psychology with a concentration in quantitative methods from Vanderbilt University in 2008. Her current research focuses on developing better outcome measures to evaluate the impact of new treatments for children with autism and other neurodevelopmental disorders, using natural language processing and other computational methods. Alison is the author of numerous journal articles and book chapters, and her work has been funded by the National Institutes of Health, the Oregon Clinical and Translational Research Institute, and Autism Speaks.
30 |
31 | In addition to research, Alison teaches graduate-level courses in OHSU's Computer Science program (https://www.ohsu.edu/csee) on statistics, data science, and data visualization using R. She has also developed and led several R workshops and smaller team-based training sessions, and loves to train new "useRs." You can find some of her workshop and teaching materials on GitHub (https://github.com/apreshill) and, of course, on her **blogdown** site.
32 |
33 | Being a new mom, Alison's current favorite books are *The Circus Ship* and *Bats at the Ballgame*. She also does rousing renditions of most Emily Arrow songs (for private audiences only).
34 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, caste, color, religion, or sexual
10 | identity and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the overall
26 | community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or advances of
31 | any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email address,
35 | without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at codeofconduct@posit.co.
63 | All complaints will be reviewed and investigated promptly and fairly.
64 |
65 | All community leaders are obligated to respect the privacy and security of the
66 | reporter of any incident.
67 |
68 | ## Enforcement Guidelines
69 |
70 | Community leaders will follow these Community Impact Guidelines in determining
71 | the consequences for any action they deem in violation of this Code of Conduct:
72 |
73 | ### 1. Correction
74 |
75 | **Community Impact**: Use of inappropriate language or other behavior deemed
76 | unprofessional or unwelcome in the community.
77 |
78 | **Consequence**: A private, written warning from community leaders, providing
79 | clarity around the nature of the violation and an explanation of why the
80 | behavior was inappropriate. A public apology may be requested.
81 |
82 | ### 2. Warning
83 |
84 | **Community Impact**: A violation through a single incident or series of
85 | actions.
86 |
87 | **Consequence**: A warning with consequences for continued behavior. No
88 | interaction with the people involved, including unsolicited interaction with
89 | those enforcing the Code of Conduct, for a specified period of time. This
90 | includes avoiding interactions in community spaces as well as external channels
91 | like social media. Violating these terms may lead to a temporary or permanent
92 | ban.
93 |
94 | ### 3. Temporary Ban
95 |
96 | **Community Impact**: A serious violation of community standards, including
97 | sustained inappropriate behavior.
98 |
99 | **Consequence**: A temporary ban from any sort of interaction or public
100 | communication with the community for a specified period of time. No public or
101 | private interaction with the people involved, including unsolicited interaction
102 | with those enforcing the Code of Conduct, is allowed during this period.
103 | Violating these terms may lead to a permanent ban.
104 |
105 | ### 4. Permanent Ban
106 |
107 | **Community Impact**: Demonstrating a pattern of violation of community
108 | standards, including sustained inappropriate behavior, harassment of an
109 | individual, or aggression toward or disparagement of classes of individuals.
110 |
111 | **Consequence**: A permanent ban from any sort of public interaction within the
112 | community.
113 |
114 | ## Attribution
115 |
116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
117 | version 2.1, available at
118 | .
119 |
120 | Community Impact Guidelines were inspired by
121 | [Mozilla's code of conduct enforcement ladder][https://github.com/mozilla/inclusion].
122 |
123 | For answers to common questions about this code of conduct, see the FAQ at
124 | . Translations are available at .
125 |
126 | [homepage]: https://www.contributor-covenant.org
127 |
--------------------------------------------------------------------------------
/docs/08-domain-name.Rmd:
--------------------------------------------------------------------------------
1 | # Domain Name
2 |
3 | While you can use the free subdomain names\index{Domain Name} like those provided by GitHub or Netlify, it may be a better idea to own a domain name of your own. The cost of an apex domain is minimal (typically the yearly cost is about US$10), and you will enter a much richer world after you purchase a domain name. For example, you are free to point your domain to any web servers, you can create as many subdomain names as you want, and you can even set up your own email accounts using the domain or subdomains. In this appendix, we will explain some basic concepts of domain names, and mention a few (free) services to help you configure your domain name.
4 |
5 | Before we dive into the details, we want to outline the big picture of how a URL works in your web browser. Suppose you typed or clicked a link `http://www.example.com/foo/index.html` in your web browser. What happens behind the scenes before you see the actual web page?
6 |
7 | First, the domain name has to be resolved through the nameservers associated with it. A nameserver knows the DNS (Domain Name System) records of a domain. Typically it will look up the "A records" to point the domain to the IP address of a web server. There are several other types of DNS records, and we will explain them later. Once the web server is reached, the server will look for the file `foo/index.html` under a directory associated with the domain name, and return its content in the response. That is basically how you can see a web page.
8 |
9 | ## Registration
10 |
11 | You can purchase a domain name from many domain name registrars. To stay neutral, we are not going to make recommendations here. You can use your search engine to find a registrar by yourself, or ask your friends for recommendations. However, we would like to remind you of a few things that you should pay attention to when looking for a domain name registrar:
12 |
13 | - You should have the freedom to transfer your domain from the current registrar to other registrars, i.e., they should not lock you in their system. To transfer a domain name, you should be given a code known as the "Transfer Auth Code" or "Auth Code" or "Transfer Key" or something like that.
14 |
15 | - You should be able to customize the nameservers (see Section \@ref(nameservers)) of your domain. By default, each registrar will assign their own nameservers to you, and these nameservers typically work very well. However, there are some special nameservers that provide services more than just DNS records, and you may be interested in using them.
16 |
17 | - Other people can freely look up your personal information, such as your email or postal address, after you register a domain and submit this information to the registrar. This is called the "WHOIS Lookup." You may want to protect your privacy, but your registrar may require an extra payment.
18 |
19 | ## Nameservers
20 |
21 | The main reason why we need nameservers\index{Nameservers} is that we want to use domains instead of IP addresses, although a domain is not strictly necessary for you to be able to access a website. You could use the IP address if you have your own server with a public IP, but there are many problems with this approach. For example, IP addresses are limited (in particular IPv4), not easy to memorize, and you can only host one website per IP address (without using other ports).
22 |
23 | A nameserver is an engine that directs the DNS records of your domain. The most common DNS record is the A record, which maps a domain to an IP address, so that the hosting server can be found via its IP address when a website is accessed through a domain. We will introduce two more types of DNS records in Section \@ref(dns-records): CNAME and MX records.
24 |
25 | In most cases, the default nameservers provided by your domain registrar should suffice, but there is a special technology missing in most nameservers: CNAME flattening. You only need this technology if you want to set a CNAME record for your apex domain. The only use case to my knowledge is when you host your website via Netlify, but want to use the apex domain instead of the `www` subdomain, e.g., you want to use `example.com` instead of `www.example.com`. To make use of this technology, you could consider [Cloudflare,](https://www.cloudflare.com) which provides this DNS feature for free. Basically, all you need to do is to point the nameservers of your domain to the nameservers provided by Cloudflare (of the form `*.ns.cloudflare.com`).
26 |
27 | ## DNS records
28 |
29 | There are many types of DNS\index{DNS Records} records, and you may see a full list on [Wikipedia.](https://en.wikipedia.org/wiki/List_of_DNS_record_types) The most commonly used types may be A, CNAME, and MX records. Figure \@ref(fig:cloudflare-dns) shows a subset of DNS records of my domain `yihui.org` on Cloudflare, which may give you an idea of what DNS records look like. You may query DNS records using command-line tools such as [`dig`](https://en.wikipedia.org/wiki/Dig_(command)) or an app provided by Google: https://toolbox.googleapps.com/apps/dig/.
30 |
31 | ```{r cloudflare-dns, fig.cap='Some DNS records of the domain yihui.org on Cloudflare.', fig.align='center', out.width='100%', echo=FALSE}
32 | knitr::include_graphics('images/cloudflare-dns.png')
33 | ```
34 |
35 | An apex domain can have any number of subdomains. You can set DNS records for the apex domain and any subdomains. You can see from Figure \@ref(fig:cloudflare-dns) that I have several subdomains, e.g., `slides.yihui.org` and `xran.yihui.org`.
36 |
37 | As we have mentioned, an A record points a domain or subdomain to an IP address of the host server. I did not use any A records for my domains since all services I use, such as GitHub Pages and Netlify, support CNAME records well. A CNAME\index{CNAME Record} record is an alias, pointing one domain to another domain. The advantage of using CNAME over A is that you do not have to tie a domain to a fixed IP address. For example, the CNAME record for `t.yihui.org` is `twitter-yihui.netlify.com`. The latter domain is provided by Netlify, and I do not need to know where they actually host the website. They are free to move the host of `twitter-yihui.netlify.com`, and I will not need to update my DNS record. Every time someone visits the website `t.yihui.org`, the web browser will route the traffic to the domain set in the CNAME record. Note that this is different from redirection, i.e., the URL `t.yihui.org` will not be explicitly redirected to `twitter-yihui.netlify.com` (you still see the former in the address bar of your browser).
38 |
39 | Normally, you can set any DNS records for the apex domain except CNAME, but I set a CNAME record for my apex domain `yihui.org`, and that is because Cloudflare supports CNAME flattening. For more information on this topic, you may read the post ["To WWW or not WWW,"](https://www.netlify.com/blog/2017/02/28/to-www-or-not-www/) by Netlify. Personally, I prefer not using the subdomain `www.yihui.org` to keep my URLs short, so I set a CNAME record for both the apex domain `yihui.org` and the `www` subdomain, and Netlify will automatically redirect the `www` subdomain to the apex domain. That said, if you are a beginner, it may be a little easier to configure and use the `www` subdomain, as suggested by Netlify. Note `www` is a conventional subdomain that sounds like an apex domain, but really is not; you can follow this convention or not as you wish.
40 |
41 | For email services, I was an early enough ["netizen",](https://en.wikipedia.org/wiki/Netizen) and when I registered my domain name, Google was still offering free email services to custom domain owners. That is how I can have a custom mailbox `xie@yihui.org`. Now you will have to pay for [G Suite.](https://gsuite.google.com) In Figure \@ref(fig:cloudflare-dns) you can see I have set some MX (stands for "mail exchange") records that point to some Google mail servers. Of course, Google is not the only possible choice when it comes to custom mailboxes. [Migadu](https://www.migadu.com) claims to be the "most affordable email hosting." You may try its free plan and see if you like it. Unless you are going to use your custom mailbox extensively and for professional purposes, the free plan may suffice. In fact, you may create an alias address on Migadu to forward emails to your other email accounts (such as Gmail) if you do not care about an actual custom mailbox. Migadu has provided detailed instructions on how to set the MX records for your domain.
42 |
--------------------------------------------------------------------------------
/man/hugo_cmd.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/hugo.R
3 | \name{hugo_cmd}
4 | \alias{hugo_cmd}
5 | \alias{hugo_version}
6 | \alias{hugo_available}
7 | \alias{hugo_build}
8 | \alias{new_site}
9 | \alias{new_content}
10 | \alias{new_post}
11 | \alias{hugo_convert}
12 | \alias{hugo_server}
13 | \title{Run Hugo commands}
14 | \usage{
15 | hugo_cmd(...)
16 |
17 | hugo_version()
18 |
19 | hugo_available(version = "0.0.0", exact = FALSE)
20 |
21 | hugo_build(
22 | local = FALSE,
23 | args = getOption("blogdown.hugo.args"),
24 | baseURL = NULL,
25 | relativeURLs = NULL
26 | )
27 |
28 | new_site(
29 | dir = ".",
30 | force = NA,
31 | install_hugo = TRUE,
32 | format = "yaml",
33 | sample = TRUE,
34 | theme = "yihui/hugo-lithium",
35 | hostname = "github.com",
36 | theme_example = TRUE,
37 | empty_dirs = FALSE,
38 | to_yaml = TRUE,
39 | netlify = TRUE,
40 | .Rprofile = TRUE,
41 | serve = if (interactive()) "ask" else FALSE
42 | )
43 |
44 | new_content(path, kind = "", open = interactive())
45 |
46 | new_post(
47 | title,
48 | kind = "",
49 | open = interactive(),
50 | author = getOption("blogdown.author"),
51 | categories = NULL,
52 | tags = NULL,
53 | date = Sys.Date(),
54 | time = getOption("blogdown.time", FALSE),
55 | file = NULL,
56 | slug = NULL,
57 | title_case = getOption("blogdown.title_case"),
58 | subdir = getOption("blogdown.subdir", "post"),
59 | ext = getOption("blogdown.ext", ".md")
60 | )
61 |
62 | hugo_convert(to = c("YAML", "TOML", "JSON"), unsafe = FALSE, ...)
63 |
64 | hugo_server(host, port)
65 | }
66 | \arguments{
67 | \item{...}{Arguments to be passed to \code{system2('hugo', ...)}, e.g.
68 | \code{new_content(path)} is basically \code{hugo_cmd(c('new', path))} (i.e.
69 | run the command \command{hugo new path}).}
70 |
71 | \item{version}{A version number.}
72 |
73 | \item{exact}{If \code{FALSE}, check if the current Hugo version is equal to
74 | or higher than the specified \code{version}. If \code{TRUE}, check if the
75 | exact version is available.}
76 |
77 | \item{local}{Whether to build the site for local preview (if \code{TRUE}, all
78 | drafts and future posts will also be built).}
79 |
80 | \item{args}{A character vector of command-line arguments to be passed to
81 | \command{hugo}, e.g., \code{c("--minify", "--quiet")}.}
82 |
83 | \item{baseURL, relativeURLs}{Custom values of \code{baseURL} and
84 | \code{relativeURLs} to override Hugo's default and the settings in the
85 | site's config file.}
86 |
87 | \item{dir}{The directory of the new site.}
88 |
89 | \item{force}{Whether to create the site in a directory even if it is not
90 | empty. By default, \code{force = TRUE} when the directory only contains
91 | hidden, RStudio project (\file{*.Rproj}), \file{LICENSE}, and/or
92 | \file{README} files.}
93 |
94 | \item{install_hugo}{Whether to install Hugo automatically if it is not found.}
95 |
96 | \item{format}{The format of the configuration file, e.g., \code{'yaml'} or
97 | \code{'toml'} (the value \code{TRUE} will be treated as \code{'yaml'}, and
98 | \code{FALSE} means \code{'toml'}). Note that the frontmatter of the new (R)
99 | Markdown file created by \code{new_content()} always uses YAML instead of
100 | TOML or JSON.}
101 |
102 | \item{sample}{Whether to add sample content. Hugo creates an empty site by
103 | default, but this function adds sample content by default.}
104 |
105 | \item{theme}{A Hugo theme on Github (a character string of the form
106 | \code{user/repo}, and you can optionally specify a GIT branch or tag name
107 | after \code{@}, i.e. \code{theme} can be of the form
108 | \code{user/repo@branch}). You can also specify a full URL to the zip file
109 | or tarball of the theme. If \code{theme = NA}, no themes will be installed,
110 | and you have to manually install a theme.}
111 |
112 | \item{hostname}{Where to find the theme. Defaults to \code{github.com};
113 | specify if you wish to use an instance of GitHub Enterprise. You can also
114 | specify the full URL of the zip file or tarball in \code{theme}, in which
115 | case this argument is ignored.}
116 |
117 | \item{theme_example}{Whether to copy the example in the \file{exampleSite}
118 | directory if it exists in the theme. Not all themes provide example sites.}
119 |
120 | \item{empty_dirs}{Whether to keep the empty directories generated by Hugo.}
121 |
122 | \item{to_yaml}{Whether to convert the metadata of all posts to YAML.}
123 |
124 | \item{netlify}{Whether to create a Netlify config file \file{netlify.toml}.}
125 |
126 | \item{.Rprofile}{Whether to create a \file{.Rprofile} file. If \code{TRUE}, a
127 | sample \file{.Rprofile} will be created. It contains some global options,
128 | such as \code{options(blogdown.hugo.version)}, which makes sure you will
129 | use a specific version of Hugo for this site in the future.}
130 |
131 | \item{serve}{Whether to start a local server to serve the site. By default,
132 | this function will ask you in an interactive R session if you want to serve
133 | the site.}
134 |
135 | \item{path}{The path to the new file under the \file{content} directory.}
136 |
137 | \item{kind}{The content type to create, i.e., the Hugo archetype. If the
138 | archetype is a page bundle archetype, it should end with a slash, e.g.,
139 | \code{post/}.}
140 |
141 | \item{open}{Whether to open the new file after creating it. By default, it is
142 | opened in an interactive R session.}
143 |
144 | \item{title}{The title of the post.}
145 |
146 | \item{author}{The author of the post.}
147 |
148 | \item{categories}{A character vector of category names.}
149 |
150 | \item{tags}{A character vector of tag names.}
151 |
152 | \item{date}{The date of the post.}
153 |
154 | \item{time}{Whether to include the time of the day in the \code{date} field
155 | of the post. If \code{TRUE}, the \code{date} will be of the format
156 | \samp{\%Y-\%m-\%dT\%H:\%M:\%S\%z} (e.g., \samp{2001-02-03T04:05:06-0700}).
157 | Alternatively, it can take a character string to be appended to the
158 | \code{date}. It can be important and helpful to include the time in the
159 | date of a post. For example, if your website is built on a server (such as
160 | Netlify or Vercel) and your local timezone is ahead of UTC, your local date
161 | may be a \emph{future} date on the server, and Hugo will not build future
162 | posts by default (unless you use the \command{-F} flag).}
163 |
164 | \item{file}{The filename of the post. By default, the filename will be
165 | automatically generated from the title by replacing non-alphanumeric
166 | characters with dashes, e.g. \code{title = 'Hello World'} may create a file
167 | \file{content/post/2016-12-28-hello-world.md}. The date of the form
168 | \code{YYYY-mm-dd} will be prepended if the filename does not start with a
169 | date.}
170 |
171 | \item{slug}{The slug of the post. By default (\code{NULL}), the slug is
172 | generated from the filename by removing the date and filename extension,
173 | e.g., if \code{file = 'post/2020-07-23-hi-there.md'}, \code{slug} will be
174 | \code{hi-there}. Set \code{slug = ''} if you do not want it.}
175 |
176 | \item{title_case}{A function to convert the title to title case. If
177 | \code{TRUE}, the function is \code{tools::\link[tools]{toTitleCase}()}).
178 | This argument is not limited to title case conversion. You can provide an
179 | arbitrary R function to convert the title.}
180 |
181 | \item{subdir}{If specified (not \code{NULL}), the post will be generated
182 | under a subdirectory under \file{content/}. It can be a nested subdirectory
183 | like \file{post/joe/}.}
184 |
185 | \item{ext}{The filename extension (e.g., \file{.md}, \file{.Rmd}, or
186 | \file{.Rmarkdown}). Ignored if \code{file} has been specified.}
187 |
188 | \item{to}{A format to convert to.}
189 |
190 | \item{unsafe}{Whether to enable unsafe operations, such as overwriting
191 | Markdown source documents. If you have backed up the website, or the
192 | website is under version control, you may try \code{unsafe = TRUE}.}
193 |
194 | \item{host, port}{The host IP address and port; see
195 | \code{servr::\link[servr]{server_config}()}.}
196 | }
197 | \description{
198 | Wrapper functions to run Hugo commands via \code{\link{system2}('hugo',
199 | ...)}.
200 | }
201 | \section{Functions}{
202 | \itemize{
203 | \item \code{hugo_cmd()}: Run an arbitrary Hugo command.
204 |
205 | \item \code{hugo_version()}: Return the version number of Hugo if possible, which is
206 | extracted from the output of \code{hugo_cmd('version')}.
207 |
208 | \item \code{hugo_available()}: Check if Hugo of a certain version (or above if
209 | \code{exact = FALSE}) is available.
210 |
211 | \item \code{hugo_build()}: Build a plain Hugo website. Note that the function
212 | \code{\link{build_site}()} first compiles Rmd files, and then calls Hugo
213 | via \code{hugo_build()} to build the site.
214 |
215 | \item \code{new_site()}: Create a new site (skeleton) via \command{hugo new
216 | site}. The directory of the new site should be empty,
217 |
218 | \item \code{new_content()}: Create a new (R) Markdown file via \command{hugo new}
219 | (e.g. a post or a page).
220 |
221 | \item \code{new_post()}: A wrapper function to create a new post under the
222 | \file{content/post/} directory via \code{new_content()}. If your post will
223 | use R code chunks, you can set \code{ext = '.Rmd'} or the global option
224 | \code{options(blogdown.ext = '.Rmd')} in your \file{~/.Rprofile}.
225 | Similarly, you can set \code{options(blogdown.author = 'Your Name')} so
226 | that the author field is automatically filled out when creating a new post.
227 |
228 | \item \code{hugo_convert()}: A wrapper function to convert source content to
229 | different formats via \command{hugo convert}.
230 |
231 | \item \code{hugo_server()}: Start a Hugo server.
232 |
233 | }}
234 | \examples{
235 | blogdown::hugo_available("1.2.3")
236 | if (interactive()) blogdown::new_site()
237 | }
238 | \references{
239 | The full list of Hugo commands: \url{https://gohugo.io/commands},
240 | and themes: \url{https://themes.gohugo.io}.
241 | }
242 |
--------------------------------------------------------------------------------
/man/figures/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pkgdown/assets/ace-1.2.3/mode-xml.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:""},{token:"text.tag-open.xml",regex:"<"},{include:"reference"},{defaultToken:"text.xml"}],xml_decl:[{token:"entity.other.attribute-name.decl-attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.decl-attribute-equals.xml",regex:"="},{include:"whitespace"},{include:"string"},{token:"punctuation.xml-decl.xml",regex:"\\?>",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:[0-9]+;)|(?:[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:[0-9]+;)|(?:[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|())((?:"+t+":)?"+t+")",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"()("+n+"(?=\\s|>|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),ace.define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getCursorPosition(),a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name"))f=a.stepBackward();var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">"+v+">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value===""?{text:"\n"+d+"\n"+p,selection:[1,d.length,1,d.length]}:{text:"\n"+d}}}}})};r.inherits(a,i),t.XmlBehaviour=a}),ace.define("ace/mode/folding/xml",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/range","ace/mode/folding/fold_mode","ace/token_iterator"],function(e,t,n){"use strict";function l(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column"},this.createWorker=function(e){var t=new f(["ace"],"ace/mode/xml_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/xml"}.call(l.prototype),t.Mode=l})
--------------------------------------------------------------------------------
/docs/04-migration.Rmd:
--------------------------------------------------------------------------------
1 | # Migration
2 |
3 | Usually, it is easier to start a new website than migrating\index{Site Migration} an old one to a new framework, but you may have to do it anyway because of the useful content on the old website that should not simply be discarded. A lazy solution is to leave the old website as is, start a new website with a new domain, and provide a link to the old website. This may be a hassle to your readers, and they may not be able to easily discover the gems that you created on your old website, so I recommend that you migrate your old posts and pages to the new website if possible.
4 |
5 | This process may be easy or hard, depending on how complicated your old website is. The bad news is that there is unlikely to be a universal or magical solution, but I have provided some helper functions in **blogdown** as well as a Shiny application to assist you, which may make it a little easier for you to migrate from Jekyll and WordPress sites.
6 |
7 | To give you an idea about the possible amount of work required, I can tell you that it took me a whole week (from the morning to midnight every day) to migrate several of my personal Jekyll-based websites to Hugo and **blogdown**. The complication in my case was not only Jekyll, but also the fact that I built several separate Jekyll websites (because I did not have a choice in Jekyll) and I wanted to unite them in the same repository. Now my two blogs (Chinese and English), the **knitr** [@R-knitr] package documentation, and the **animation** package [@R-animation] documentation are maintained in the same repository: https://github.com/rbind/yihui. I have about 1000 pages on this website, most of which are blog posts. It used to take me more than 30 seconds to preview my blog in Jekyll, and now it takes less than 2 seconds to build the site in Hugo.
8 |
9 | Another complicated example is the website of Rob J Hyndman (https://robjhyndman.com). He started his website in 1993 (12 years before me), and had accumulated a lot of content over the years. You can read the post https://support.rbind.io/2017/05/15/converting-robjhyndman-to-blogdown/ for the stories about how he migrated his WordPress website to **blogdown**. The key is that you probably need a long international flight when you want to migrate a complicated website.
10 |
11 | A simpler example is the Simply Statistics blog (https://simplystatistics.org). Originally it was built on Jekyll^[It was migrated from WordPress a few years ago. The WordPress site was actually migrated from an earlier Tumblr blog.] and the source was hosted in the GitHub repository https://github.com/simplystats/simplystats.github.io. I volunteered to help them move to **blogdown**, and it took me about four hours. My time was mostly spent on cleaning up the YAML metadata of posts and tweaking the Hugo theme. They had about 1000 posts, which sounds like a lot, but the number does not really matter, because I wrote an R script to process all posts automatically. The new repository is at https://github.com/rbind/simplystats.
12 |
13 | If you do not really have too many pages (e.g., under 20), I recommend that you cut and paste them to Markdown files, because it may actually take longer to write a script to process these pages.
14 |
15 | It is likely that some links will be broken after the migration because Hugo renders different links for your pages and posts. In that case, you may either fix the permanent links (e.g., by tweaking the slug of a post), or use 301 redirects (e.g., on Netlify).
16 |
17 | ## From Jekyll
18 |
19 | When converting a Jekyll\index{Jekyll} website to Hugo, the most challenging part is the theme. If you want to keep exactly the same theme, you will have to rewrite your Jekyll templates using Hugo's syntax (see Section \@ref(templates)). However, if you can find an existing theme in Hugo (https://themes.gohugo.io), things will be much easier, and you only need to move the content of your website to Hugo, which is relatively easy. Basically, you copy the Markdown pages and posts to the `content/` directory in Hugo and tweak these text files.
20 |
21 | Usually, posts in Jekyll are under the `_posts/` directory, and you can move them to `content/post/` (you are free to use other directories). Then you need to define a custom rule for permanent URLs in `config.toml` like (see Section \@ref(options)):
22 |
23 | ```js
24 | [permalinks]
25 | post = "/:year/:month/:day/:slug/"
26 | ```
27 |
28 | This depends on the format of the URLs you used in Jekyll (see the `permalink` option in your `_config.yml`).
29 |
30 | If there are static assets like images, they can be moved to the `static/` directory in Hugo.
31 |
32 | Then you need to use your favorite tool with some string manipulation techniques to process all Markdown files. If you use R, you can list all Markdown files and process them one by one in a loop. Below is a sketch of the code:
33 |
34 | ```{r eval=FALSE, tidy=FALSE}
35 | files = list.files(
36 | 'content/', '[.](md|markdown)$', full.names = TRUE,
37 | recursive = TRUE
38 | )
39 | for (f in files) {
40 | xfun::process_file(f, function(x) {
41 | # process x here and return the modified x
42 | x
43 | })
44 | }
45 | ```
46 |
47 | The `process_file()` function from the **xfun** package takes a filename and a processor function to manipulate the content of the file, and writes the modified text back to the file.
48 |
49 | To give you an idea of what a processor function may look like, I provided a few simple helper functions in **blogdown**, and below are two of them:
50 |
51 | ```{r comment=''}
52 | blogdown:::remove_extra_empty_lines
53 | blogdown:::process_bare_urls
54 | ```
55 |
56 | The first function substitutes two or more empty lines with a single empty line. The second function replaces links of the form `[url](url)` with ``. There is nothing wrong with excessive empty lines or the syntax `[url](url)`, though. These helper functions may make your Markdown text a little cleaner. You can find all such helper functions at https://github.com/rstudio/blogdown/blob/master/R/clean.R. Note they are not exported from **blogdown**, so you need triple colons to access them.
57 |
58 | The YAML metadata of your posts may not be completely clean, especially when your Jekyll website was actually converted from an earlier WordPress website. The internal helper function `blogdown:::modify_yaml()` may help you clean up the metadata. For example, below is the YAML metadata of a blog post of Simply Statistics when it was built on Jekyll:
59 |
60 | ```yaml
61 | ---
62 | id: 4155
63 | title: Announcing the JHU Data Science Hackathon 2015
64 | date: 2015-07-28T13:31:04+00:00
65 | author: Roger Peng
66 | layout: post
67 | guid: http://simplystatistics.org/?p=4155
68 | permalink: /2015/07/28/announcing-the-jhu-data-science-hackathon-2015
69 | pe_theme_meta:
70 | - 'O:8:"stdClass":2:{s:7:"gallery";O:8:"stdClass":...}'
71 | al2fb_facebook_link_id:
72 | - 136171103105421_837886222933902
73 | al2fb_facebook_link_time:
74 | - 2015-07-28T17:31:11+00:00
75 | al2fb_facebook_link_picture:
76 | - post=http://simplystatistics.org/?al2fb_image=1
77 | dsq_thread_id:
78 | - 3980278933
79 | categories:
80 | - Uncategorized
81 | ---
82 | ```
83 |
84 | You can discard the YAML fields that are not useful in Hugo. For example, you may only keep the fields `title`, `author`, `date`, `categories`, and `tags`, and discard other fields. Actually, you may also want to add a `slug` field that takes the base filename of the post (with the leading date removed). For example, when the post filename is `2015-07-28-announcing-the-jhu-data-science-hackathon-2015.md`, you may want to add `slug: announcing-the-jhu-data-science-hackathon-2015` to make sure the URL of the post on the new site remains the same.
85 |
86 | Here is the code to process the YAML metadata of all posts:
87 |
88 | ```{r eval=FALSE, tidy=FALSE}
89 | for (f in files) {
90 | blogdown:::modify_yaml(f, slug = function(old, yaml) {
91 | # YYYY-mm-dd-name.md -> name
92 | gsub('^\\d{4}-\\d{2}-\\d{2}-|[.](md|markdown)', '', f)
93 | }, categories = function(old, yaml) {
94 | # remove the Uncategorized category
95 | setdiff(old, 'Uncategorized')
96 | }, .keep_fields = c(
97 | 'title', 'author', 'date', 'categories', 'tags', 'slug'
98 | ), .keep_empty = FALSE)
99 | }
100 | ```
101 |
102 | You can pass a file path to `modify_yaml()`, define new YAML values (or functions to return new values based on the old values), and decide which fields to preserve (`.keep_fields`). You may discard empty fields via `.keep_empty = FALSE`. The processed YAML metadata is below, which looks much cleaner:
103 |
104 | ```yaml
105 | ---
106 | title: Announcing the JHU Data Science Hackathon 2015
107 | author: Roger Peng
108 | date: '2015-07-28T13:31:04+00:00'
109 | slug: announcing-the-jhu-data-science-hackathon-2015
110 | ---
111 | ```
112 |
113 | ## From WordPress
114 |
115 | From our experience, the best way to import WordPress\index{WordPress} blog posts to Hugo is to import them to Jekyll, and write an R script to clean up the YAML metadata of all pages if necessary, instead of using the migration tools listed on the [official guide,](https://gohugo.io/tools/) including the WordPress plugin `wordpress-to-hugo-exporter`.
116 |
117 | To our knowledge, the best tool to convert a WordPress website to Jekyll is the Python tool [Exitwp.](https://github.com/thomasf/exitwp) Its author has provided detailed instructions on how to use it. You have to know how to install Python libraries and execute Python scripts. If you do not, I have provided an online tool at https://github.com/yihui/travis-exitwp. You can upload your WordPress XML file there, and get a download link to a ZIP archive that contains your posts in Markdown.
118 |
119 | The biggest challenge in converting WordPress posts to Hugo is to clean up the post content in Markdown. Fortunately, I have done this for three different WordPress blogs,^[The RViews blog (https://rviews.rstudio.com), the RStudio blog (https://blog.rstudio.com), and Karl Broman's blog (http://kbroman.org). The RViews blog took me a few days. The RStudio blog took me one day. Karl Broman's blog took me an hour.] and I think I have managed to automate this process as much as possible. You may refer to the pull request I submitted to Karl Broman to convert his WordPress posts to Markdown (https://github.com/kbroman/oldblog_xml/pull/1), in which I provided both the R script and the Markdown files. I recommend that you go to the "Commits" tab and view all my GIT commits one by one to see the full process.
120 |
121 | The key is the R script https://github.com/yihui/oldblog_xml/blob/master/convert.R, which converts the WordPress XML file to Markdown posts and cleans them. Before you run this script on your XML file, you have to adjust a few parameters, such as the XML filename, your old WordPress site's URL, and your new blog's URL.
122 |
123 | Note that this script depends on the Exitwp tool. If you do not know how to run Exitwp, please use the online tool I mentioned before (travis-exitwp), and skip the R code that calls Exitwp.
124 |
125 | The Markdown posts should be fairly clean after the conversion, but there may be remaining HTML tags in your posts, such as `
` and `
`. You will need to manually clean them, if any exist.
126 |
127 | ## From other systems
128 |
129 | If you have a website built by other applications or systems, your best way to go may be to import your website to WordPress first, export it to Jekyll, and clean up the Markdown files. You can try to search for solutions like "how to import blogger.com to WordPress" or "how to import Tumblr to WordPress."
130 |
131 | If you are very familiar with web scraping techniques, you can also scrape the HTML pages of your website, and convert them to Markdown via Pandoc, e.g.,
132 |
133 | ```{r eval=FALSE, tidy=FALSE}
134 | rmarkdown::pandoc_convert(
135 | 'foo.html', to = 'markdown', output = 'foo.md'
136 | )
137 | ```
138 |
139 | I have actually tried this way on a website, but was not satisfied, since I still had to heavily clean up the Markdown files. If your website is simpler, this approach may work better for you.
140 |
--------------------------------------------------------------------------------