Skip to content

Commit

Permalink
adopt ES modules, mocha
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Aug 21, 2021
1 parent 4290665 commit 0535d27
Show file tree
Hide file tree
Showing 16 changed files with 2,207 additions and 1,085 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
node-version: [15.x]
node-version: [14.x]

steps:
- uses: actions/checkout@v2
Expand Down
3 changes: 3 additions & 0 deletions bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import "./src/style.css";
export {version} from "./package.json";
export * from "./src/index.js";
49 changes: 26 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@
"url": "https://observablehq.com"
},
"license": "ISC",
"main": "dist/inputs.cjs.js",
"jsdelivr": "dist/inputs.umd.min.js",
"unpkg": "dist/inputs.umd.min.js",
"type": "module",
"main": "src/index.js",
"module": "src/index.js",
"jsdelivr": "dist/inputs.min.js",
"unpkg": "dist/inputs.min.js",
"exports": {
"umd": "./dist/inputs.min.js",
"default": "./src/index.js"
},
"repository": {
"type": "git",
"url": "https://github.com/observablehq/inputs.git"
Expand All @@ -22,34 +28,31 @@
"node": ">=14.5.0"
},
"scripts": {
"bundle": "rm -rf dist && rollup -c",
"test": "yarn bundle && mkdir -p test/output && tape -r esm -r module-alias/register 'test/**/*-test.js' | tap-dot && node -r esm -r module-alias/register test/input.js | tap-dot && eslint src test",
"prepublishOnly": "yarn bundle",
"test": "mkdir -p test/output && mocha -r module-alias/register 'test/**/*-test.js' && mocha -r module-alias/register test/input.js && eslint src test",
"prepublishOnly": "rm -rf dist && rollup -c",
"postpublish": "git push && git push --tags",
"dev": "snowpack dev"
},
"_moduleAliases": {
"@observablehq/inputs": "./dist/inputs.cjs.js"
"@observablehq/inputs": "./src/index.js"
},
"devDependencies": {
"@rollup/plugin-replace": "^2.3.4",
"clean-css": "^5.0.1",
"d3": "^6.5.0",
"eslint": "^7.12.1",
"esm": "^3.2.25",
"js-beautify": "^1.13.5",
"jsdom": "^16.4.0",
"jsesc": "^3.0.2",
"module-alias": "^2.2.2",
"rollup": "^2.32.1",
"rollup-plugin-terser": "^7.0.2",
"snowpack": "^3.0.11",
"tap-dot": "^2.0.0",
"tape": "^4.13.3",
"tape-await": "^0.1.2"
"@rollup/plugin-json": "4",
"@rollup/plugin-replace": "3",
"clean-css": "5",
"d3": "7",
"eslint": "7",
"js-beautify": "1",
"jsdom": "17",
"jsesc": "3",
"mocha": "9",
"module-alias": "2",
"rollup": "2",
"rollup-plugin-terser": "7",
"snowpack": "3"
},
"dependencies": {
"htl": "^0.2.3"
"htl": "0.3"
},
"publishConfig": {
"access": "public"
Expand Down
60 changes: 21 additions & 39 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import crypto from "crypto";
import fs from "fs";
import path from "path";
import replace from "@rollup/plugin-replace";
import {terser} from "rollup-plugin-terser";
import replace from "@rollup/plugin-replace";
import json from "@rollup/plugin-json";
import jsesc from "jsesc";
import CleanCSS from "clean-css";
import * as meta from "./package.json";

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

// Resolve HTL dependency.
const htl = require("htl/package.json");
const htl = JSON.parse(fs.readFileSync("./node_modules/htl/package.json", "utf-8"));
if (typeof htl.jsdelivr === "undefined") throw new Error("unable to resolve htl");
const htlPath = `htl@${htl.version}/${htl.jsdelivr}`;

// Extract copyrights from the LICENSE.
const copyrights = fs.readFileSync("./LICENSE", "utf-8")
.split(/\n/g)
.filter(line => /^copyright\s+/i.test(line))
.map(line => line.replace(/^copyright\s+/i, ""));

// Create a content-hashed namespace for our styles.
const stylePath = path.resolve("./src/style.css");
Expand All @@ -26,6 +35,7 @@ const css = {
transform(input, id) {
if (id !== stylePath) return;
return {
moduleSideEffects: true,
code: `if (typeof document !== 'undefined' && !document.querySelector('.${styleNs}')) {
const style = document.createElement('style');
style.className = '${styleNs}';
Expand All @@ -38,69 +48,41 @@ document.head.appendChild(style);
};

const config = {
input: "src/index.js",
input: "bundle.js",
external: ["htl"],
output: {
indent: false,
banner: `// ${meta.name} v${meta.version} Copyright ${(new Date).getFullYear()} ${meta.author.name}`,
banner: `// ${meta.name} v${meta.version} Copyright ${copyrights.join(", ")}`,
name: "observablehq",
format: "umd",
extend: true,
globals: {"htl": "htl"}
globals: {"htl": "htl"},
paths: {"htl": htlPath}
},
plugins: [
css,
json(),
replace({__ns__: styleNs, preventAssignment: true})
]
};

const minify = terser({
output: {
preamble: config.output.banner
}
});

export default [
{
...config,
output: {
...config.output,
format: "es",
file: `dist/${filename}.js`,
paths: {"htl": `https://cdn.jsdelivr.net/npm/htl@${htl.version}/${htl.module}`}
},
plugins: [
...config.plugins,
minify
]
},
{
...config,
output: {
...config.output,
format: "cjs",
file: `dist/${filename}.cjs.js`
}
},
{
...config,
output: {
...config.output,
format: "umd",
file: `dist/${filename}.umd.js`,
paths: {"htl": `htl@${htl.version}/${htl.unpkg}`}
file: `dist/${filename}.umd.js`
}
},
{
...config,
output: {
...config.output,
format: "umd",
file: `dist/${filename}.umd.min.js`,
paths: {"htl": `htl@${htl.version}/${htl.unpkg}`}
file: `dist/${filename}.umd.min.js`
},
plugins: [
...config.plugins,
minify
terser({output: {preamble: config.output.banner}})
]
}
];
2 changes: 0 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import "./style.css";

export {button} from "./button.js";
export {checkbox, radio, toggle} from "./checkbox.js";
export {range} from "./range.js";
Expand Down
5 changes: 5 additions & 0 deletions test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"env": {
"mocha": true
}
}
7 changes: 4 additions & 3 deletions test/bind-test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import assert from "assert";
import * as Inputs from "@observablehq/inputs";
import tape from "./jsdom.js";
import it from "./jsdom.js";

tape("Inputs.bind(button, button) dispatches click events", test => {
it("Inputs.bind(button, button) dispatches click events", () => {
const b1 = document.createElement("button");
const b2 = Inputs.bind(document.createElement("button"), b1, new Promise(() => {}));
let clicked = false;
b1.addEventListener("click", () => void (clicked = true), {once: true});
b2.dispatchEvent(new Event("click"));
test.equal(clicked, true);
assert.strictEqual(clicked, true);
});
27 changes: 14 additions & 13 deletions test/checkbox-test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import * as Inputs from "@observablehq/inputs";
import tape from "./jsdom.js";
import assert from "assert";
import it from "./jsdom.js";

tape("Inputs.checkbox([]) handles empty options", test => {
it("Inputs.checkbox([]) handles empty options", () => {
const r = Inputs.checkbox([]);
test.deepEqual(r.value, []);
test.strictEqual(r.value, r.value);
assert.deepStrictEqual(r.value, []);
assert.strictEqual(r.value, r.value);
r.value = ["red"];
test.deepEqual(r.value, []);
test.strictEqual(r.value, r.value);
assert.deepStrictEqual(r.value, []);
assert.strictEqual(r.value, r.value);
});

tape("Inputs.checkbox([value]) handles singular option", test => {
it("Inputs.checkbox([value]) handles singular option", () => {
const r = Inputs.checkbox(["red"]);
test.deepEqual(r.value, []);
test.strictEqual(r.value, r.value);
assert.deepStrictEqual(r.value, []);
assert.strictEqual(r.value, r.value);
r.value = ["blue"];
test.deepEqual(r.value, []);
assert.deepStrictEqual(r.value, []);
r.value = ["red"];
test.deepEqual(r.value, ["red"]);
test.strictEqual(r.value, r.value);
assert.deepStrictEqual(r.value, ["red"]);
assert.strictEqual(r.value, r.value);
r.value = ["blue"];
test.deepEqual(r.value, []);
assert.deepStrictEqual(r.value, []);
});
18 changes: 9 additions & 9 deletions test/input.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import assert from "assert";
import {promises as fs} from "fs";
import * as path from "path";
import {html as beautify} from "js-beautify";
import tape from "tape-await";
import beautify from "js-beautify";
import it from "./jsdom.js";
import * as inputs from "./inputs/index.js";
import {withJsdom} from "./jsdom.js";

(async () => {
for (const [name, input] of Object.entries(inputs)) {
tape(`input ${name}`, async test => {
it(`input ${name}`, async () => {
const reid = new Map();
const element = await withJsdom(input);
const actual = beautify(
const element = await input();
const actual = beautify.html(
element.outerHTML
.replace(/(?<=="[^"]*)\boi-[a-f0-9]{6}-([0-9]+)\b/g, (_, id) => `__ns__-${reid.has(id) ? reid.get(id) : (reid.set(id, id = reid.size + 1), id)}`)
.replace(/(?<=="[^"]*)\boi-[a-f0-9]{6}\b/g, `__ns__`),
.replace(/(?<=="[^"]*)\b__ns__-([0-9]+)\b/g, (_, id) => `__ns__-${reid.has(id) ? reid.get(id) : (reid.set(id, id = reid.size + 1), id)}`)
.replace(/(?<=="[^"]*)\b__ns__\b/g, `__ns__`),
{indent_size: 2}
);
const outfile = path.resolve("./test/output", path.basename(name, ".js") + ".html");
Expand All @@ -29,7 +29,7 @@ import {withJsdom} from "./jsdom.js";
throw error;
}
}
test.ok(actual === expected, `${name} must match snapshot`);
assert(actual === expected, `${name} must match snapshot`);
});
}
})();
60 changes: 28 additions & 32 deletions test/jsdom.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
import {promises as fs} from "fs";
import * as path from "path";
import {JSDOM} from "jsdom";
import tape from "tape-await";

export default Object.assign(wrap(tape), {
skip: wrap(tape.skip),
only: wrap(tape.only)
});

function wrap(tape) {
return function(description, run) {
return tape(description, test => {
return withJsdom(() => run(test));
});
};
export default function jsdomit(description, run) {
return it(description, withJsdom(run));
}

export async function withJsdom(run) {
const jsdom = new JSDOM("");
global.window = jsdom.window;
global.document = jsdom.window.document;
global.Event = jsdom.window.Event;
global.Node = jsdom.window.Node;
global.NodeList = jsdom.window.NodeList;
global.HTMLCollection = jsdom.window.HTMLCollection;
global.fetch = async (href) => new Response(path.resolve("./test", href));
try {
return await run();
} finally {
delete global.window;
delete global.document;
delete global.Event;
delete global.Node;
delete global.NodeList;
delete global.HTMLCollection;
delete global.fetch;
}
jsdomit.skip = (description, run) => {
return it.skip(description, withJsdom(run));
};

function withJsdom(run) {
return async () => {
const jsdom = new JSDOM("");
global.window = jsdom.window;
global.document = jsdom.window.document;
global.Event = jsdom.window.Event;
global.Node = jsdom.window.Node;
global.NodeList = jsdom.window.NodeList;
global.HTMLCollection = jsdom.window.HTMLCollection;
global.fetch = async (href) => new Response(path.resolve("./test", href));
try {
return await run();
} finally {
delete global.window;
delete global.document;
delete global.Event;
delete global.Node;
delete global.NodeList;
delete global.HTMLCollection;
delete global.fetch;
}
};
}

class Response {
Expand Down

0 comments on commit 0535d27

Please sign in to comment.