From 4230c5d614335a7ee72da5408e4255242ce46f6b Mon Sep 17 00:00:00 2001 From: Pierre Demailly Date: Sun, 19 Jan 2025 16:04:45 +0100 Subject: [PATCH] feat(interface): differentiate local scans from npm scans --- public/common/utils.js | 5 +++-- public/components/searchbar/searchbar.css | 4 ++++ public/components/views/search/search.css | 12 +++++++++++ public/components/views/search/search.js | 5 +++-- public/core/search-nav.js | 16 ++++++++------ src/commands/scanner.js | 13 +++++++++--- src/http-server/cache.js | 26 ++++++++++++++++------- src/http-server/endpoints/data.js | 2 +- src/http-server/websocket/search.js | 13 ++++++------ 9 files changed, 68 insertions(+), 28 deletions(-) diff --git a/public/common/utils.js b/public/common/utils.js index da597cf1..9d20bb6c 100644 --- a/public/common/utils.js +++ b/public/common/utils.js @@ -64,10 +64,11 @@ export function createLink(href, text = null) { export function parseNpmSpec(spec) { const parts = spec.split("@"); + const [version, local] = parts[parts.length - 1].split("#"); return spec.startsWith("@") ? - { name: `@${parts[1]}`, version: parts[2] } : - { name: parts[0], version: parts[1] }; + { name: `@${parts[1]}`, version, local: Boolean(local) } : + { name: parts[0], version, local: Boolean(local) }; } export function parseRepositoryUrl(repository = {}, defaultValue = null) { diff --git a/public/components/searchbar/searchbar.css b/public/components/searchbar/searchbar.css index cddcc483..8f8b0ed6 100644 --- a/public/components/searchbar/searchbar.css +++ b/public/components/searchbar/searchbar.css @@ -268,6 +268,10 @@ div.search-result-pannel .package+.package { display: block; } +#search-nav .packages>.package>b:last-of-type:not(:first-of-type) { + background: #f57c00; +} + #search-nav .packages>.package>b{ font-weight: bold; font-size: 12px; diff --git a/public/components/views/search/search.css b/public/components/views/search/search.css index 494df83e..78ffdb0a 100644 --- a/public/components/views/search/search.css +++ b/public/components/views/search/search.css @@ -198,6 +198,18 @@ input:-webkit-autofill { border-radius: 4px; } +.package-result>span>b { + background: #f57c00; + color: white; + font-weight: bold; + font-size: 12px; + margin-left: 5px; + padding: 0 5px; + border-radius: 2px; + font-family: "Roboto"; + letter-spacing: 1px; +} + .package-result .description { font-size: 14px; color: rgb(78, 76, 76); diff --git a/public/components/views/search/search.js b/public/components/views/search/search.js index e2ad6551..f0b966c9 100644 --- a/public/components/views/search/search.js +++ b/public/components/views/search/search.js @@ -2,7 +2,7 @@ import { getJSON, NodeSecureDataSet, NodeSecureNetwork } from "@nodesecure/vis-network"; // Import Internal Dependencies -import { currentLang, debounce, createDOMElement } from "../../../common/utils.js"; +import { currentLang, debounce, createDOMElement, parseNpmSpec } from "../../../common/utils.js"; // CONSTANTS const kMinPackageNameLength = 2; @@ -162,10 +162,11 @@ export class SearchView { cachePackagesElement.appendChild(h1Element); for (const pkg of window.scannedPackageCache) { + const { name, version, local } = parseNpmSpec(pkg); const pkgElement = document.createElement("div"); pkgElement.classList.add("package-result"); const pkgSpanElement = document.createElement("span"); - pkgSpanElement.textContent = pkg; + pkgSpanElement.innerHTML = `${name}@${version}${local ? " local" : ""}`; pkgSpanElement.addEventListener("click", () => { window.socket.send(JSON.stringify({ action: "SEARCH", pkg })); }, { once: true }); diff --git a/public/core/search-nav.js b/public/core/search-nav.js index 58fe08a1..f8e95adf 100644 --- a/public/core/search-nav.js +++ b/public/core/search-nav.js @@ -53,14 +53,18 @@ function initPackagesNavigation(data) { }); for (const pkg of packages) { - const { name, version } = parseNpmSpec(pkg); - + const { name, version, local } = parseNpmSpec(pkg); + + const childs = [ + createDOMElement("p", { text: name }), + createDOMElement("b", { text: `v${version}` }) + ]; + if (local) { + childs.push(createDOMElement("b", { text: "local" })); + } const pkgElement = createDOMElement("div", { classList: ["package"], - childs: [ - createDOMElement("p", { text: name }), - createDOMElement("b", { text: `v${version}` }) - ] + childs }); pkgElement.dataset.name = pkg; if (pkg === data.current) { diff --git a/src/commands/scanner.js b/src/commands/scanner.js index bfe2aa13..08c4d113 100644 --- a/src/commands/scanner.js +++ b/src/commands/scanner.js @@ -60,7 +60,7 @@ export async function cwd(options) { initLogger(void 0, !silent) ); - return await logAndWrite(payload, output); + return await logAndWrite(payload, output, { local: true }); } export async function from(spec, options) { @@ -154,7 +154,9 @@ function initLogger(spec, verbose = true) { return logger; } -async function logAndWrite(payload, output = "nsecure-result") { +async function logAndWrite(payload, output = "nsecure-result", options = {}) { + const { local = false } = options; + if (payload === null) { console.log(i18n.getTokenSync("cli.no_dep_to_proceed")); @@ -170,6 +172,11 @@ async function logAndWrite(payload, output = "nsecure-result") { const ret = JSON.stringify(payload, null, 2); + if (local) { + // FIXME: would it make more sense to manage this directly within Scanner? + Object.assign(ret, { local }); + } + const fileName = path.extname(output) === ".json" ? filenamify(output) : `${filenamify(output)}.json`; @@ -180,7 +187,7 @@ async function logAndWrite(payload, output = "nsecure-result") { console.log(kleur.white().bold(i18n.getTokenSync("cli.successfully_written_json", kleur.green().bold(filePath)))); console.log(""); - await appCache.setRootPayload(payload, { logging: false }); + await appCache.setRootPayload(payload, { logging: false, local }); return filePath; } diff --git a/src/http-server/cache.js b/src/http-server/cache.js index 2b6c312a..9c4f1627 100644 --- a/src/http-server/cache.js +++ b/src/http-server/cache.js @@ -116,40 +116,50 @@ class _AppCache { logger.info(`[cache|init](packagesInFolder: ${packagesInFolder})`); } - await cacache.put(CACHE_PATH, kPayloadsCache, JSON.stringify({ older: packagesInFolder, current: null, lru: [] })); + await cacache.put(CACHE_PATH, kPayloadsCache, JSON.stringify({ + older: packagesInFolder, + current: null, + lru: [] + })); } removePayload(pkg) { - fs.rmSync(path.join(kPayloadsPath, pkg.replaceAll("/", "-"))); + fs.rmSync(path.join(kPayloadsPath, pkg.replaceAll("/", "-")), { force: true }); } async removeLastLRU() { - const { lru, lastUsed, older, root } = await this.payloadsList(); + const { lru, lastUsed, older, ...cache } = await this.payloadsList(); if (lru.length < kMaxPayloads) { - return { lru, older, lastUsed, root }; + return { + ...cache, + lru, + older, + lastUsed + }; } const packageToBeRemoved = Object.keys(lastUsed) .filter((key) => lru.includes(key)) .sort((a, b) => lastUsed[a] - lastUsed[b])[0]; return { + ...cache, lru: lru.filter((pkg) => pkg !== packageToBeRemoved), older: [...older, packageToBeRemoved], - lastUsed, - root + lastUsed }; } async setRootPayload(payload, options) { - const { logging = true } = options; + const { logging = true, local = false } = options; const version = Object.keys(payload.dependencies[payload.rootDependencyName].versions)[0]; - const pkg = `${payload.rootDependencyName}@${version}`; + const pkg = `${payload.rootDependencyName}@${version}${local ? "#local" : ""}`; this.updatePayload(pkg, payload); await this.initPayloadsList({ logging }); const { lru, older, lastUsed } = await this.removeLastLRU(); + const updatedPayloadsCache = { lru: [...new Set([...lru, pkg])], older, diff --git a/src/http-server/endpoints/data.js b/src/http-server/endpoints/data.js index aa28786a..fbc8397a 100644 --- a/src/http-server/endpoints/data.js +++ b/src/http-server/endpoints/data.js @@ -25,7 +25,7 @@ export async function get(_req, res) { const payload = JSON.parse(fs.readFileSync(kDefaultPayloadPath, "utf-8")); const version = Object.keys(payload.dependencies[payload.rootDependencyName].versions)[0]; - const formatted = `${payload.rootDependencyName}@${version}`; + const formatted = `${payload.rootDependencyName}@${version}${payload.local ? "#local" : ""}`; const payloadsList = { lru: [formatted], current: formatted, diff --git a/src/http-server/websocket/search.js b/src/http-server/websocket/search.js index cad88c21..cff86931 100644 --- a/src/http-server/websocket/search.js +++ b/src/http-server/websocket/search.js @@ -24,13 +24,14 @@ export async function search(ws, pkg) { return; } - const { lru, older, lastUsed, root } = await appCache.removeLastLRU(); + + const { lru, older, lastUsed, ...updatedCache } = await appCache.removeLastLRU(); const updatedList = { + ...updatedCache, lru: [...new Set([...lru, pkg])], current: pkg, older: older.filter((pckg) => pckg !== pkg), - lastUsed: { ...lastUsed, [pkg]: Date.now() }, - root + lastUsed: { ...lastUsed, [pkg]: Date.now() } }; await appCache.updatePayloadsList(updatedList); @@ -57,15 +58,15 @@ export async function search(ws, pkg) { logger.info(`[ws|search](scan ${pkg} done|cache: updated)`); // update the payloads list - const { lru, older, lastUsed, root } = await appCache.removeLastLRU(); + const { lru, older, lastUsed, ...cache } = await appCache.removeLastLRU(); lru.push(pkg); appCache.updatePayload(pkg, payload); const updatedList = { + ...cache, lru: [...new Set(lru)], older, lastUsed: { ...lastUsed, [pkg]: Date.now() }, - current: pkg, - root + current: pkg }; await appCache.updatePayloadsList(updatedList);