chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
# Perspective CLI
|
||||
|
||||
```bash
|
||||
Usage: perspective [options] [command]
|
||||
|
||||
A convenient command-line client for Perspective.js. Can convert between Perspective supported format, or host a local web server.
|
||||
|
||||
Options:
|
||||
-V, --version output the version number
|
||||
-h, --help output usage information
|
||||
|
||||
Commands:
|
||||
convert [options] [filename] Convert a file into a new format. Reads from STDIN if no filename is provided
|
||||
Options:
|
||||
-f, --format <format> Which output format to use: arrow, csv, columns, json.
|
||||
-o, --output <filename> Filename to write to. If not supplied, writes to STDOUT
|
||||
-h, --help output usage information
|
||||
|
||||
host [options] [filename] Host a file on a local Websocket/HTTP server using a server-side Perspective. Reads from STDIN if no filename is provided
|
||||
Options:
|
||||
-p, --port <port> Which port to bind to (default: 8080)
|
||||
-o, --open Open a browser automagically
|
||||
-h, --help output usage information
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@perspective-dev/cli",
|
||||
"version": "4.5.2",
|
||||
"description": "Perspective.js CLI",
|
||||
"main": "src/js/index.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"type": "module",
|
||||
"files": [
|
||||
"src/**/*",
|
||||
"perspective"
|
||||
],
|
||||
"types": "index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/perspective-dev/perspective"
|
||||
},
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"perspective": "perspective"
|
||||
},
|
||||
"dependencies": {
|
||||
"@perspective-dev/client": "workspace:",
|
||||
"@perspective-dev/viewer": "workspace:",
|
||||
"@perspective-dev/viewer-charts": "workspace:",
|
||||
"@perspective-dev/viewer-datagrid": "workspace:",
|
||||
"@perspective-dev/workspace": "workspace:",
|
||||
"commander": "catalog:",
|
||||
"puppeteer": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@perspective-dev/test": "workspace:"
|
||||
}
|
||||
}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node --max-old-space-size=8192
|
||||
|
||||
require("@perspective-dev/cli");
|
||||
@@ -0,0 +1,55 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
|
||||
|
||||
<title>Perspective CLI</title>
|
||||
|
||||
<link rel="stylesheet" crossorigin="anonymous" href="/node_modules/@perspective-dev/viewer/dist/css/pro.css" />
|
||||
<link rel="stylesheet" crossorigin="anonymous" href="/node_modules/@perspective-dev/workspace/dist/css/pro.css" />
|
||||
|
||||
<script type="module" src="/node_modules/@perspective-dev/workspace/dist/cdn/perspective-workspace.js"></script>
|
||||
<script type="module" src="/node_modules/@perspective-dev/viewer-datagrid/dist/cdn/perspective-viewer-datagrid.js"></script>
|
||||
<script type="module" src="/node_modules/@perspective-dev/viewer-charts/dist/cdn/perspective-viewer-charts.js"></script>
|
||||
|
||||
<style>
|
||||
perspective-workspace {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
html {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
touch-action: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<perspective-workspace id="view1">
|
||||
<perspective-viewer table="data"></perspective-viewer>
|
||||
</perspective-workspace>
|
||||
<script type="module">
|
||||
import "/node_modules/@perspective-dev/viewer/dist/cdn/perspective-viewer.js";
|
||||
import perspective from "/node_modules/@perspective-dev/client/dist/cdn/perspective.js";
|
||||
|
||||
// const WORKER = worker();
|
||||
var elem = document.getElementById("view1");
|
||||
var worker = await perspective.websocket(location.origin.replace("http", "ws"));
|
||||
elem.tables.set("data", worker.open_table("data_source_one"));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+178
@@ -0,0 +1,178 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
||||
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
||||
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
||||
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
import { WebSocketServer, table } from "@perspective-dev/client";
|
||||
import { read_stdin, open_browser } from "./utils.js";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { program } from "commander";
|
||||
import puppeteer from "puppeteer";
|
||||
import { createRequire } from "node:module";
|
||||
import * as url from "node:url";
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL(".", import.meta.url)).slice(0, -1);
|
||||
|
||||
const _require = createRequire(import.meta.url);
|
||||
|
||||
/**
|
||||
* Convert data from one format to another.
|
||||
*
|
||||
* @param {*} filename
|
||||
* @param {*} options
|
||||
*/
|
||||
async function convert(filename, options) {
|
||||
let file;
|
||||
if (filename) {
|
||||
file = fs.readFileSync(filename).toString();
|
||||
} else {
|
||||
file = await read_stdin();
|
||||
}
|
||||
try {
|
||||
file = JSON.parse(file);
|
||||
} catch {}
|
||||
let tbl = await table(file);
|
||||
let view = await tbl.view();
|
||||
let out;
|
||||
options.format = options.format || "arrow";
|
||||
if (options.format === "csv") {
|
||||
out = await view.to_csv();
|
||||
} else if (options.format === "columns") {
|
||||
out = JSON.stringify(await view.to_columns());
|
||||
} else if (options.format === "json") {
|
||||
out = JSON.stringify(await view.to_json());
|
||||
} else {
|
||||
out = await view.to_arrow();
|
||||
}
|
||||
if (options.format === "arrow") {
|
||||
if (options.output) {
|
||||
fs.writeFileSync(options.output, Buffer.from(out), "binary");
|
||||
} else {
|
||||
console.log(Buffer.from(out).toString());
|
||||
}
|
||||
} else {
|
||||
if (options.output) {
|
||||
fs.writeFileSync(options.output, out);
|
||||
} else {
|
||||
console.log(out);
|
||||
}
|
||||
}
|
||||
view.delete();
|
||||
tbl.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Host a Perspective on a web server.
|
||||
*
|
||||
* @param {*} filename
|
||||
* @param {*} options
|
||||
*/
|
||||
export async function host(filename, options) {
|
||||
let files = [process.cwd(), path.join(__dirname, "..", "html")];
|
||||
if (options.assets) {
|
||||
files = [options.assets, ...files];
|
||||
}
|
||||
const server = new WebSocketServer({
|
||||
assets: files,
|
||||
port: options.port,
|
||||
});
|
||||
|
||||
let file;
|
||||
if (filename) {
|
||||
file = await table(fs.readFileSync(filename).toString(), {
|
||||
name: "data_source_one",
|
||||
});
|
||||
} else {
|
||||
file = await read_stdin();
|
||||
}
|
||||
if (options.open) {
|
||||
const browser = await puppeteer.launch({
|
||||
headless: false,
|
||||
defaultViewport: null,
|
||||
args: [
|
||||
"--incognito",
|
||||
"--noerrdialogs",
|
||||
"--disable-translate",
|
||||
"--no-first-run",
|
||||
"--fast",
|
||||
"--fast-start",
|
||||
"--disable-infobars",
|
||||
"--window-size=1200,800",
|
||||
"--disable-features=TranslateUI",
|
||||
"--disk-cache-dir=/dev/null",
|
||||
`--app=http://localhost:${options.port}/`,
|
||||
],
|
||||
});
|
||||
|
||||
const pages = await browser.pages();
|
||||
|
||||
const page = pages[0];
|
||||
page.on("close", () => {
|
||||
browser.close();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
program
|
||||
.version(
|
||||
JSON.parse(
|
||||
fs
|
||||
.readFileSync(path.join(__dirname, "..", "..", "package.json"))
|
||||
.toString(),
|
||||
).version,
|
||||
)
|
||||
.description(
|
||||
"A convenient command-line client for Perspective.js. Can convert between Perspective supported format, or host a local web server.",
|
||||
)
|
||||
.action(() => program.help());
|
||||
|
||||
program
|
||||
.command("convert [filename]")
|
||||
.description(
|
||||
"Convert a file into a new format. Reads from STDIN if no filename is provided.",
|
||||
)
|
||||
.option(
|
||||
"-f, --format <format>",
|
||||
"Which output format to use: arrow, csv, columns, json.",
|
||||
/^(arrow|json|columns|csv)$/i,
|
||||
"arrow",
|
||||
)
|
||||
.option(
|
||||
"-o, --output <filename>",
|
||||
"Filename to write to. If not supplied, writes to STDOUT.",
|
||||
)
|
||||
.action(convert);
|
||||
|
||||
program
|
||||
.command("host [filename]")
|
||||
.description(
|
||||
"Host a file on a local Websocket/HTTP server using a server-side Perspective. Reads from STDIN if no filename is provided",
|
||||
)
|
||||
.option(
|
||||
"-p, --port <port>",
|
||||
"Which port to bind to.",
|
||||
(x) => parseInt(x),
|
||||
8080,
|
||||
)
|
||||
.option("-a, --assets <path>", "Host from a working directory")
|
||||
.option("-o, --open", "Open a browser automagically.")
|
||||
.action(host);
|
||||
|
||||
if (_require.main.path.endsWith("perspective-cli")) {
|
||||
if (!process.argv.slice(2).length) {
|
||||
program.help();
|
||||
} else {
|
||||
program.parse(process.argv);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
||||
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
||||
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
||||
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
import { exec } from "child_process";
|
||||
import { table } from "@perspective-dev/client";
|
||||
|
||||
const OPEN = (port) => `
|
||||
if which xdg-open > /dev/null
|
||||
then
|
||||
xdg-open http://localhost:${port}/
|
||||
elif which gnome-open > /dev/null
|
||||
then
|
||||
gnome-open http://localhost:${port}/
|
||||
elif which open > /dev/null
|
||||
then
|
||||
open http://localhost:${port}/
|
||||
fi`;
|
||||
|
||||
export function infer_table(buffer) {
|
||||
if (buffer.slice(0, 6).toString() === "ARROW1") {
|
||||
console.log("Loaded Arrow");
|
||||
return table(buffer.buffer);
|
||||
} else {
|
||||
let text = buffer.toString();
|
||||
try {
|
||||
let json = JSON.parse(text);
|
||||
console.log("Loaded JSON");
|
||||
return table(json);
|
||||
} catch (e) {
|
||||
console.log("Loaded CSV");
|
||||
return table(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const read_stdin = function read_stdin() {
|
||||
const ret = [];
|
||||
let len = 0;
|
||||
return new Promise((resolve) => {
|
||||
process.stdin
|
||||
.on("readable", function () {
|
||||
let chunk;
|
||||
while ((chunk = process.stdin.read())) {
|
||||
ret.push(Buffer.from(chunk));
|
||||
len += chunk.length;
|
||||
}
|
||||
})
|
||||
.on("end", function () {
|
||||
const buffer = Buffer.concat(ret, len);
|
||||
resolve(infer_table(buffer));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const execute = function execute(command, callback) {
|
||||
exec(command, function (error, stdout) {
|
||||
if (callback) {
|
||||
callback(stdout);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const open_browser = function open_browser(port) {
|
||||
module.exports.execute(OPEN(port), console.log);
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
x,y
|
||||
1,2
|
||||
3,4
|
||||
5,6
|
||||
|
@@ -0,0 +1,51 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
||||
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
||||
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
||||
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
import { test, expect } from "@perspective-dev/test";
|
||||
import * as path from "node:path";
|
||||
import { host } from "../../src/js/index.js";
|
||||
import * as url from "url";
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL(".", import.meta.url)).slice(0, -1);
|
||||
|
||||
test.describe("CLI", function () {
|
||||
let server, port;
|
||||
test.beforeAll(async () => {
|
||||
const options = { port: 0 };
|
||||
server = await host(path.join(__dirname, "../csv/test.csv"), options);
|
||||
port = server._server.address().port;
|
||||
});
|
||||
|
||||
test.afterAll(async (options) => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
test("Tests something", async ({ page }) => {
|
||||
await page.goto(`http://localhost:${port}/`);
|
||||
await page.waitForSelector(
|
||||
"perspective-viewer perspective-viewer-datagrid",
|
||||
);
|
||||
|
||||
const json = await page.evaluate(async function () {
|
||||
const viewer = document.querySelector("perspective-viewer");
|
||||
await viewer.flush();
|
||||
const view = await viewer.getView();
|
||||
return await view.to_json();
|
||||
});
|
||||
|
||||
expect(json).toEqual([
|
||||
{ x: 1, y: 2 },
|
||||
{ x: 3, y: 4 },
|
||||
{ x: 5, y: 6 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user