Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/easy-schools-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nodesecure/cli": major
---

Enable checkJS and add missing JSDoc and fix all application errors
11 changes: 7 additions & 4 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ export default [
jsdoc
},
rules: {
"jsdoc/no-undefined-types": ["warn", {
disableReporting: true,
markVariablesAsUsed: true
}]
"jsdoc/no-undefined-types": [
"warn", {
disableReporting: true,
markVariablesAsUsed: true
}
],
"no-inline-comments": "off"
}
},
{
Expand Down
1 change: 1 addition & 0 deletions workspaces/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@openally/httpie": "1.1.2",
"@playwright/test": "1.61.1",
"@stylistic/stylelint-plugin": "5.1.0",
"@types/semver": "7.7.1",
"esbuild": "0.28.1",
"highlight.js": "11.11.1",
"postcss-lit": "1.4.1",
Expand Down
55 changes: 49 additions & 6 deletions workspaces/cli/public/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function extractEmojis(strWithEmojis) {
* @param {keyof HTMLElementTagNameMap} kind
* @param {object} [options]
* @param {string[]} [options.classList]
* @param {HTMLElement[]} [options.childs]
* @param {(Node | null)[]} [options.childs]
* @param {Record<string, any>} [options.attributes]
* @param {Record<string, any>} [options.styles]
* @param {string | null} [options.text]
Expand Down Expand Up @@ -136,7 +136,7 @@ export function parseRepositoryUrl(repository = {}, defaultValue = null) {

/**
* @param {string} title
* @param {string} value
* @param {string | number} value
* @param {Record<string, any>} options
* @returns {HTMLElement}
*/
Expand All @@ -148,11 +148,12 @@ export function createLiField(title, value, options = {}) {
let elementToAppend;

if (isLink) {
const textValue = value.length > 26 ? `${value.slice(0, 26)}...` : value;
elementToAppend = createLink(value, textValue);
const href = /** @type {string} */ (value);
const textValue = href.length > 26 ? `${href.slice(0, 26)}...` : href;
elementToAppend = createLink(href, textValue);
}
else {
elementToAppend = createDOMElement("p", { text: value });
elementToAppend = createDOMElement("p", { text: String(value) });
}
liElement.appendChild(elementToAppend);

Expand All @@ -163,7 +164,7 @@ export function createLiField(title, value, options = {}) {
* @param {HTMLElement} node - The parent DOM element.
* @param {string[]} items - Array of strings to display.
* @param {Object} [options] - Optional configuration options.
* @param {Function} [options.onclick] - Callback function (event, item).
* @param {(event: Event, item: string) => void} [options.onclick] - Callback function (event, item).
* @param {boolean} [options.hideItems] - Hide items if needed.
* @param {number} [options.hideItemsLength] - Number of visible elements before masking.
* @returns {void}
Expand Down Expand Up @@ -307,6 +308,48 @@ export function hideOnClickOutside(
return outsideClickListener;
}

/**
* Returns the i18n dictionary for the given language (defaults to the current language).
*
* Works around `window.i18n`'s declared type being widened to `Record<string, unknown>`
* by a third-party ambient declaration (`@nodesecure/vis-network`'s `utils.d.ts`).
*
* @param {string} [lang]
* @returns {Record<string, Record<string, string>>}
*/
export function getI18n(lang = currentLang()) {
return /** @type {Record<string, Record<string, string>>} */ (
window.i18n[lang]
);
}

/**
* Returns the application settings config.
*
* Works around `window.settings`'s declared type being unreliably shadowed by a
* conflicting third-party ambient declaration (`@nodesecure/vis-network`'s
* `dataset.d.ts` declares `Window.settings: { config: { showFriendlyDependencies } }`).
*
* @returns {import("../types.js").AppConfig}
*/
export function getSettingsConfig() {
return /** @type {import("../types.js").AppConfig} */ (/** @type {unknown} */ (
window.settings.config
));
}

/**
* `window.navigation` collides with the browser's native, read-only Navigation API.
* The app intentionally shadows it with its own `ViewNavigation` controller.
*
* @returns {import("../components/navigation/navigation.js").ViewNavigation}
*/
export function getNavigation() {
return /** @type {import("../components/navigation/navigation.js").ViewNavigation} */ (/** @type {unknown} */ (
window.navigation
));
}

/** @returns {string} */
export function currentLang() {
const detectedLang = document.getElementById("lang")?.dataset.lang;
Expand Down
15 changes: 14 additions & 1 deletion workspaces/cli/public/components/bundlephobia/bundlephobia.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,21 @@ class Bundlephobia extends LitElement {
version: { type: String }
};

constructor() {
super();
/** @type {string} */
this.name = "";
/** @type {string} */
this.version = "";
}

#bunldeTask = new Task(this, {
task: async([name, version]) => {
const {
gzip, size, dependencySizes
} = await getJSON(`/bundle/${this.#httpName(name)}/${version}`);
} = /** @type {{ gzip: number, size: number, dependencySizes: { approximateSize: number }[] }} */ (
await getJSON(`/bundle/${this.#httpName(name)}/${version}`)
);
const fullSize = dependencySizes.reduce((prev, curr) => prev + curr.approximateSize, 0);

return {
Expand All @@ -100,6 +110,9 @@ class Bundlephobia extends LitElement {
args: () => [this.name, this.version]
});

/**
* @param {string} name
*/
#httpName(name) {
return name.replaceAll("/", "%2F");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { repeat } from "lit/directives/repeat.js";
import { classMap } from "lit/directives/class-map.js";

// Import Internal Dependencies
import { currentLang } from "../../common/utils.js";
import * as utils from "../../common/utils.js";
import {
FLAG_LIST,
SIZE_PRESETS,
Expand All @@ -23,10 +23,29 @@ const kListTitleKeys = {
};

/**
* @param {{ linker: Map, queries: Array, inputValue: string, onAdd: Function, onRemove: Function }} props
* @returns {Record<string, string>}
*/
function getSearchCommandI18n() {
return /** @type {Record<string, string>} */ (/** @type {unknown} */ (utils.getI18n().search_command));
}

/**
* @typedef {import("@nodesecure/vis-network").LinkerEntry} LinkerEntry
* @typedef {{ filter: string, value: string }} SearchQuery
* @typedef {{ display: string, value: string, hint?: string }} HelperValue
*/

/**
* @param {{
* linker: Map<number, LinkerEntry>,
* queries: SearchQuery[],
* inputValue: string,
* onAdd: (filter: string, value: string) => void,
* onRemove: (filter: string, value: string) => void
* }} props
*/
export function renderFlagPanel({ linker, queries, inputValue, onAdd, onRemove }) {
const i18n = window.i18n[currentLang()].search_command;
const i18n = getSearchCommandI18n();
const flagCounts = getFlagCounts(linker);
const activeFlags = new Set(
queries
Expand Down Expand Up @@ -65,10 +84,10 @@ export function renderFlagPanel({ linker, queries, inputValue, onAdd, onRemove }
}

/**
* @param {{ activeFilter: string, onAdd: Function }} props
* @param {{ activeFilter: string, onAdd: (filter: string, value: string) => void }} props
*/
export function renderRangePanel({ activeFilter, onAdd }) {
const i18n = window.i18n[currentLang()].search_command;
const i18n = getSearchCommandI18n();
const isSizeFilter = activeFilter === "size";
const presets = isSizeFilter ? SIZE_PRESETS : VERSION_PRESETS;
const title = isSizeFilter ? i18n.section_size : i18n.section_version;
Expand All @@ -93,12 +112,19 @@ export function renderRangePanel({ activeFilter, onAdd }) {
}

/**
* @param {{ linker: Map, activeFilter: string, helpers: Array, selectedIndex: number, onAdd: Function }} props
* @param {{
* linker: Map<number, LinkerEntry>,
* activeFilter: string,
* helpers: HelperValue[],
* selectedIndex: number,
* onAdd: (filter: string, value: string) => void
* }} props
*/
export function renderListPanel({ linker, activeFilter, helpers, selectedIndex, onAdd }) {
const i18n = window.i18n[currentLang()].search_command;
const i18n = getSearchCommandI18n();
const counts = getFilterValueCounts(linker, activeFilter);
const title = i18n[kListTitleKeys[activeFilter]] ?? activeFilter;
const titleI18nKey = /** @type {Record<string, string>} */ (kListTitleKeys)[activeFilter];
const title = i18n[titleI18nKey] ?? activeFilter;

return html`
<div class="section">
Expand All @@ -117,10 +143,10 @@ export function renderListPanel({ linker, activeFilter, helpers, selectedIndex,
}

/**
* @param {{ helpers: Array, selectedIndex: number, onSelect: Function }} props
* @param {{ helpers: HelperValue[], selectedIndex: number, onSelect: (helper: HelperValue) => void }} props
*/
export function renderFilterList({ helpers, selectedIndex, onSelect }) {
const i18n = window.i18n[currentLang()].search_command;
const i18n = getSearchCommandI18n();

return html`
<div class="section">
Expand All @@ -138,10 +164,13 @@ export function renderFilterList({ helpers, selectedIndex, onSelect }) {
}

/**
* @param {{ presets: Array, onApply: Function }} props
* @param {{
* presets: { id: string, filter: string, value: string }[],
* onApply: (preset: { id: string, filter: string, value: string }) => void
* }} props
*/
export function renderPresets({ presets, onApply }) {
const i18n = window.i18n[currentLang()].search_command;
const i18n = getSearchCommandI18n();

return html`
<div class="section">
Expand All @@ -161,10 +190,13 @@ export function renderPresets({ presets, onApply }) {
}

/**
* @param {{ actions: Array<{ id: string, label: string, kbd: string|null }>, onExecute: Function }} props
* @param {{
* actions: { id: string, label: string, kbd: string|null }[],
* onExecute: (action: { id: string, label: string, kbd: string|null }) => void
* }} props
*/
export function renderActions({ actions, onExecute }) {
const i18n = window.i18n[currentLang()].search_command;
const i18n = getSearchCommandI18n();

return html`
<div class="section">
Expand Down Expand Up @@ -214,14 +246,19 @@ export function renderIgnorePanel({ title, items, ignored, onToggle }) {
}

/**
* @param {{ results: Array, selectedIndex: number, helperCount: number, onFocus: Function }} props
* @param {{
* results: { id: string, flags: string, name: string, version: string }[],
* selectedIndex: number,
* helperCount: number,
* onFocus: (id: string) => void
* }} props
*/
export function renderResults({ results, selectedIndex, helperCount, onFocus }) {
if (results.length === 0) {
return nothing;
}

const i18n = window.i18n[currentLang()].search_command;
const i18n = getSearchCommandI18n();

return html`
<div class="section">
Expand Down
Loading
Loading