diff --git a/.changeset/remove-dead-surface.md b/.changeset/remove-dead-surface.md new file mode 100644 index 00000000..b2a197b6 --- /dev/null +++ b/.changeset/remove-dead-surface.md @@ -0,0 +1,7 @@ +--- +"@parity/cdm-builder": major +"@parity/cdm-env": patch +"@parity/cdm-cli": patch +--- + +Remove dead API surface. `@parity/cdm-builder` drops never-called exports (`ContractDeployer.deployBatch`, `getOnChainCode`, the flat toposort API `toposort`/`DeploymentOrder`/`createCrateToPackageMap`/`detectDeploymentOrder`, sync `pvmContractBuild`, `computeCid`, `normalizeCdmJson`, the solidity `readSolidityAbi`/`artifactDisplayPath`/`bytecodeSize` helpers), the never-emitted `check-cached` deploy event and `"cached"` summary status, the reserved-but-unused `waitFor`/`timeoutMs`/`gateway` deploy options, the constants re-exports (`GAS_LIMIT`/`STORAGE_DEPOSIT_LIMIT`/`CONTRACTS_REGISTRY_CRATE` — import from `@parity/cdm-utils` instead), the unused `MetadataPublisher` client constructor param, and the hardcoded fake `txHash`/`blockHash` on `publishBatch`/`publish-done`. `@parity/cdm-env` drops the unused `AssetHubConnection`/`BulletinConnection` types. `@parity/cdm-cli` sheds the unwired status-adapter observer API and unreachable UI states, stops running contract detection twice per build/deploy, and now shows where installed artifacts were saved after `cdm install`. diff --git a/src/apps/cli/src/commands/deploy.ts b/src/apps/cli/src/commands/deploy.ts index 0c4628b5..18916223 100644 --- a/src/apps/cli/src/commands/deploy.ts +++ b/src/apps/cli/src/commands/deploy.ts @@ -12,8 +12,12 @@ import { type CdmChainClient, } from "@parity/cdm-env"; import { getAccount } from "@parity/cdm-utils/accounts"; -import { ALICE_SS58, CONTRACTS_REGISTRY_PACKAGE } from "@parity/cdm-utils"; -import { ContractDeployer, CONTRACTS_REGISTRY_CRATE, resolveFeatures } from "@parity/cdm-builder"; +import { + ALICE_SS58, + CONTRACTS_REGISTRY_CRATE, + CONTRACTS_REGISTRY_PACKAGE, +} from "@parity/cdm-utils"; +import { ContractDeployer, resolveFeatures } from "@parity/cdm-builder"; import type { HexString } from "polkadot-api"; import { ensureAccountMapped } from "../lib/account-mapping"; import { runDeployWithUI, spinner } from "../lib/ui"; diff --git a/src/apps/cli/src/commands/install/index.ts b/src/apps/cli/src/commands/install/index.ts index ccbe02b4..08bf4ef3 100644 --- a/src/apps/cli/src/commands/install/index.ts +++ b/src/apps/cli/src/commands/install/index.ts @@ -19,13 +19,9 @@ import { } from "@parity/cdm-builder"; import { spinner } from "../../lib/ui"; import { runInstallWithUI } from "../../lib/install-pipeline"; -import type { InstallResult } from "../../lib/install-pipeline"; -import { postInstallRust } from "./rust"; import { postInstallSolidity } from "./solidity"; import { postInstallTypeScript } from "./typescript"; -export type { InstallResult } from "../../lib/install-pipeline"; - function detectProjectType(dir: string): { hasRust: boolean; hasSolidity: boolean; @@ -184,11 +180,9 @@ install.action(async (libraries: string[], rawOpts: InstallOptions) => { writeCdmJson(cdmJson); - // Run post-install hooks and update status line + // Run post-install hooks and update status line. Rust projects need no + // post-install step: cdm.json (written above) is all cdm::import! reads. if (results.length > 0) { - if (projectType.hasRust) { - await postInstallRust(); - } if (projectType.hasSolidity) { await postInstallSolidity(); } diff --git a/src/apps/cli/src/commands/install/rust.ts b/src/apps/cli/src/commands/install/rust.ts deleted file mode 100644 index be69c79f..00000000 --- a/src/apps/cli/src/commands/install/rust.ts +++ /dev/null @@ -1 +0,0 @@ -export async function postInstallRust(): Promise {} diff --git a/src/apps/cli/src/lib/components/DeployTable.tsx b/src/apps/cli/src/lib/components/DeployTable.tsx index 16e47ac2..d089da48 100644 --- a/src/apps/cli/src/lib/components/DeployTable.tsx +++ b/src/apps/cli/src/lib/components/DeployTable.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { Box, Text } from "ink"; -import type { ContractStatus, PhaseInfo } from "../deploy-pipeline"; +import type { ContractStatus } from "../deploy-pipeline"; import { Link, LinkLine, @@ -12,7 +12,6 @@ import { Idle, Done, Failed, - Cached, LogTail, truncateAddress, shortHash, @@ -26,11 +25,9 @@ const COL_PHASE = 5; const COL_ADDR = 14; /** Infer which phase failed based on what fields exist on the status */ -function errorPhase(s: ContractStatus): "build" | "deploy" | "metadata" | "register" { - // Register failed: both deploy and publish completed, error during register - if (s.address && s.publishTxHash) return "register"; - // Deploy completed but publish didn't: metadata/publish failure - if (s.address && !s.publishTxHash && s.cid) return "metadata"; +function errorPhase(s: ContractStatus): "build" | "deploy" | "metadata" { + // Deploy completed: metadata/publish failure + if (s.address && s.cid) return "metadata"; if (s.bytecodeSize !== undefined) return "deploy"; // Build completed: deploy phase (or parallel deploy+publish) failed if ( @@ -81,9 +78,9 @@ function ContractRow({ } else if (state === "waiting") { buildCell = ; } else { - // built/deploying/deployed/publishing/registering/done — show completed bar - // plus the compiled bytecode size (base-10 kB/MB) if the library - // populated `bytecodeSize` on the status. + // built/deploying/done — show completed bar plus the compiled + // bytecode size (base-10 kB/MB) if the library populated + // `bytecodeSize` on the status. const bp = s?.buildProgress; if (bp?.total && bp.total > 0) { buildCell = ( @@ -109,52 +106,19 @@ function ContractRow({ ); } - // Cached state — show cache indicator across all deploy columns - if (state === "cached") { - return ( - - - - {name} - - - {buildCell} - - - - - - - - - - - {s?.address ? {truncateAddress(s.address)} : } - - - ); - } - // Deploy column — use deployInProgress flag for spinner let deployCell: React.ReactNode; - if (state === "checking") { - deployCell = ; - } else if (s?.deployInProgress) { + if (s?.deployInProgress) { deployCell = ; } else if (state === "error" && errorPhase(s!) === "deploy") { deployCell = ; - } else if ( - ["registering", "done"].includes(state) && - s?.deployTxHash && - s?.deployBlockHash && - assethubUrl - ) { + } else if (state === "done" && s?.deployTxHash && s?.deployBlockHash && assethubUrl) { deployCell = ( {shortHash(s.deployTxHash)} ); - } else if (["registering", "done"].includes(state)) { + } else if (state === "done") { deployCell = ; } else { deployCell = ; @@ -166,14 +130,12 @@ function ContractRow({ metaCell = ; } else if (state === "error" && errorPhase(s!) === "metadata") { metaCell = ; - } else if (["registering", "done"].includes(state) && s?.cid && ipfsGatewayUrl) { + } else if (state === "done" && s?.cid && ipfsGatewayUrl) { metaCell = ( {shortHash(s.cid)} ); - } else if (["registering", "done"].includes(state) && s?.publishTxHash) { - metaCell = ; } else { metaCell = ; } @@ -182,8 +144,6 @@ function ContractRow({ let registerCell: React.ReactNode; if (s?.registerInProgress) { registerCell = ; - } else if (state === "error" && errorPhase(s!) === "register") { - registerCell = ; } else if (state === "done" && s?.registerTxHash && s?.registerBlockHash && assethubUrl) { registerCell = ( @@ -226,15 +186,10 @@ function ContractRow({ // full link on its own line below the row. Conditions mirror the cell // rendering above so an inline line appears iff a cell shows a link. const linkDefs: { label: string; url: string }[] = []; - if ( - ["registering", "done"].includes(state) && - s?.deployTxHash && - s?.deployBlockHash && - assethubUrl - ) { + if (state === "done" && s?.deployTxHash && s?.deployBlockHash && assethubUrl) { linkDefs.push({ label: "deploy", url: pjsExplorerUrl(assethubUrl, s.deployBlockHash) }); } - if (["registering", "done"].includes(state) && s?.cid && ipfsGatewayUrl) { + if (state === "done" && s?.cid && ipfsGatewayUrl) { linkDefs.push({ label: "metadata", url: ipfsUrl(ipfsGatewayUrl, s.cid) }); } if (state === "done" && s?.registerTxHash && s?.registerBlockHash && assethubUrl) { @@ -261,7 +216,6 @@ function ContractRow({ export interface DeployTableProps { statuses: Map; displayNames: Map; - crates: string[]; buildOnly: boolean; assethubUrl?: string; bulletinUrl?: string; @@ -273,7 +227,6 @@ export interface DeployTableProps { export function DeployTable({ statuses, displayNames, - crates, buildOnly, assethubUrl, ipfsGatewayUrl, @@ -287,10 +240,9 @@ export function DeployTable({ return () => clearInterval(timer); }, []); - const rowCrates = [ - ...crates, - ...Array.from(statuses.keys()).filter((crate) => !crates.includes(crate)), - ]; + // Row order comes from `statuses` insertion order, which the adapter + // fills in layered deployment order on the `detect` event. + const rowCrates = Array.from(statuses.keys()); // Collect and group errors for display below table. Toolchain-level // failures often apply to every contract in a build batch, and printing diff --git a/src/apps/cli/src/lib/components/InstallTable.tsx b/src/apps/cli/src/lib/components/InstallTable.tsx index 51269229..8ea4d599 100644 --- a/src/apps/cli/src/lib/components/InstallTable.tsx +++ b/src/apps/cli/src/lib/components/InstallTable.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from "react"; +import { relative } from "node:path"; import { Box, Text } from "ink"; import type { InstallStatus } from "../install-pipeline"; import { @@ -87,12 +88,17 @@ function InstallRow({ // When OSC 8 hyperlinks aren't available, the metadata cell only shows the // short CID hash; surface the full link on its own line below the row. - const hasLink = state === "done" && s?.metadataCid && ipfsGatewayUrl; - if (hyperlinksSupported || !hasLink) return row; + const showLinkLine = + !hyperlinksSupported && state === "done" && s?.metadataCid && ipfsGatewayUrl; + // After a successful install, show where the artifacts landed. + const savedPath = + state === "done" && s?.savedPath ? relative(process.cwd(), s.savedPath) : null; + if (!showLinkLine && !savedPath) return row; return ( {row} - + {showLinkLine && } + {savedPath && {` ↳ ${savedPath}`}} ); } diff --git a/src/apps/cli/src/lib/components/shared.tsx b/src/apps/cli/src/lib/components/shared.tsx index 9ec00284..f8b788dd 100644 --- a/src/apps/cli/src/lib/components/shared.tsx +++ b/src/apps/cli/src/lib/components/shared.tsx @@ -92,10 +92,6 @@ export function Failed() { return ; } -export function Cached() { - return ~; -} - export function LogTail({ lines, height, diff --git a/src/apps/cli/src/lib/deploy-pipeline.ts b/src/apps/cli/src/lib/deploy-pipeline.ts index 7518731f..ef4f1aad 100644 --- a/src/apps/cli/src/lib/deploy-pipeline.ts +++ b/src/apps/cli/src/lib/deploy-pipeline.ts @@ -1,10 +1,4 @@ -import type { - BuildEvent, - DeployEvent, - BuildSummary, - DeploySummary, - ContractInfo, -} from "@parity/cdm-builder"; +import type { BuildEvent, DeployEvent } from "@parity/cdm-builder"; /** * CLI-local `ContractStatus` shape that the Ink `DeployTable.tsx` component @@ -14,16 +8,7 @@ import type { * The pipeline itself now lives in `@parity/cdm-builder`; this file only handles * event → UI-status translation so the terminal table stays unchanged. */ -export type ContractState = - | "waiting" - | "building" - | "built" - | "checking" - | "cached" - | "deploying" - | "registering" - | "done" - | "error"; +export type ContractState = "waiting" | "building" | "built" | "deploying" | "done" | "error"; export interface ContractStatus { crateName: string; @@ -33,8 +18,6 @@ export interface ContractStatus { cid?: string; deployTxHash?: string; deployBlockHash?: string; - publishTxHash?: string; - publishBlockHash?: string; /** Same value as `deployTxHash` since deploy+register are one batch now. */ registerTxHash?: string; registerBlockHash?: string; @@ -47,53 +30,23 @@ export interface ContractStatus { registerInProgress?: boolean; } -/** - * Current "phase" signal emitted by the library to describe dead time between - * build and per-row deploy spinners. Mirrors the `DeployEvent.phase` variant - * shape; the adapter stores the latest phase on itself and invokes - * `onPhaseChange` so the Ink UI can render a spinner above the table. - */ -export interface PhaseInfo { - name: - | "connecting-registry" - | "checking-versions" - | "precomputing-addresses" - | "preparing-metadata" - | "deploying" - | "publishing" - | "done"; - description: string; - layer?: number; -} - export interface AdapterOptions { - /** Called on every status mutation (after the update is applied). */ - onStatusChange?: (crateName: string, status: ContractStatus) => void; /** Called when a build reveals a crate's CDM package name. */ onCdmPackageDetected?: (crateName: string, cdmPackage: string) => void; - /** Called when the library emits a `phase` event. */ - onPhaseChange?: (phase: PhaseInfo | null) => void; - /** Called when a process log line is appended to the retained tail. */ - onLogChange?: (lines: string[]) => void; } /** - * Build/deploy adapter — maintains a `Map` that the Ink - * UI reads, plus an `onEvent` handler to pass into `buildContracts()` or - * `deployContracts()`. Also exposes `crates` / `layers` / `contracts` so the UI - * can render the table layout as soon as detection completes. + * Build/deploy adapter — maintains a `Map` and a log + * tail that the Ink UI reads on each render tick, plus an `onEvent` handler + * to pass into `buildContracts()` or `deployContracts()`. Rows appear as the + * library's `detect` event populates `statuses`. */ export class PipelineStatusAdapter { static readonly LOG_TAIL_LINES = 5; readonly statuses = new Map(); readonly logLines: string[] = []; - crates: string[] = []; - layers: string[][] = []; - contracts: ContractInfo[] = []; cdmPackageMap = new Map(); - /** Most recent `phase` event (null until first phase event fires). */ - phase: PhaseInfo | null = null; constructor(private opts: AdapterOptions = {}) {} @@ -104,20 +57,15 @@ export class PipelineStatusAdapter { if (this.logLines.length > PipelineStatusAdapter.LOG_TAIL_LINES) { this.logLines.splice(0, this.logLines.length - PipelineStatusAdapter.LOG_TAIL_LINES); } - this.opts.onLogChange?.([...this.logLines]); } private clearLogs() { - if (this.logLines.length === 0) return; this.logLines.splice(0); - this.opts.onLogChange?.([]); } private update(crate: string, state: ContractState, extra?: Partial) { const current = this.statuses.get(crate) ?? { crateName: crate, state: "waiting" }; - const updated: ContractStatus = { ...current, state, ...extra }; - this.statuses.set(crate, updated); - this.opts.onStatusChange?.(crate, updated); + this.statuses.set(crate, { ...current, state, ...extra }); } /** Forward a `BuildEvent` (emitted by `buildContracts()`) into the UI map. */ @@ -127,15 +75,11 @@ export class PipelineStatusAdapter { this.appendLog(e.line); return; case "detect": - this.contracts = e.contracts; - this.layers = e.layers; - this.crates = e.layers.flat(); for (const c of e.contracts) { if (c.cdmPackage) this.cdmPackageMap.set(c.name, c.cdmPackage); } - for (const crate of this.crates) { + for (const crate of e.layers.flat()) { this.statuses.set(crate, { crateName: crate, state: "waiting" }); - this.opts.onStatusChange?.(crate, this.statuses.get(crate)!); } for (const [crate, pkg] of this.cdmPackageMap) { this.opts.onCdmPackageDetected?.(crate, pkg); @@ -193,26 +137,16 @@ export class PipelineStatusAdapter { case "build-error": this.handleBuildEvent(e as BuildEvent); return; - case "check-cached": - this.update(e.crate, "cached", { address: e.address }); - return; case "check-needs-deploy": // Address precomputed — no state change yet, deploy-register // will follow. return; case "deploy-plan": - // Diagnostic-only — no per-crate state change. The CLI's - // `runDeployWithUI` logs the event to stderr so the user can - // see the real budget vs per-contract weights on their next - // run. Nothing to mutate here. + // Diagnostic-only — no per-crate state change and nothing to + // mutate here. return; case "phase": - this.phase = { - name: e.name, - description: e.description, - layer: e.layer, - }; - this.opts.onPhaseChange?.(this.phase); + // Coarse progress signal — not surfaced in the table UI. return; case "sign-request": // Not forwarded to UI for now — `deploy-register-start` / @@ -267,7 +201,6 @@ export class PipelineStatusAdapter { this.update(crate, existing?.state ?? "done", { publishInProgress: false, cid, - publishTxHash: e.txHash, }); } return; @@ -290,21 +223,9 @@ export class PipelineStatusAdapter { return; } }; - - /** Snapshot of addresses suitable for the old `PipelineResult` consumers. */ - addressesFromSummary(summary: DeploySummary | BuildSummary): Record { - const out: Record = {}; - for (const c of (summary as DeploySummary).contracts) { - const dc = c as { crate: string; address?: string }; - if (dc.address) out[dc.crate] = dc.address; - } - return out; - } } -const ANSI_PATTERN = - // biome-ignore lint/suspicious/noControlCharactersInRegex: terminal log sanitization. - /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g; +const ANSI_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g; function cleanLogLine(line: string): string { return line.replace(ANSI_PATTERN, "").replace(/\r/g, "").trimEnd(); diff --git a/src/apps/cli/src/lib/install-pipeline.ts b/src/apps/cli/src/lib/install-pipeline.ts index c60fc00b..8ef1ff1d 100644 --- a/src/apps/cli/src/lib/install-pipeline.ts +++ b/src/apps/cli/src/lib/install-pipeline.ts @@ -4,12 +4,11 @@ import { installContracts, type InstallContractsOptions, type InstallEvent, - type InstallResult, type InstallSummary, } from "@parity/cdm-builder"; import { InstallTable } from "./components/InstallTable"; -export type { InstallResult, InstallSummary } from "@parity/cdm-builder"; +export type { InstallSummary } from "@parity/cdm-builder"; export type InstallState = "waiting" | "querying" | "fetching" | "done" | "error"; diff --git a/src/apps/cli/src/lib/ui.ts b/src/apps/cli/src/lib/ui.ts index b7c92a42..b88a70e9 100644 --- a/src/apps/cli/src/lib/ui.ts +++ b/src/apps/cli/src/lib/ui.ts @@ -3,7 +3,6 @@ import { render } from "ink"; import { buildContracts, deployContracts, - detectBuildOrder, type BuildContractsOptions, type DeployContractsOptions, type BuildSummary, @@ -42,16 +41,6 @@ export function spinner(label: string, detail: string) { }; } -export function progressBar(current: number, total: number, width: number = 20): string { - if (total === 0) return "░".repeat(width); - const filled = Math.round((current / total) * width); - return "█".repeat(filled) + "░".repeat(width - filled); -} - -export function formatDuration(ms: number): string { - return `${(ms / 1000).toFixed(1)}s`; -} - export interface BuildUIOptions extends Omit {} export interface DeployUIOptions extends Omit { @@ -61,10 +50,8 @@ export interface DeployUIOptions extends Omit } interface RenderArgs { - statuses: Map; + adapter: PipelineStatusAdapter; displayNames: Map; - crates: string[]; - logLines: string[]; buildOnly: boolean; assethubUrl?: string; bulletinUrl?: string; @@ -74,10 +61,9 @@ interface RenderArgs { function makeUI(args: RenderArgs) { return render( React.createElement(DeployTable, { - statuses: args.statuses, + statuses: args.adapter.statuses, displayNames: args.displayNames, - crates: args.crates, - logLines: args.logLines, + logLines: args.adapter.logLines, buildOnly: args.buildOnly, assethubUrl: args.assethubUrl, bulletinUrl: args.bulletinUrl, @@ -86,43 +72,24 @@ function makeUI(args: RenderArgs) { ); } -function precomputeBuildDisplay(rootDir: string, contracts: string[] | undefined) { - const order = detectBuildOrder(rootDir, contracts); - const crates = order.layers.flat(); - const displayNames = new Map(); - for (const contract of order.contracts) { - displayNames.set( - contract.name, - contract.cdmPackage ?? contract.displayName ?? contract.name, - ); - } - return { crates, displayNames }; -} - /** * Run `buildContracts()` and render progress into the Ink `DeployTable`. * - * The table layout is populated lazily from the `detect` event — crates, layers, - * and CDM package names are all supplied by the library, not derived up-front - * in the CLI. + * The table layout is populated from the library's `detect` event — the + * adapter fills `statuses` (row order) and `displayNames` in place, and the + * table re-reads both on every render tick. Nothing is detected up-front in + * the CLI. */ export async function runBuildWithUI(opts: BuildUIOptions): Promise<{ summary: BuildSummary; result: PipelineResult; }> { - const { crates, displayNames } = precomputeBuildDisplay(opts.rootDir, opts.contracts); - + const displayNames = new Map(); const adapter = new PipelineStatusAdapter({ onCdmPackageDetected: (crate, pkg) => displayNames.set(crate, pkg), }); - const app = makeUI({ - statuses: adapter.statuses, - displayNames, - crates, - logLines: adapter.logLines, - buildOnly: true, - }); + const app = makeUI({ adapter, displayNames, buildOnly: true }); let summary: BuildSummary; try { @@ -153,17 +120,14 @@ export async function runDeployWithUI(opts: DeployUIOptions): Promise<{ summary: DeploySummary; result: PipelineResult; }> { - const { crates, displayNames } = precomputeBuildDisplay(opts.rootDir, opts.contracts); - + const displayNames = new Map(); const adapter = new PipelineStatusAdapter({ onCdmPackageDetected: (crate, pkg) => displayNames.set(crate, pkg), }); const app = makeUI({ - statuses: adapter.statuses, + adapter, displayNames, - crates, - logLines: adapter.logLines, buildOnly: false, assethubUrl: opts.assethubUrl, bulletinUrl: opts.bulletinUrl, diff --git a/src/apps/frontend/README.md b/src/apps/frontend/README.md index d2e77611..b299c1ea 100644 --- a/src/apps/frontend/README.md +++ b/src/apps/frontend/README.md @@ -1,73 +1,7 @@ -# React + TypeScript + Vite +# @parity/cdm-frontend -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +Web dashboard for the CDM on-chain contract registry: browse and search published +contracts, view readmes, ABIs, versions, and dependencies (React 19 + Vite). -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## React Compiler - -The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: - -```js -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - - // Remove tseslint.configs.recommended and replace with this - tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - tseslint.configs.stylisticTypeChecked, - - // Other configs... - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` - -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: - -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - // Enable lint rules for React - reactX.configs['recommended-typescript'], - // Enable lint rules for React DOM - reactDom.configs.recommended, - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` +- `pnpm dev` (from the repo root) — start the dev server with workspace deps built +- `pnpm --filter @parity/cdm-frontend build` — production build diff --git a/src/apps/frontend/src/App.css b/src/apps/frontend/src/App.css deleted file mode 100644 index 67783503..00000000 --- a/src/apps/frontend/src/App.css +++ /dev/null @@ -1 +0,0 @@ -/* This file is intentionally left empty. App styles are in component/page CSS files */ diff --git a/src/apps/frontend/src/components/Header.tsx b/src/apps/frontend/src/components/Header.tsx index 7f9ecad1..148b281b 100644 --- a/src/apps/frontend/src/components/Header.tsx +++ b/src/apps/frontend/src/components/Header.tsx @@ -5,11 +5,7 @@ import "./Header.css"; const REPO_URL = "https://github.com/paritytech/contract-dependency-manager"; -const EXTERNAL_LINKS: { label: string; href: string }[] = [ - { label: "Docs", href: REPO_URL }, - { label: "Github", href: REPO_URL }, - { label: "Playground", href: "https://playground.dot" }, -]; +const EXTERNAL_LINKS: { label: string; href: string }[] = [{ label: "Github", href: REPO_URL }]; export default function Header() { return ( diff --git a/src/apps/frontend/src/components/PackageCard.tsx b/src/apps/frontend/src/components/PackageCard.tsx index 2bfc44a7..177054a4 100644 --- a/src/apps/frontend/src/components/PackageCard.tsx +++ b/src/apps/frontend/src/components/PackageCard.tsx @@ -1,5 +1,6 @@ import { Link } from "react-router-dom"; import { metadataCidFromUri } from "../data/registry-queries"; +import { splitPackageName } from "../lib/package-name"; import type { Package } from "../data/types"; import "./PackageCard.css"; @@ -8,19 +9,6 @@ interface PackageCardProps { linkTarget?: string; } -function formatCalls(n: number): string { - return n.toLocaleString(); -} - -function splitPackageName(name: string): { prefix: string; leaf: string } { - const idx = name.lastIndexOf("/"); - if (idx < 0) return { prefix: "", leaf: name }; - return { - prefix: name.slice(0, idx + 1), - leaf: name.slice(idx + 1), - }; -} - export default function PackageCard({ pkg, linkTarget }: PackageCardProps) { const metadataLoading = !!metadataCidFromUri(pkg.metadataUri) && !pkg.metadataLoaded; const name = splitPackageName(pkg.name); @@ -54,11 +42,6 @@ export default function PackageCard({ pkg, linkTarget }: PackageCardProps) { ) : ( )} - {pkg.weeklyCalls != null && ( - - {formatCalls(pkg.weeklyCalls)} weekly calls - - )} {pkg.publishedDate ? ( {pkg.publishedDate} ) : metadataLoading ? ( diff --git a/src/apps/frontend/src/components/SkeletonCard.tsx b/src/apps/frontend/src/components/SkeletonCard.tsx index 87f9d317..75da806b 100644 --- a/src/apps/frontend/src/components/SkeletonCard.tsx +++ b/src/apps/frontend/src/components/SkeletonCard.tsx @@ -27,7 +27,6 @@ export function SkeletonGrid({ count = 9 }: SkeletonGridProps) { return ( diff --git a/src/apps/frontend/src/data/registry-queries.ts b/src/apps/frontend/src/data/registry-queries.ts index e0c97dd1..3fe8d8a5 100644 --- a/src/apps/frontend/src/data/registry-queries.ts +++ b/src/apps/frontend/src/data/registry-queries.ts @@ -53,7 +53,6 @@ export async function queryContractByName( return { name, version: String(latestVersion), - weeklyCalls: 0, address: unwrapOption(addressResult.value), metadataUri: unwrapOption(metadataResult.value), metadataLoaded: false, @@ -87,7 +86,6 @@ function parseContractEntry(value: unknown): Package | null { return { name, version: String(Number(version ?? 0)), - weeklyCalls: 0, address: typeof address === "string" ? address : undefined, metadataUri: typeof metadataUri === "string" ? metadataUri : undefined, metadataLoaded: false, diff --git a/src/apps/frontend/src/data/types.ts b/src/apps/frontend/src/data/types.ts index 684590bc..3ed945be 100644 --- a/src/apps/frontend/src/data/types.ts +++ b/src/apps/frontend/src/data/types.ts @@ -18,7 +18,6 @@ export interface Package { version: string; description?: string; author?: string; - weeklyCalls?: number; license?: string; keywords?: string[]; publishedDate?: string; @@ -27,7 +26,6 @@ export interface Package { homepage?: string; readme?: string; dependencies?: Record; - versions?: { version: string; date: string }[]; abi?: AbiEntry[]; address?: string; metadataUri?: string; diff --git a/src/apps/frontend/src/index.css b/src/apps/frontend/src/index.css deleted file mode 100644 index 98ce4e51..00000000 --- a/src/apps/frontend/src/index.css +++ /dev/null @@ -1 +0,0 @@ -/* This file is intentionally left empty. Global styles are in styles/global.css */ diff --git a/src/apps/frontend/src/lib/package-name.ts b/src/apps/frontend/src/lib/package-name.ts new file mode 100644 index 00000000..af42e6e5 --- /dev/null +++ b/src/apps/frontend/src/lib/package-name.ts @@ -0,0 +1,9 @@ +/** Split a CDM package name into its scope prefix ("@org/") and leaf part. */ +export function splitPackageName(name: string): { prefix: string; leaf: string } { + const idx = name.lastIndexOf("/"); + if (idx < 0) return { prefix: "", leaf: name }; + return { + prefix: name.slice(0, idx + 1), + leaf: name.slice(idx + 1), + }; +} diff --git a/src/apps/frontend/src/pages/PackagePage.css b/src/apps/frontend/src/pages/PackagePage.css index 91d1e7d3..ee28110a 100644 --- a/src/apps/frontend/src/pages/PackagePage.css +++ b/src/apps/frontend/src/pages/PackagePage.css @@ -334,16 +334,6 @@ border-radius: 999px; color: var(--color-text-secondary); font-size: 12px; - text-decoration: none; - transition: - background-color 0.1s ease, - color 0.1s ease; -} - -.sidebar-keyword:hover { - background: rgba(255, 255, 255, 0.08); - color: var(--color-text-primary); - text-decoration: none; } /* ============ Status pages ============ */ diff --git a/src/apps/frontend/src/pages/PackagePage.tsx b/src/apps/frontend/src/pages/PackagePage.tsx index 87e9ed6c..8722114e 100644 --- a/src/apps/frontend/src/pages/PackagePage.tsx +++ b/src/apps/frontend/src/pages/PackagePage.tsx @@ -5,6 +5,7 @@ import DOMPurify from "dompurify"; import Layout from "../components/Layout"; import { CopyIcon, CheckIcon } from "../components/Icons"; import { handleExternalClick } from "../lib/external-link"; +import { splitPackageName } from "../lib/package-name"; import { usePackage } from "../hooks/usePackage"; import { usePackageVersions } from "../hooks/usePackageVersions"; import type { PackageVersionInfo } from "../data/registry-queries"; @@ -55,12 +56,6 @@ function getBadgeClass(entry: AbiEntry): string { } } -function splitName(name: string): { prefix: string; leaf: string } { - const idx = name.lastIndexOf("/"); - if (idx < 0) return { prefix: "", leaf: name }; - return { prefix: name.slice(0, idx + 1), leaf: name.slice(idx + 1) }; -} - function shortAddress(addr: string): string { if (addr.length <= 14) return addr; return `${addr.slice(0, 8)}…${addr.slice(-6)}`; @@ -76,7 +71,6 @@ function ParamType({ param, depth = 0 }: { param: AbiParam; depth?: number }) { {param.components.map((c, i) => ( {inputs.map((p, i) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: param index is stable {p.name || `_${i}`} @@ -167,7 +160,6 @@ function AbiEntryCard({ entry }: { entry: AbiEntry }) { {outputs.map((p, i) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: param index is stable {p.name || `_${i}`} @@ -309,7 +301,6 @@ function VersionsTab({ pkg, versions, loading, error }: VersionsTabProps) { return (
{Array.from({ length: 3 }).map((_, i) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: fixed decorative array ))}
@@ -374,7 +365,6 @@ function PackageBody({ pkg, activeTab, setActiveTab }: PackageBodyProps) { (pkg.readme ? (
{Array.from({ length: 5 }).map((_, i) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: fixed decorative array ))}
@@ -449,7 +438,6 @@ function PackageSkeleton() {