Skip to content

Commit

Permalink
Converting to LaTeX.
Browse files Browse the repository at this point in the history
  • Loading branch information
gvwilson committed Sep 25, 2020
1 parent 114f302 commit 8c49a3a
Show file tree
Hide file tree
Showing 64 changed files with 3,962 additions and 1,293 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Expand Up @@ -3,4 +3,10 @@
.jekyll-cache
.jekyll-metadata
_site
book.aux
book.log
book.out
book.pdf
book.tex
book.toc
node_modules
1 change: 1 addition & 0 deletions .nojekyll
@@ -0,0 +1 @@
no Jekyll
45 changes: 21 additions & 24 deletions CONTRIBUTING.md
Expand Up @@ -57,50 +57,47 @@ This project uses [Martha's Rules](https://journals.sagepub.com/doi/10.1177/0886

## Format

1. We use [EJS][ejs] to create our website,
so please write in Markdown.
1. We use [EJS][ejs] to create our website.
Please write in Markdown where you can, and use HTML tags for special cases.

1. Each lesson is in a file `./slug/index.md`,
where "slug" is a hyphenated short name for the topic (e.g., `writing-functions`).

1. `_config.yml` contains configuration information and metadata about lessons and external links.

1. Use relative links `[like this](../slug/)` to refer from one lesson to another.
1. `config.yml` contains configuration information and metadata about chapters and appendices.

1. Use level-2 headings for sub-topics, and phrase these as questions
(e.g., "How do I check if a file exists?").
Do not use headings below level 3.

1. Put definitions of external links in the `links` table in `_config.yml`
1. Put definitions of external links in `links.yml`
and refer to them using `[text to display][link-key]`.
Entries should be in alphabetical order by slug.

1. Write cross-references like `<xref key="slug"></xref>` or `<xref key="slug">some text</xref>`
to refer from one chapter or appendix to another.
(We cannot use the empty tag `<xref key="slug"/>` because the parser doesn't like it.)
`static/site.js` converts this to a glossary reference in the online version
and `bin/latex.js` converts it for the PDF version;
if no text is provided inside the tag,
we fill it in with `Chapter N` or `Appendix X`.

1. When defining a term, use `<g key="some_key">some text</g>`.
We do some post-processing in `static/site.js` to convert this to a glossary reference.
The key must exist in either `gloss.yml` (our local glossary),
and again, `static/site.js` and `bin/latex.js` convert this to a glossary reference.

1. Use `<cite>Key123,Key456</cite>` for bibliographic citations.
We do some post-processing in `static/site.js` to convert these to bibliographic citations.
The keys must match those in the `bib` section of `_config.yml`.
The keys must exist in `bib.yml`, and yes, `static/site.js` and `bin/latex.js` do the conversions.

1. Use first person plural ("we" rather than "you"),
Simplified English (i.e., American spelling),
and the Oxford comma.
Do not use exclamation marks---few things are actually that surprising the first time around,
and none the second.

## Tools

1. Run `npm run build` to build the the website in `_site`
and `npm run serve` to serve it locally on port 4000.

1. Rebuilding the glossary takes a bit of work---we hope it will be easier once `glosario-js` is installable.
In the meantime:
1. Clone <https://github.com/carpentries/glosario/> somewhere (not in this repository).
1. Run

```
bin/gloss-keys.js index.md */index.md | ~/glosario/utils/merge-glossaries.py -w - ~/glosario/glossary.yml > _glosario.yml
```
## Tasks and Tools

to create a local slice of the Glosario data.
1. Run `npm run gloss` to regenerate `gloss.md` by comining the glossaries.
1. NPM doesn't allow us to document our tasks or make them depend on one another,
so we use [NPM][npm] to manage packages and [Bajel][bajel] to run tasks---run
`npx bajel help` to get help with the latter.
(Note that it's `npx` with an 'x' rather than `npm` with an 'm'.)
All of the commands are in `build.yaml` and all of our tools are in the `bin` directory.
16 changes: 9 additions & 7 deletions authors.md
@@ -1,10 +1,12 @@
---
---

<% site.authors.forEach(person => { %>
<div class="author">
<h2><% if (person.link) { %><a href="<%- person.link %>"><% } %><%- person.name %><% if (person.link) { %></a><% } %></h2>
<img src="<%- `${relativeRoot}/static/${person.avatar}` %>" alt="<%- person.name %>" />
<p class="noindent"><%- person.bio.replace('\n', '').trim() %></p>
</div>
<% }) %>
[Greg Wilson](http://third-bit.com) has worked for 35 years in both industry and academia,
and is the author or editor of several books on computing,
including <em>Beautiful Code</em>,
<em><a href="http://aosabook.org">The Architecture of Open Source Applications</a></em>,
<em><a href="http://js4ds.org">JavaScript for Data Science</a></em>,
and <em><a href="http://teachtogether.tech">Teaching Tech Together</a></em>.
He was the co-founder and first Executive Director of <a href="http://carpentries.org">Software Carpentry</a>,
a non-profit organization that teaches basic computing skills to researchers,
and was the recipient of the ACM SIGSOFT Influential Educator Award in 2020.
20 changes: 10 additions & 10 deletions bib.md
Expand Up @@ -2,78 +2,78 @@
---

<dl class="bibliography">
<dt id="Brown2011">Brown2011</dt>
<dt id="Brown2011" class="bibliography">Brown2011</dt>
<dd>
Amy Brown and Greg Wilson (eds.):
<a href="http://aosabook.org">The Architecture of Open Source Applications: Elegance, Evolution, and a Few Fearless Hacks</a>.
Lulu, 2011, 978-1257638017.
<em>Descriptions of the architecture of two dozen open source systems written by the people who created them.</em>
</dd>

<dt id="Brown2012">Brown2012</dt>
<dt id="Brown2012" class="bibliography">Brown2012</dt>
<dd>
Amy Brown and Greg Wilson (eds.):
<a href="http://aosabook.org">The Architecture of Open Source Applications: Structure, Scale, and a Few More Fearless Hacks</a>.
Lulu, 2012, 978-0201103427.
<em>More descriptions open source system architectures written by those systems' creators.</em>
</dd>

<dt id="Brown2016">Brown2016</dt>
<dt id="Brown2016" class="bibliography">Brown2016</dt>
<dd>
Amy Brown and Michael DiBernardo (eds.):
<a href="http://aosabook.org">500 Lines or Less: Experienced Programmers Solve Interesting Problems</a>.
Lulu, 2016, 978-1329871274.
<em>Scale models of real applications, with commentary from their creators.</em>
</dd>

<dt id="Davis2018">Davis2018</dt>
<dt id="Davis2018" class="bibliography">Davis2018</dt>
<dd>
Ashley Davis:
<a href="https://www.manning.com/books/data-wrangling-with-javascript">Data Wrangling with JavaScript</a>.
Manning, 2018, 978-1617294846.
<em>A step-by-step guide to managing data with JavaScript.</em>
</dd>

<dt id="Kernighan1979">Kernighan1979</dt>
<dt id="Kernighan1979" class="bibliography">Kernighan1979</dt>
<dd>
Brian W. Kernighan and P. J. Plauger:
The Elements of Programming Style.
McGraw-Hill, 1979, 978-0070342071.
<em>An early and influential description of the Unix programming philosophy.</em>
</dd>

<dt id="Kernighan1983">Kernighan1983</dt>
<dt id="Kernighan1983" class="bibliography">Kernighan1983</dt>
<dd>
Brian W. Kernighan and Rob Pike:
The Unix Programming Environment.
Prentice-Hall, 1983, 978-0139376818.
<em>An influential early description of Unix.</em>
</dd>

<dt id="Kernighan1988">Kernighan1988</dt>
<dt id="Kernighan1988" class="bibliography">Kernighan1988</dt>
<dd>
Brian W. Kernighan and Dennis M. Ritchie:
The C Programming Language.
Prentice-Hall, 1988, 978-0131103627.
<em>The book that made C a popular programming language.</em>
</dd>

<dt id="Minahan1986">Minahan1986</dt>
<dt id="Minahan1986" class="bibliography">Minahan1986</dt>
<dd>
Anne Minahan:
"Martha's Rules".
Affilia, 1(2), 1986, <a href="https://doi.org/10.1177/088610998600100206">10.1177/088610998600100206</a>.
<em>Describes a lightweight set of rules for consensus-based decision making.</em>
</dd>

<dt id="Osmani2017">Osmani2017</dt>
<dt id="Osmani2017" class="bibliography">Osmani2017</dt>
<dd>
Addy Osmani:
"<a href="https://addyosmani.com/resources/essentialjsdesignpatterns/book/">Learning JavaScript Design Patterns</a>".
<em>A guide to design patterns using JavaScript.</em>
</dd>

<dt id="Smith2011">Smith2011</dt>
<dt id="Smith2011" class="bibliography">Smith2011</dt>
<dd>
Peter Smith:
Software Build Systems: Principles and Experience.
Expand Down
2 changes: 1 addition & 1 deletion bin/make-bib.js → bin/bib.js
Expand Up @@ -209,7 +209,7 @@ const descriptionEnd = (entry) => {
const key = (entry) => {
assert('key' in entry,
`Every entry must have key`)
return `<dt id="${entry.key}">${entry.key}</dt>`
return `<dt id="${entry.key}" class="bibliography">${entry.key}</dt>`
}

/**
Expand Down
7 changes: 7 additions & 0 deletions bin/clean.js
@@ -0,0 +1,7 @@
#!/usr/bin/env node

'use strict'

import rimraf from 'rimraf'

process.argv.slice(2).forEach(pattern => rimraf.sync(pattern))
46 changes: 39 additions & 7 deletions bin/make-gloss.js → bin/gloss.js
Expand Up @@ -39,7 +39,7 @@ const main = () => {
download(config, GLOSARIO_URL).then(glosario => {
const local = getGlossary(config)
const merged = mergeGlossaries(glosario, local)
const required = getRequired(config)
const required = getRequired(config, merged)
const filtered = filterGlossary(merged, required)
const text = makeGloss(filtered)
fs.writeFileSync(config.output, text)
Expand Down Expand Up @@ -99,16 +99,36 @@ const mergeGlossaries = (...glossaries) => {
* @param {Object} gloss All glossary entries.
*/
const getRequired = (config, gloss) => {
// Required in files.
const fromFiles = new Set(
let result = new Set()
let expanded = new Set(
config.sources.map(filename => {
const text = fs.readFileSync(filename, 'utf-8')
return [...text.matchAll(/<g\s+key="(.+?)">/g)]
.map(match => match[1])
}).flat()
)
// FIXME: transitive closure of cross-references.
return fromFiles
while (expanded.size > result.size) {
result = expanded
expanded = addKeysFromDefs(gloss, result)
}
return result
}

/**
* Expand definitions by looking at bodies of definitions.
* @param {Object} gloss All glossary entries.
* @param {Set} soFar Keys found so far.
* @returns {Set} Expanded set of keys.
*/
const addKeysFromDefs = (gloss, soFar) => {
const result = new Set()
for (let key of soFar) {
result.add(key)
for (match in gloss[key].en.def.matchAll(/]\(#(.+?)\)/g)) {
result.add(match[1])
}
}
return result
}

/**
Expand Down Expand Up @@ -162,16 +182,28 @@ const sortTerms = (data) => {
*/
const makeEntry = (entry) => {
const acronym = ('acronym' in entry.en) ? ` (${entry.en.acronym})` : ''
const term = `<dt id="${entry.slug}">${entry.en.term}${acronym}</dt>`
const term = `<dt id="${entry.slug}" class="glossary">${entry.en.term}${acronym}</dt>`
const mdi = new MarkdownIt({html: true})
const def = mdi.render(entry.en.def.replace('\n', ' '))
.replace('<p>', '')
.replace('</p>', '')
.trim()
const body = `<dd>${def}</dd>`
.replace(/<a href="#(.+?)">(.+?)<\/a>/g, fixCrossRef)
const body = `<dd class="glossary">${def}</dd>`
return `${term}\n${body}`
}

/**
* Fix internal cross-reference links.
* @param {string} match Entire matching string.
* @param {string} key Key embedded in URL.
* @param {string} value Visible text.
* @returns {string} Patched definition.
*/
const fixCrossRef = (match, key, value) => {
return `<g key="${key}">${value}</g>`
}

/**
* Create a promise for downloading a file from a URL.
* @param {Object} config Program configuration.
Expand Down
35 changes: 27 additions & 8 deletions bin/make-site.js → bin/html.js
Expand Up @@ -25,6 +25,11 @@ const DEFAULTS = {
rootDir: '.'
}

/**
* Files to copy directly.
*/
const COPY_FILES = ['.nojekyll', 'CNAME']

/**
* File containing Markdown-formatted links.
*/
Expand All @@ -50,8 +55,7 @@ const main = () => {
loadFiles(allFiles)
rimraf.sync(config.outputDir)
allFiles.forEach(fileInfo => translateFile(config, fileInfo, linksText))
copyFiles(config)
noJekyll(config)
finalize(config)
}

/**
Expand Down Expand Up @@ -177,10 +181,11 @@ const slugify = (text) => {
}

/**
* Copy files verbatim.
* Copy static files and save numbering data.
* @param {Object} config Configuration.
*/
const copyFiles = (config) => {
const finalize = (config) => {
// Simple files.
const excludes = config.exclude.map(pattern => new minimatch.Minimatch(pattern))
const toCopy = config.copy
.map(pattern => path.join(config.rootDir, pattern))
Expand All @@ -192,15 +197,29 @@ const copyFiles = (config) => {
ensureOutputDir(dest)
fs.copyFileSync(source, dest)
})

// Numbering.
const numbering = buildNumbering(config)
fs.writeFileSync(path.join(config.outputDir, 'numbering.js'),
JSON.stringify(numbering, null, 2))
}

/**
* Signal that the site is not built with Jekyll (for GitHub Pages).
* Build numbering lookup table for chapters and appendices.
* @param {Object} config Configuration.
* @returns {Object} slug-to-number-or-letter lookup table.
*/
const noJekyll = (config) => {
const filePath = path.join(config.outputDir, '.nojekyll')
fs.writeFileSync(filePath, 'no Jekyll')
const buildNumbering = (config) => {
const result = {}
const numbered = [...config.extras, ...config.chapters]
numbered.forEach((fileInfo, i) => {
result[fileInfo.slug] = `${i}`
})
const start = 'A'.charCodeAt(0)
config.appendices.forEach((fileInfo, i) => {
result[fileInfo.slug] = String.fromCharCode(start + i)
})
return result
}

/**
Expand Down

0 comments on commit 8c49a3a

Please sign in to comment.