From c8b95e1d2f4f8682d252722c4086e7d08890a4b3 Mon Sep 17 00:00:00 2001 From: Alex Stephen <1325798+rambleraptor@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:02:22 -0700 Subject: [PATCH] json path ish support (#47) --- package-lock.json | 147 +++++++++++++++++++++++++++++++++ package.json | 1 + src/components/Sample.astro | 90 +------------------- src/components/utils/sample.ts | 128 ++++++++++++++++++++++++++++ 4 files changed, 278 insertions(+), 88 deletions(-) create mode 100644 src/components/utils/sample.ts diff --git a/package-lock.json b/package-lock.json index 6c5d260..8a5b3ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@astrojs/tailwind": "^5.1.0", "@hpcc-js/wasm": "^2.22.1", "astro": "^4.10.2", + "jsonpath": "^1.1.1", "marked": "^14.1.2", "rehype-graphviz": "^0.3.0", "sharp": "^0.32.5", @@ -3133,6 +3134,11 @@ "node": ">=4.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, "node_modules/defu": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", @@ -3321,6 +3327,36 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -3333,6 +3369,14 @@ "node": ">=4" } }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-util-attach-comments": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", @@ -3404,6 +3448,14 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -3491,6 +3543,11 @@ "node": ">=8.6.0" } }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, "node_modules/fast-uri": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", @@ -4545,6 +4602,28 @@ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" }, + "node_modules/jsonpath": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", + "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", + "dependencies": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + } + }, + "node_modules/jsonpath/node_modules/esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -4561,6 +4640,18 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lilconfig": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", @@ -6029,6 +6120,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/ora": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ora/-/ora-8.0.1.tgz", @@ -6516,6 +6623,14 @@ "node": ">=18.12" } }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", @@ -7310,6 +7425,14 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/static-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "dependencies": { + "escodegen": "^1.8.1" + } + }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -8218,6 +8341,17 @@ "node": "*" } }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -8254,6 +8388,11 @@ "semver": "^7.3.8" } }, + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -8852,6 +8991,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", diff --git a/package.json b/package.json index 779210c..bba2eb9 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@astrojs/tailwind": "^5.1.0", "@hpcc-js/wasm": "^2.22.1", "astro": "^4.10.2", + "jsonpath": "^1.1.1", "marked": "^14.1.2", "rehype-graphviz": "^0.3.0", "sharp": "^0.32.5", diff --git a/src/components/Sample.astro b/src/components/Sample.astro index dd0b8cb..c5bf6ae 100644 --- a/src/components/Sample.astro +++ b/src/components/Sample.astro @@ -1,99 +1,13 @@ --- import { Code } from "@astrojs/starlight/components"; -import { redirectToDefaultLocale } from "astro:i18n"; import fs from "node:fs/promises"; +import sampleCode from './utils/sample'; const { path, type, token1 = "", token2 = "" } = Astro.props; let code = await fs.readFile(path, "utf-8"); -let partialCode = code; -let symbols = []; -if (token1 != "") { - symbols.push(token1); -} -if (token2 != "") { - symbols.push(token2); -} - -if (symbols.length != 0) { - let snippets = []; - for (let symbol of symbols) { - const match = code.match(new RegExp(`^([\\s]*)(${symbol})`, "m")); - if (!match) { - throw new Error(`Symbol not found: ${symbol}`); - } - - // Determine the end of the symbol. - let start = match.index; - let ix, block_token; - try { - [ix, block_token] = [ - ...[":", "{", ";"].map((token) => [ - code.indexOf(token, match.index), - token, - ]), - ] - .filter(([loc]) => loc !== -1) - .sort((a, b) => a[0] - b[0])[0]; - } catch (e) { - throw new Error( - `No block character (:, {) found after ${symbol} at line ${code.slice(0, start).split("\n").length - 1}` - ); - } - - // Push the start marker backwards to include any leading comments. - let lines = code.slice(0, start).split("\n"); - for (let line of lines.reverse()) { - if (/^[\s]*(\/\/|#)/.test(line)) { - start -= line.length + 1; - } else { - break; - } - } - - // Handle block types based on the token found. - let snippet = ""; - if (block_token === ":") { - const indent = match[1].length; - const endMatch = code - .slice(ix) - .match(new RegExp(`^[\\s]{0,${indent}}[^\\s]`, "m")); - snippet = endMatch - ? code.slice(start, ix + endMatch.index) - : code.slice(start); - } else if (block_token === "{") { - let cursor = match.index; - while (true) { - const close_brace = code.indexOf("}", cursor); - if (close_brace === -1) { - throw new Error( - `No corresponding } found for ${symbol} at line ${code.slice(0, start).split("\n").length - 1}` - ); - } - const s = match.index, - e = close_brace + 1; - if ( - code.slice(s, e).split("{").length === - code.slice(s, e).split("}").length - ) { - snippet = code.slice(start, e); - break; - } - cursor = e; - } - } else { - const end = code.indexOf(";", match.index) + 1; - snippet = code.slice(start, end); - } - - // Append the snippet to the list of snippets. - snippets.push(snippet.trim()); - } - - // We have a snippet. Time to put the Markdown together. - partialCode = snippets.join("\n\n"); -} +let partialCode = sampleCode(code, type, token1, token2); --- diff --git a/src/components/utils/sample.ts b/src/components/utils/sample.ts new file mode 100644 index 0000000..dd83005 --- /dev/null +++ b/src/components/utils/sample.ts @@ -0,0 +1,128 @@ +import yaml from 'js-yaml'; + +function sampleCode(code: string, type: string, token1: string, token2: string): string { + if (type == "protobuf") { + return sampleProto(code, token1, token2); + } else if (type == "yml") { + return sampleYaml(code, token1); + } + throw new Error(`Type not found: ${type}`); +} + +function sampleYaml(code: string, path: string): string { + const yaml_code = yaml.load(code); + + // Convert dot notation path into nested object + let segments = path.split('.'); + let result = {}; + let current = result; + + let traversal = yaml_code; + + for (let i = 0; i < segments.length - 1; i++) { + if (segments[i] != "$") { + current[segments[i]] = {}; + current = current[segments[i]]; + + if(!(segments[i] in traversal)) { + throw new Error(`Invalid JSON Path: ${path}`); + } + traversal = traversal[segments[i]]; + } + } + current[segments[segments.length - 1]] = traversal; + + return yaml.dump(result); +} + +function sampleProto(code: string, token1: string, token2: string) { + let symbols = []; + if (token1 != "") { + symbols.push(token1); + } + if (token2 != "") { + symbols.push(token2); + } + + if (symbols.length != 0) { + let snippets = []; + for (let symbol of symbols) { + const match = code.match(new RegExp(`^([\\s]*)(${symbol})`, "m")); + if (!match) { + throw new Error(`Symbol not found: ${symbol}`); + } + + // Determine the end of the symbol. + let start = match.index; + let ix, block_token; + try { + [ix, block_token] = [ + ...[":", "{", ";"].map((token) => [ + code.indexOf(token, match.index), + token, + ]), + ] + .filter(([loc]) => loc !== -1) + .sort((a, b) => a[0] - b[0])[0]; + } catch (e) { + throw new Error( + `No block character (:, {) found after ${symbol} at line ${code.slice(0, start).split("\n").length - 1}` + ); + } + + // Push the start marker backwards to include any leading comments. + let lines = code.slice(0, start).split("\n"); + for (let line of lines.reverse()) { + if (/^[\s]*(\/\/|#)/.test(line)) { + start -= line.length + 1; + } else { + break; + } + } + + // Handle block types based on the token found. + let snippet = ""; + if (block_token === ":") { + const indent = match[1].length; + const endMatch = code + .slice(ix) + .match(new RegExp(`^[\\s]{0,${indent}}[^\\s]`, "m")); + snippet = endMatch + ? code.slice(start, ix + endMatch.index) + : code.slice(start); + } else if (block_token === "{") { + let cursor = match.index; + while (true) { + const close_brace = code.indexOf("}", cursor); + if (close_brace === -1) { + throw new Error( + `No corresponding } found for ${symbol} at line ${code.slice(0, start).split("\n").length - 1}` + ); + } + const s = match.index, + e = close_brace + 1; + if ( + code.slice(s, e).split("{").length === + code.slice(s, e).split("}").length + ) { + snippet = code.slice(start, e); + break; + } + cursor = e; + } + } else { + const end = code.indexOf(";", match.index) + 1; + snippet = code.slice(start, end); + } + + // Append the snippet to the list of snippets. + snippets.push(snippet.trim()); + } + + // We have a snippet. Time to put the Markdown together. + return snippets.join("\n\n"); + } + throw new Error("Bad output"); +} + +export default sampleCode; \ No newline at end of file