Skip to content

Commit 977e011

Browse files
authored
copy docs from README to code (observablehq#1096)
* copy jsdocs from README to code * only tsc once
1 parent 6a110e8 commit 977e011

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+645
-2723
lines changed

.github/workflows/node.js.yml

-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,3 @@ jobs:
2727
echo ::add-matcher::.github/eslint.json
2828
yarn run eslint . --format=compact
2929
- run: yarn test
30-
- run: yarn readme:check

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
2+
build/
23
dist/
34
types/
45
node_modules/

README.md

+466-975
Large diffs are not rendered by default.

bundle.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export {version} from "./package.json";
2-
export * from "./src/index.js";
2+
export * from "./dist/index.js";

package.json

+3-7
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,11 @@
2828
"types/**/*.d.ts"
2929
],
3030
"scripts": {
31-
"test": "yarn test:mocha && yarn test:typecheck && yarn test:lint && yarn readme:check",
31+
"test": "yarn test:mocha && yarn test:typecheck && yarn test:lint",
3232
"test:mocha": "mkdir -p test/output && mocha --conditions=mocha 'test/**/*-test.*' 'test/plot.js'",
3333
"test:lint": "eslint src test",
3434
"test:typecheck": "tsc --noEmit",
35-
"readme:check": "tsx scripts/jsdoc-to-readme.ts --check",
36-
"readme:update": "tsx scripts/jsdoc-to-readme.ts",
37-
"prepublishOnly": "rm -rf dist && rollup -c && tsc",
35+
"prepublishOnly": "rm -rf build dist && tsc && tsx scripts/readme-to-jsdoc.ts && rollup -c",
3836
"postpublish": "git push && git push --tags",
3937
"dev": "vite"
4038
},
@@ -47,7 +45,6 @@
4745
"@rollup/plugin-commonjs": "22",
4846
"@rollup/plugin-json": "4",
4947
"@rollup/plugin-node-resolve": "13",
50-
"@rollup/plugin-typescript": "^8.3.2",
5148
"@types/d3": "^7.4.0",
5249
"@types/expect": "^24.3.0",
5350
"@types/mocha": "^9.1.1",
@@ -62,13 +59,12 @@
6259
"htl": "0.3",
6360
"js-beautify": "1",
6461
"jsdom": "19",
62+
"mkdirp": "^1.0.4",
6563
"mocha": "10",
6664
"module-alias": "2",
6765
"prettier": "^2.7.1",
6866
"rollup": "2",
6967
"rollup-plugin-terser": "7",
70-
"ts-morph": "^15.1.0",
71-
"tslib": "^2.4.0",
7268
"tsx": "^3.8.0",
7369
"typescript": "^4.6.4",
7470
"typescript-module-alias": "^1.0.2",

rollup.config.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import commonjs from "@rollup/plugin-commonjs";
44
import json from "@rollup/plugin-json";
55
import node from "@rollup/plugin-node-resolve";
66
import * as meta from "./package.json";
7-
import typescript from "@rollup/plugin-typescript";
87

98
const filename = meta.name.split("/").pop();
109

@@ -27,7 +26,7 @@ const config = {
2726
indent: false,
2827
banner: `// ${meta.name} v${meta.version} Copyright ${copyrights.join(", ")}`
2928
},
30-
plugins: [typescript(), commonjs(), json(), node()]
29+
plugins: [commonjs(), json(), node()]
3130
};
3231

3332
export default [

scripts/jsdoc-to-readme.ts

-119
This file was deleted.

scripts/readme-to-jsdoc.ts

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import {readFileSync, writeFileSync} from "fs";
2+
import {dirname} from "path";
3+
import glob from "glob";
4+
import mkdirp from "mkdirp";
5+
6+
// Extract the documentation from the README.
7+
const readme = readFileSync("./README.md", "utf-8");
8+
const docmap = new Map<string, string[]>();
9+
let doc: {name: string; lines: string[]} | null = null;
10+
for (const [i, line] of readme.split("\n").entries()) {
11+
if (/<!--\s*jsdoc/.test(line)) {
12+
let match: RegExpExecArray | null;
13+
if ((match = /^<!--\s+jsdoc\s+(\w+)\s+-->$/.exec(line))) {
14+
const [, name] = match;
15+
if (doc) {
16+
throw new Error(`nested jsdoc directive on line ${i}: ${line}`);
17+
}
18+
if (docmap.has(name)) {
19+
throw new Error(`duplicate jsdoc directive on line ${i}: ${line}`);
20+
}
21+
doc = {name, lines: []};
22+
} else if ((match = /^<!--\s+jsdocEnd\s+(\w+)\s+-->$/.exec(line))) {
23+
const [, name] = match;
24+
if (!doc) {
25+
throw new Error(`orphaned jsdocEnd directive on line ${i}: ${line}`);
26+
}
27+
if (doc.name !== name) {
28+
throw new Error(`mismatched jsdocEnd ${doc.name} directive on line ${i}: ${line}`);
29+
}
30+
docmap.set(doc.name, doc.lines);
31+
doc = null;
32+
} else {
33+
throw new Error(`malformed jsdoc directive on line ${i}: ${line}`);
34+
}
35+
} else if (doc) {
36+
doc.lines.push(line);
37+
}
38+
}
39+
40+
// Make relative and anchor links absolute.
41+
for (const lines of docmap.values()) {
42+
for (let i = 0, n = lines.length; i < n; ++i) {
43+
lines[i] = lines[i]
44+
.replace(/\]\(#([^)]+)\)/g, "](./README.md#$1)")
45+
.replace(/\]\(\.\/([^)]+)\)/g, "](https://github.com/observablehq/plot/blob/main/$1)");
46+
}
47+
}
48+
49+
// Copy files from build/ to dist/, replacing /** @jsdoc name */ directives.
50+
const unused = new Set(docmap.keys());
51+
for (const file of glob.sync("build/**/*.js")) {
52+
process.stdout.write(`\x1b[2m${file}\x1b[0m`);
53+
const lines = readFileSync(file, "utf-8").split("\n");
54+
let count = 0;
55+
for (let i = 0, n = lines.length; i < n; ++i) {
56+
let match: RegExpExecArray | null;
57+
if ((match = /^\/\*\*\s+@jsdoc\s+(\w+)\s+\*\/$/.exec(lines[i]))) {
58+
const [, name] = match;
59+
const docs = docmap.get(name);
60+
if (!docs) throw new Error(`missing @jsdoc definition: ${name}`);
61+
if (!unused.has(name)) throw new Error(`duplicate @jsdoc reference: ${name}`);
62+
unused.delete(name);
63+
++count;
64+
lines[i] = docs
65+
.map((line, i, lines) => (i === 0 ? `/** ${line}` : i === lines.length - 1 ? ` * ${line}\n */` : ` * ${line}`))
66+
.join("\n");
67+
}
68+
}
69+
const ofile = file.replace(/^build\//, "dist/");
70+
process.stdout.write(` → \x1b[36m${ofile}\x1b[0m${count ? ` (${count} jsdoc${count === 1 ? "" : "s"})` : ""}\n`);
71+
const odir = dirname(ofile);
72+
mkdirp.sync(odir);
73+
writeFileSync(ofile, lines.join("\n"), "utf-8");
74+
}
75+
76+
for (const name of unused) {
77+
console.warn(`\x1b[33m[warning] unused @jsdoc directive:\x1b[0m ${name}`);
78+
}

src/format.ts

+3-33
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,7 @@ export function formatNumber(locale = "en-US"): (value: any) => string | undefin
2020
return (i: any) => (i != null && !isNaN(i) ? format.format(i) : undefined);
2121
}
2222

