Skip to content

Commit bb4ae56

Browse files
committed
Make build scripts ESM
1 parent 2f24557 commit bb4ae56

30 files changed

+104
-77
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
/html/[012]*.html
1313
/html/ejs.js
1414
/code/chapter/*
15+
/code/chapter_info.js
1516
/code/file_server.js
1617
/code/skillsharing.zip
1718
/code/solutions/20_3_a_public_space_on_the_web.zip

00_intro.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ Each line of the previous program contains a single instruction. It could be wri
114114

115115
Although that is already more readable than the soup of bits, it is still rather obscure. Using names instead of numbers for the instructions and memory locations helps.
116116

117-
```{lang: "text/plain"}
117+
```{lang: "null"}
118118
Set “total” to 0.
119119
Set “count” to 1.
120120
[loop]

02_program_structure.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ Binding names can be any word. Digits can be part of binding names—`catch22` i
137137

138138
Words with a special meaning, such as `let`, are _((keyword))s_, and they may not be used as binding names. There are also a number of words that are "reserved for use" in ((future)) versions of JavaScript, which also can't be used as binding names. The full list of keywords and reserved words is rather long.
139139

140-
```{lang: "text/plain"}
140+
```{lang: "null"}
141141
break case catch class const continue debugger default
142142
delete do else enum export extends false finally for
143143
function if implements import interface in instanceof let

04_data.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ JSON looks similar to JavaScript's way of writing arrays and objects, with a few
940940

941941
A journal entry might look like this when represented as JSON data:
942942

943-
```{lang: "application/json"}
943+
```{lang: "json"}
944944
{
945945
"squirrel": false,
946946
"events": ["work", "touched tree", "pizza", "running"]

05_higher_order.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Though I can fluently read only Latin characters, I appreciate the fact that peo
219219
The example ((data set)) contains some pieces of information about the 140 scripts defined in Unicode. It is available in the [coding sandbox](https://eloquentjavascript.net/code#5) for this chapter[ ([_https://eloquentjavascript.net/code#5_](https://eloquentjavascript.net/code#5))]{if book} as the `SCRIPTS` binding. The binding contains an array of objects, each of which describes a script.
220220

221221

222-
```{lang: "application/json"}
222+
```{lang: "json"}
223223
{
224224
name: "Coptic",
225225
ranges: [[994, 1008], [11392, 11508], [11513, 11520]],

09_regexp.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ This makes use of the fact that the value of an ((assignment)) expression (`=`)
721721

722722
To conclude the chapter, we'll look at a problem that calls for ((regular expression))s. Imagine we are writing a program to automatically collect information about our enemies from the ((Internet)). (We will not actually write that program here, just the part that reads the ((configuration)) file. Sorry.) The configuration file looks like this:
723723

724-
```{lang: "text/plain"}
724+
```{lang: "null"}
725725
searchengine=https://duckduckgo.com/?q=$1
726726
spitefulness=9.7
727727

10_modules.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ A package is a chunk of code that can be distributed on its own. NPM is a reposi
384384

385385
These are the bindings that the project from [Chapter ?](robot) creates:
386386

387-
```{lang: "text/plain"}
387+
```{lang: "null"}
388388
roads
389389
buildGraph
390390
roadGraph

12_language.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Expressions of type `"value"` represent literal strings or numbers. Their `value
6161

6262
The `>(x, 5)` part of the previous program would be represented like this:
6363

64-
```{lang: "application/json"}
64+
```{lang: "json"}
6565
{
6666
type: "apply",
6767
operator: {type: "word", name: ">"},

13_browser.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ A _network ((protocol))_ describes a style of communication over a ((network)).
3434

3535
For example, the _Hypertext Transfer Protocol_ (((HTTP))) is a protocol for retrieving named ((resource))s (chunks of information, such as web pages or pictures). It specifies that the side making the request should start with a line like this, naming the resource and the version of the protocol that it is trying to use:
3636

37-
```{lang: "text/plain"}
37+
```{lang: "null"}
3838
GET /index.html HTTP/1.1
3939
```
4040

14_dom.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ When multiple rules define a value for the same property, the most recently read
513513

514514
It is possible to target things other than ((tag)) names in CSS rules. A rule for `.abc` applies to all elements with `"abc"` in their `class` attribute. A rule for `#xyz` applies to the element with an `id` attribute of `"xyz"` (which should be unique within the document).
515515

516-
```{lang: "text/css"}
516+
```{lang: "css"}
517517
.subtle {
518518
color: gray;
519519
font-size: 80%;

16_game.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ As mentioned, the background is drawn as a `<table>` element. This nicely corres
403403

404404
The following ((CSS)) makes the table look like the background we want:
405405

406-
```{lang: "text/css"}
406+
```{lang: "css"}
407407
.background { background: rgb(52, 166, 251);
408408
table-layout: fixed;
409409
border-spacing: 0; }
@@ -441,7 +441,7 @@ function drawActors(actors) {
441441

442442
To give an element more than one class, we separate the class names by spaces. In the ((CSS)) code shown next, the `actor` class gives the actors their absolute position. Their type name is used as an extra class to give them a color. We don't have to define the `lava` class again because we're reusing the class for the lava grid squares we defined earlier.
443443

444-
```{lang: "text/css"}
444+
```{lang: "css"}
445445
.actor { position: absolute; }
446446
.coin { background: rgb(241, 229, 89); }
447447
.player { background: rgb(64, 64, 64); }
@@ -465,7 +465,7 @@ DOMDisplay.prototype.syncState = function(state) {
465465

466466
By adding the level's current status as a class name to the wrapper, we can style the player actor slightly differently when the game is won or lost by adding a ((CSS)) rule that takes effect only when the player has an ((ancestor element)) with a given class.
467467

468-
```{lang: "text/css"}
468+
```{lang: "css"}
469469
.lost .player {
470470
background: rgb(160, 64, 64);
471471
}
@@ -484,7 +484,7 @@ After touching ((lava)), the player's color turns dark red, suggesting scorching
484484

485485
We can't assume that the level always fits in the _viewport_—the element into which we draw the game. That is why the `scrollPlayerIntoView` call is needed. It ensures that if the level is protruding outside the viewport, we scroll that viewport to make sure the player is near its center. The following ((CSS)) gives the game's wrapping DOM element a maximum size and ensures that anything that sticks out of the element's box is not visible. We also give it a relative position so that the actors inside it are positioned relative to the level's top-left corner.
486486

487-
```{lang: "text/css"}
487+
```{lang: "css"}
488488
.game {
489489
overflow: hidden;
490490
max-width: 600px;

20_node.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ In the `npm install` example, you could see a ((warning)) about the fact that th
186186

187187
The robot simulation from [Chapter ?](robot), as modularized in the exercise in [Chapter ?](modules#modular_robot), might have a `package.json` file like this:
188188

189-
```{lang: "application/json"}
189+
```{lang: "json"}
190190
{
191191
"author": "Marijn Haverbeke",
192192
"name": "eloquent-javascript-robot",

21_skillsharing.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ We will use ((JSON)) as the format of our request and response body. Like in the
8282

8383
A `GET` request to `/talks` returns a JSON document like this:
8484

85-
```{lang: "application/json"}
85+
```{lang: "json"}
8686
[{"title": "Unituning",
8787
"presenter": "Jamal",
8888
"summary": "Modifying your cycle for extra style",

Makefile

+13-13
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ html: $(foreach CHAP,$(CHAPTERS),html/$(CHAP).html) html/ejs.js \
88
code/skillsharing.zip code/solutions/20_3_a_public_space_on_the_web.zip html/code/chapter_info.js
99

1010
html/%.html: %.md
11-
node src/render_html.js $< > $@
12-
node src/build_code.js $<
11+
node src/render_html.mjs $< > $@
12+
node src/build_code.mjs $<
1313

14-
html/code/chapter_info.js: $(foreach CHAP,$(CHAPTERS),$(CHAP).md) code/solutions/* src/chapter_info.js
15-
node src/chapter_info.js > html/code/chapter_info.js
14+
html/code/chapter_info.js: $(foreach CHAP,$(CHAPTERS),$(CHAP).md) code/solutions/* src/chapter_info.mjs
15+
node src/chapter_info.mjs > html/code/chapter_info.js
1616

1717
html/ejs.js: node_modules/codemirror/dist/index.js \
1818
node_modules/@codemirror/view/dist/index.js \
@@ -34,8 +34,8 @@ code/solutions/20_3_a_public_space_on_the_web.zip: $(wildcard code/solutions/20_
3434
cd code/solutions; zip 20_3_a_public_space_on_the_web.zip 20_3_a_public_space_on_the_web/*
3535

3636
test: html
37-
@for F in $(CHAPTERS); do echo Testing $$F:; node src/run_tests.js $$F.md; done
38-
@node src/check_links.js
37+
@for F in $(CHAPTERS); do echo Testing $$F:; node src/run_tests.mjs $$F.md; done
38+
@node src/check_links.mjs
3939
@echo Done.
4040

4141
tex: $(foreach CHAP,$(CHAPTERS),pdf/$(CHAP).tex) pdf/hints.tex $(patsubst img/%.svg,img/generated/%.pdf,$(SVGS))
@@ -51,8 +51,8 @@ book_mobile.pdf: pdf/book_mobile.tex tex
5151
cd pdf && sh build.sh book_mobile > /dev/null
5252
mv pdf/book_mobile.pdf .
5353

54-
pdf/hints.tex: $(foreach CHAP,$(CHAPTERS),$(CHAP).md) src/extract_hints.js
55-
node src/extract_hints.js | node src/render_latex.js - > $@
54+
pdf/hints.tex: $(foreach CHAP,$(CHAPTERS),$(CHAP).md) src/extract_hints.mjs
55+
node src/extract_hints.mjs | node src/render_latex.mjs - > $@
5656

5757
img/generated/%.pdf: img/%.svg
5858
inkscape --export-pdf=$@ $<
@@ -61,21 +61,21 @@ pdf/%.tex: %.md
6161
node src/render_latex.js $< > $@
6262

6363
book.epub: epub/titlepage.xhtml epub/toc.xhtml epub/hints.xhtml $(foreach CHAP,$(CHAPTERS),epub/$(CHAP).xhtml) \
64-
epub/content.opf.src epub/style.css src/add_images_to_epub.js
64+
epub/content.opf.src epub/style.css src/add_images_to_epub.mjs
6565
rm -f $@
6666
grep '<img' epub/*.xhtml | sed -e 's/.*src="\([^"]*\)".*/\1/' | xargs -I{} rsync -R "{}" epub
67-
node src/add_images_to_epub.js
67+
node src/add_images_to_epub.mjs
6868
cd epub; zip -X ../$@ mimetype
6969
cd epub; zip -X ../$@ -r * -x mimetype -x *.src
7070