23-
/**
24-
* ```js
25-
* Plot.formatMonth("es-MX", "long")(0) // "enero"
26-
* ```
27-
*
28-
* Returns a function that formats a given month number (from 0 = January to 11
29-
* = December) according to the specified *locale* and *format*. The *locale* is
30-
* a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to
31-
* U.S. English. The *format* is a [month
32-
* format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*,
33-
* *numeric*, *narrow*, *short*, *long*; if not specified, it defaults to
34-
* *short*.
35-
*/
23+
/** @jsdoc formatMonth */
3624
export function formatMonth(
3725
locale = "en-US",
3826
format: "numeric" | "2-digit" | "long" | "short" | "narrow" | undefined = "short"
@@ -42,32 +30,14 @@ export function formatMonth(
4230
i != null && !isNaN((i = +new Date(Date.UTC(2000, +i)))) ? fmt.format(i) : undefined;
4331
}
4432

45-
/**
46-
* ```js
47-
* Plot.formatWeekday("es-MX", "long")(0) // "domingo"
48-
* ```
49-
*
50-
* Returns a function that formats a given week day number (from 0 = Sunday to 6
51-
* = Saturday) according to the specified *locale* and *format*. The *locale* is
52-
* a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to
53-
* U.S. English. The *format* is a [weekday
54-
* format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*,
55-
* *short*, or *long*; if not specified, it defaults to *short*.
56-
*/
33+
/** @jsdoc formatWeekday */
5734
export function formatWeekday(locale = "en-US", format: "long" | "short" | "narrow" | undefined = "short") {
5835
const fmt = weekdayFormat(locale, format);
5936
return (i: Date | number | null | undefined) =>
6037
i != null && !isNaN((i = +new Date(Date.UTC(2001, 0, +i)))) ? fmt.format(i) : undefined;
6138
}
6239

63-
/**
64-
* ```js
65-
* Plot.formatIsoDate(new Date("2020-01-01T00:00.000Z")) // "2020-01-01"
66-
* ```
67-
*
68-
* Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the
69-
* given *date* is not valid, returns `"Invalid Date"`.
70-
*/
40+
/** @jsdoc formatIsoDate */
7141
export function formatIsoDate(date: Date): string {
7242
return isoFormat(date, "Invalid Date");
7343
}

src/legends.js

+1-24
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,7 @@ const legendRegistry = new Map([
1111
["opacity", legendOpacity]
1212
]);
1313

14-
/**
15-
* Returns a standalone legend for the scale defined by the given *options*
16-
* object. The *options* object must define at least one scale; see [Scale
17-
* options](https://github.com/observablehq/plot/blob/main/README.md#scale-options)
18-
* for how to define a scale. For example, here is a ramp legend of a linear
19-
* color scale with the default domain of [0, 1] and default scheme *turbo*:
20-
*
21-
* ```js
22-
* Plot.legend({color: {type: "linear"}})
23-
* ```
24-
*
25-
* The *options* object may also include any additional legend options described
26-
* in the previous section. For example, to make the above legend slightly
27-
* wider:
28-
*
29-
* ```js
30-
* Plot.legend({
31-
* width: 320,
32-
* color: {
33-
* type: "linear"
34-
* }
35-
* })
36-
* ```
37-
*/
14+
/** @jsdoc legend */
3815
export function legend(options = {}) {
3916
for (const [key, value] of legendRegistry) {
4017
const scale = options[key];

0 commit comments

Comments
 (0)