7171
epub/toc.xhtml: epub/toc.xhtml.src $(foreach CHAP,$(CHAPTERS),epub/$(CHAP).xhtml) epub/hints.xhtml
72-
node src/generate_epub_toc.js $^ > $@
72+
node src/generate_epub_toc.mjs $^ > $@
7373

7474
epub/%.xhtml: %.md src/render_html.js
7575
node src/render_html.js --epub $< > $@
7676

77-
epub/hints.xhtml: $(foreach CHAP,$(CHAPTERS),$(CHAP).md) src/extract_hints.js src/render_html.js
78-
node src/extract_hints.js | node src/render_html.js --epub - > $@
77+
epub/hints.xhtml: $(foreach CHAP,$(CHAPTERS),$(CHAP).md) src/extract_hints.mjs src/render_html.js
78+
node src/extract_hints.mjs | node src/render_html.mjs --epub - > $@
7979

8080
epubcheck: book.epub
8181
epubcheck book.epub 2>&1 | grep -v 'img/.*\.svg'

html/css/ejs.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ blockquote footer:before {
316316
.tok-atom, .sandbox-output-bool {color: #106}
317317
.tok-number, .sandbox-output-number {color: #042}
318318
.tok-definition {color: #009}
319-
.tok-variableName2, .tok-propertyName {color: #027}
319+
.tok-variableName2 {color: #027}
320320
.tok-type {color: #072}
321321
.tok-comment {color: #740}
322322
.tok-string, .sandbox-output-string {color: #700}

src/add_images_to_epub.js src/add_images_to_epub.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const {readdirSync, lstatSync, readFileSync, writeFileSync} = require("fs")
2-
const path = require("path")
1+
import {readdirSync, lstatSync, readFileSync, writeFileSync} from "fs"
2+
import * as path from "path"
33

44
let images = []
55
function scanDir(dir) {

src/build_code.js src/build_code.mjs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const fs = require("fs")
2-
const PJSON = require("./pseudo_json")
3-
const varify = require("./varify")
1+
import * as fs from "fs"
2+
import * as PJSON from "./pseudo_json.mjs"
3+
import varify from "./varify.mjs"
44

55
let file = process.argv[2]
66
let input = fs.readFileSync(file, "utf8")

src/chapter_info.js src/chapter_info.mjs

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
// their starting code, and collects it into a big JSON object
33
// together with the solution code.
44

5-
const PJSON = require("./pseudo_json")
6-
let fs = require("fs");
5+
import * as PJSON from "./pseudo_json.mjs"
6+
import * as fs from "fs"
7+
import jszip from "jszip"
78

89
let output = [], failed = false;
910

@@ -193,7 +194,7 @@ function chapterZipFile(meta, chapter) {
193194
if (exists && files.every(file => fs.statSync("html/" + file).mtime < exists))
194195
return name;
195196

196-
let zip = new (require("jszip"));
197+
let zip = new jszip;
197198
for (let file of files) {
198199
zip.file(chapter.id + "/" + file, fs.readFileSync("html/" + file));
199200
}

src/check_links.js src/check_links.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
let {readdirSync, readFileSync} = require("fs")
1+
import {readdirSync, readFileSync} from "fs"
22

33
let files = Object.create(null)
44
for (let name of readdirSync(".")) {

src/client/code.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class CodeSandbox {
7373

7474
document.querySelector("#solution").addEventListener("click", () => {
7575
let context = this.editor.state.facet(contextFacet)
76-
this.setEditorState(context.solution, {type: context.type})
76+
this.setEditorState(context.solution, context)
7777
})
7878

7979
this.parseFragment() || this.selectChapter(0, "box")

src/extract_hints.js src/extract_hints.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const {readdirSync, readFileSync} = require("fs")
1+
import {readdirSync, readFileSync} from "fs"
22

33
process.stdout.write("# Exercise Hints\n\nThe hints below might help when you are stuck with one of the exercises in this book. They don't give away the entire solution, but rather try to help you find it yourself.\n\n");
44

src/generate_epub_toc.js src/generate_epub_toc.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const {readFileSync} = require("fs")
2-
const {basename} = require("path")
1+
import {readFileSync} from "fs"
2+
import {basename} from "path"
33

44
let [template, ...chapters] = process.argv.slice(2)
55

src/markdown.js src/markdown.mjs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const PJSON = require("./pseudo_json")
2-
const markdownIt = require("markdown-it")
1+
import * as PJSON from "./pseudo_json.mjs"
2+
import markdownIt from "markdown-it"
33

44
function parseData(str) {
55
let tag = /^\s*(\w+)\s*?/.exec(str), args
@@ -171,7 +171,7 @@ function plugin(md) {
171171
md.inline.ruler.before("strikethrough", "index_term", parseIndexTerm)
172172
}
173173

174-
module.exports = markdownIt({html: true})
175-
.use(plugin)
176-
.use(require("markdown-it-sup"))
177-
.use(require("markdown-it-sub"))
174+
import sup from "markdown-it-sup"
175+
import sub from "markdown-it-sub"
176+
177+
export default markdownIt({html: true}).use(plugin).use(sup).use(sub)

src/pseudo_json.js src/pseudo_json.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class Stream {
2626
}
2727
}
2828

29-
exports.parse = function(str) {
29+
export function parse(str) {
3030
let stream = new Stream(str)
3131
stream.space()
3232
let value = parseValue(stream)

src/render_html.js src/render_html.mjs

+33-16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
const PJSON = require("./pseudo_json")
2-
let fs = require("fs"), mold = new (require("mold-template"))
3-
let {transformTokens} = require("./transform")
4-
let CodeMirror = require("codemirror/addon/runmode/runmode.node.js")
5-
require("codemirror/mode/javascript/javascript.js")
6-
require("codemirror/mode/xml/xml.js")
7-
require("codemirror/mode/css/css.js")
8-
require("codemirror/mode/htmlmixed/htmlmixed.js")
1+
import * as PJSON from "./pseudo_json.mjs"
2+
import {transformTokens} from "./transform.mjs"
3+
import markdown from "./markdown.mjs"
4+
import * as fs from "fs"
5+
import {dirname} from "path"
6+
import {fileURLToPath} from "url"
7+
import moldTemplate from "mold-template"
8+
import {highlightCode, classHighlighter} from "@lezer/highlight"
9+
import {Tree} from "@lezer/common"
10+
import {html} from "@codemirror/lang-html"
11+
import {javascript} from "@codemirror/lang-javascript"
12+
import {css} from "@codemirror/lang-css"
13+
import {json} from "@codemirror/lang-json"
14+
15+
const mold = new moldTemplate
916

1017
let file, epub = false
1118
for (let arg of process.argv.slice(2)) {
@@ -16,16 +23,17 @@ for (let arg of process.argv.slice(2)) {
1623
if (!file) throw new Error("No input file")
1724
let chapter = /^\d{2}_([^\.]+)/.exec(file) || [null, "hints"]
1825

19-
let {tokens, metadata} = transformTokens(require("./markdown").parse(fs.readFileSync(file, "utf8"), {}), {
26+
let {tokens, metadata} = transformTokens(markdown.parse(fs.readFileSync(file, "utf8"), {}), {
2027
defined: epub ? ["book", "html"] : ["interactive", "html"],
2128
strip: epub ? "hints" : "",
2229
takeTitle: true,
2330
index: false
2431
})
2532

2633
let close = epub ? "/" : ""
34+
const dir = dirname(fileURLToPath(import.meta.url))
2735

28-
let chapters = fs.readdirSync(__dirname + "/..")
36+
let chapters = fs.readdirSync(dir + "/..")
2937
.filter(file => /^\d{2}_\w+\.md$/.test(file))
3038
.sort()
3139
.map(file => /^\d{2}_(\w+)\.md$/.exec(file)[1])
@@ -36,12 +44,21 @@ function escapeChar(ch) {
3644
}
3745
function escape(str) { return str.replace(/[<>&"]/g, escapeChar) }
3846

47+
// FIXME http highlighting
48+
49+
const parsers = {
50+
css: css().language.parser,
51+
html: html().language.parser,
52+
javascript: javascript().language.parser,
53+
json: json().language.parser
54+
}
55+
3956
function highlight(lang, text) {
40-
let result = ""
41-
CodeMirror.runMode(text, lang, (text, style) => {
42-
let esc = escape(text)
43-
result += style ? `<span class="${style.replace(/^|\s+/g, "$&cm-")}">${esc}</span>` : esc
44-
})
57+
let result = "", parser = parsers[lang], tree = parser ? parser.parse(text) : Tree.empty
58+
highlightCode(text, tree, classHighlighter, (code, cls) => {
59+
let esc = escape(code)
60+
result += cls ? `<span class="${cls}">${esc}</span>` : esc
61+
}, () => result += "\n")
4562
return result
4663
}
4764

@@ -184,6 +201,6 @@ if (chapter && (index = chapters.indexOf(chapter[1])) > -1) {
184201
metadata.page = {type: "hints"}
185202
}
186203

187-
let template = mold.bake("chapter", fs.readFileSync(__dirname + `/${epub ? "epub_" : ""}chapter.html`, "utf8"))
204+
let template = mold.bake("chapter", fs.readFileSync(dir + `/${epub ? "epub_" : ""}chapter.html`, "utf8"))
188205

189206
console.log(template(metadata))

0 commit comments

Comments
 (0)