diff --git a/README.md b/README.md index 8d59946c..0a05510b 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ All packages are packaged underneath the `@stephansama` scope (for example: `@st | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | [ai-commit-msg](core/ai-commit-msg/README.md) | ![npm version image](https://img.shields.io/npm/v/%40stephansama%2Fai-commit-msg?logo=npm&logoColor=red&color=211F1F&labelColor=211F1F) | ![npm downloads](https://img.shields.io/npm/dw/@stephansama/ai-commit-msg?labelColor=211F1F) | generate commit messages using ai | | [alfred-kaomoji](core/alfred-kaomoji/README.md) | ![npm version image](https://img.shields.io/npm/v/%40stephansama%2Falfred-kaomoji?logo=npm&logoColor=red&color=211F1F&labelColor=211F1F) | ![npm downloads](https://img.shields.io/npm/dw/@stephansama/alfred-kaomoji?labelColor=211F1F) | Alfred Kaomoji Picker | +| [anchor-pnpm](core/anchor-pnpm/README.md) | ![npm version image](https://img.shields.io/npm/v/%40stephansama%2Fanchor-pnpm?logo=npm&logoColor=red&color=211F1F&labelColor=211F1F) | ![npm downloads](https://img.shields.io/npm/dw/@stephansama/anchor-pnpm?labelColor=211F1F) | Manage YAML anchor declarations in pnpm-workspace.yaml | | [astro-iconify-svgmap](core/astro-iconify-svgmap/README.md) | ![npm version image](https://img.shields.io/npm/v/%40stephansama%2Fastro-iconify-svgmap?logo=npm&logoColor=red&color=211F1F&labelColor=211F1F) | ![npm downloads](https://img.shields.io/npm/dw/@stephansama/astro-iconify-svgmap?labelColor=211F1F) | Astro integration for generating iconify svgmaps for ssg sites | | [auto-readme](core/auto-readme/README.md) | ![npm version image](https://img.shields.io/npm/v/%40stephansama%2Fauto-readme?logo=npm&logoColor=red&color=211F1F&labelColor=211F1F) | ![npm downloads](https://img.shields.io/npm/dw/@stephansama/auto-readme?labelColor=211F1F) | Generate lists and tables for your README automagically based on your repository and comments | | [catppuccin-jsonresume-theme](core/catppuccin-jsonresume-theme/README.md) | ![npm version image](https://img.shields.io/npm/v/%40stephansama%2Fcatppuccin-jsonresume-theme?logo=npm&logoColor=red&color=211F1F&labelColor=211F1F) | ![npm downloads](https://img.shields.io/npm/dw/@stephansama/catppuccin-jsonresume-theme?labelColor=211F1F) | theme for resume cli website | diff --git a/core/anchor-pnpm/README.md b/core/anchor-pnpm/README.md new file mode 100644 index 00000000..f4f4a00c --- /dev/null +++ b/core/anchor-pnpm/README.md @@ -0,0 +1,42 @@ +# @stephansama/anchor-pnpm + +CLI for managing YAML anchor declarations in `pnpm-workspace.yaml`. + +Many pnpm catalog setups repeat the same version string across multiple packages in a catalog group: + +```yaml +catalogs: + alpine: + alpinejs: 3.15.8 + "@alpinejs/focus": 3.15.8 +``` + +Sharing the version via a YAML anchor keeps them in lock-step: + +```yaml +__versions: + - &alpine 3.15.8 + +catalogs: + alpine: + alpinejs: *alpine + "@alpinejs/focus": *alpine +``` + +`anchor-pnpm` automates creating and maintaining that `__versions` block while preserving the surrounding YAML (anchors, comments, ordering) via the `yaml` package's Document API. + +## Commands + +- `anchor-pnpm list` — print every anchor name and resolved version. +- `anchor-pnpm add ` — append a new `& ` entry. +- `anchor-pnpm update ` — set the scalar value of an existing anchor (all `*` aliases follow automatically). +- `anchor-pnpm remove ` — remove the anchor; errors if aliases still reference it (pass `--force` to inline them first). +- `anchor-pnpm sync` — _scheduled, not yet implemented._ Auto-detect catalog groups with repeated versions and convert them to anchor+alias pairs. + +## Global flags + +``` +--workspace, -w Path to pnpm-workspace.yaml (default: auto-detect via @manypkg/find-root) +--dry-run, -d Print the rewritten YAML to stdout instead of writing +--verbose, -v Show detailed output +``` diff --git a/core/anchor-pnpm/__snapshots__/tsnapi/cli.snapshot.d.ts b/core/anchor-pnpm/__snapshots__/tsnapi/cli.snapshot.d.ts new file mode 100644 index 00000000..745de720 --- /dev/null +++ b/core/anchor-pnpm/__snapshots__/tsnapi/cli.snapshot.d.ts @@ -0,0 +1,4 @@ +/** + * Generated by tsnapi — public API snapshot of `@stephansama/anchor-pnpm/cli` + */ +/* no exports */ \ No newline at end of file diff --git a/core/anchor-pnpm/__snapshots__/tsnapi/cli.snapshot.js b/core/anchor-pnpm/__snapshots__/tsnapi/cli.snapshot.js new file mode 100644 index 00000000..745de720 --- /dev/null +++ b/core/anchor-pnpm/__snapshots__/tsnapi/cli.snapshot.js @@ -0,0 +1,4 @@ +/** + * Generated by tsnapi — public API snapshot of `@stephansama/anchor-pnpm/cli` + */ +/* no exports */ \ No newline at end of file diff --git a/core/anchor-pnpm/__snapshots__/tsnapi/index.snapshot.d.ts b/core/anchor-pnpm/__snapshots__/tsnapi/index.snapshot.d.ts new file mode 100644 index 00000000..2e1afc9a --- /dev/null +++ b/core/anchor-pnpm/__snapshots__/tsnapi/index.snapshot.d.ts @@ -0,0 +1,22 @@ +/** + * Generated by tsnapi — public API snapshot of `@stephansama/anchor-pnpm` + */ +// #region Functions +export declare function add(_: Document, _: string, _: string): { + added: boolean; +}; +export declare function list(_: Document): AnchorEntry[]; +export declare function loadWorkspace(_: string): Promise; +export declare function remove(_: Document, _: string, _?: { + force?: boolean; +}): { + dangling: string[]; + removed: boolean; +}; +export declare function resolveWorkspacePath(_?: string): Promise; +export declare function saveWorkspace(_: string, _: Document): Promise; +export declare function sync(_: Document): SyncProposal[]; +export declare function update(_: Document, _: string, _: string): { + updated: boolean; +}; +// #endregion \ No newline at end of file diff --git a/core/anchor-pnpm/__snapshots__/tsnapi/index.snapshot.js b/core/anchor-pnpm/__snapshots__/tsnapi/index.snapshot.js new file mode 100644 index 00000000..9fcc0347 --- /dev/null +++ b/core/anchor-pnpm/__snapshots__/tsnapi/index.snapshot.js @@ -0,0 +1,13 @@ +/** + * Generated by tsnapi — public API snapshot of `@stephansama/anchor-pnpm` + */ +// #region Functions +export function add(_, _, _) {} +export function list(_) {} +export async function loadWorkspace(_) {} +export function remove(_, _, _) {} +export async function resolveWorkspacePath(_) {} +export async function saveWorkspace(_, _) {} +export function sync(_) {} +export function update(_, _, _) {} +// #endregion \ No newline at end of file diff --git a/core/anchor-pnpm/package.json b/core/anchor-pnpm/package.json new file mode 100644 index 00000000..d20f99c0 --- /dev/null +++ b/core/anchor-pnpm/package.json @@ -0,0 +1,64 @@ +{ + "name": "@stephansama/anchor-pnpm", + "version": "0.1.0", + "description": "Manage YAML anchor declarations in pnpm-workspace.yaml", + "keywords": [ + "anchor", + "anchor-pnpm", + "cli", + "pnpm", + "tanstack-intent", + "workspace", + "yaml" + ], + "homepage": "https://packages.stephansama.info/api/@stephansama/anchor-pnpm", + "repository": { + "type": "git", + "url": "git+https://github.com/stephansama/packages.git", + "directory": "core/anchor-pnpm" + }, + "license": "MIT", + "author": { + "name": "Stephan Randle", + "email": "stephanrandle.dev@gmail.com", + "url": "https://stephansama.info" + }, + "sideEffects": false, + "type": "module", + "exports": { + ".": "./dist/index.mjs", + "./cli": "./dist/cli.mjs", + "./package.json": "./package.json" + }, + "types": "./dist/index.d.mts", + "bin": { + "anchor-pnpm": "./dist/cli.mjs" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsdown", + "build:snapshot": "tsdown -u", + "dev": "tsdown --watch", + "lint": "eslint ./src/ --pass-on-no-patterns --no-error-on-unmatched-pattern", + "lint:fix": "eslint ./src/ --pass-on-no-patterns --no-error-on-unmatched-pattern --fix" + }, + "dependencies": { + "@manypkg/find-root": "catalog:manypkg", + "cleye": "catalog:cli", + "yaml": "catalog:", + "zod": "catalog:schema" + }, + "devDependencies": { + "tsdown": "catalog:" + }, + "packageManager": "pnpm@10.29.3", + "engines": { + "node": ">=24" + }, + "publishConfig": { + "access": "public", + "provenance": true + } +} diff --git a/core/anchor-pnpm/src/anchors.ts b/core/anchor-pnpm/src/anchors.ts new file mode 100644 index 00000000..8078c671 --- /dev/null +++ b/core/anchor-pnpm/src/anchors.ts @@ -0,0 +1,217 @@ +import { + Alias, + Document, + isAlias, + isMap, + isPair, + isScalar, + isSeq, + Scalar, + YAMLSeq, +} from "yaml"; + +const VERSIONS_KEY = "__versions"; +const CATALOGS_KEY = "catalogs"; + +export type AnchorEntry = { name: string; value: string }; + +export type SyncProposal = { + anchorName: string; + catalog: string; + keys: string[]; + version: string; +}; + +export function add( + document: Document, + name: string, + value: string, +): { added: boolean } { + const seq = ensureVersionsSeq(document); + if (findAnchor(seq, name)) return { added: false }; + const scalar = new Scalar(value); + scalar.anchor = name; + seq.add(scalar); + return { added: true }; +} + +export function applySyncProposal( + document: Document, + proposal: SyncProposal, +): void { + add(document, proposal.anchorName, proposal.version); + const catalogs = document.get(CATALOGS_KEY); + if (!isMap(catalogs)) return; + const group = catalogs.get(proposal.catalog); + if (!isMap(group)) return; + + for (const entry of group.items) { + if (!isPair(entry) || !isScalar(entry.key)) continue; + if (!proposal.keys.includes(String(entry.key.value))) continue; + entry.value = new Alias(proposal.anchorName); + } +} + +export function list(document: Document): AnchorEntry[] { + const seq = getVersionsSeq(document); + if (!seq) return []; + const entries: AnchorEntry[] = []; + for (const item of seq.items) { + if (!isScalar(item) || !item.anchor) continue; + entries.push({ name: item.anchor, value: String(item.value) }); + } + return entries; +} + +export function remove( + document: Document, + name: string, + options: { force?: boolean } = {}, +): { dangling: string[]; removed: boolean } { + const seq = getVersionsSeq(document); + if (!seq) return { dangling: [], removed: false }; + + const dangling = collectAliasPaths(document, name); + if (dangling.length > 0 && !options.force) { + return { dangling, removed: false }; + } + + if (dangling.length > 0 && options.force) { + const scalar = findAnchor(seq, name); + replaceAliasesWithLiteral(document, name, scalar?.value); + } + + const index = seq.items.findIndex( + (item) => isScalar(item) && item.anchor === name, + ); + if (index === -1) return { dangling: [], removed: false }; + seq.items.splice(index, 1); + return { dangling: [], removed: true }; +} + +export function sync(document: Document): SyncProposal[] { + const catalogs = document.get(CATALOGS_KEY); + if (!isMap(catalogs)) return []; + + const proposals: SyncProposal[] = []; + + for (const pair of catalogs.items) { + if (!isPair(pair) || !isScalar(pair.key) || !isMap(pair.value)) + continue; + const catalogName = String(pair.key.value); + const versionToKeys = new Map(); + + for (const entry of pair.value.items) { + if (!isPair(entry) || !isScalar(entry.key)) continue; + const value = entry.value; + if (!isScalar(value) || value.anchor) continue; + const versionString = String(value.value); + const keyString = String(entry.key.value); + versionToKeys.set(versionString, [ + ...(versionToKeys.get(versionString) ?? []), + keyString, + ]); + } + + for (const [version, keys] of versionToKeys) { + if (keys.length < 2) continue; + proposals.push({ + anchorName: catalogName, + catalog: catalogName, + keys, + version, + }); + } + } + + return proposals; +} + +export function update( + document: Document, + name: string, + value: string, +): { updated: boolean } { + const seq = getVersionsSeq(document); + if (!seq) return { updated: false }; + const scalar = findAnchor(seq, name); + if (!scalar) return { updated: false }; + scalar.value = value; + return { updated: true }; +} + +function collectAliasPaths(document: Document, name: string): string[] { + const paths: string[] = []; + function walk(node: unknown, trail: string[]): void { + if (isAlias(node)) { + if (node.source === name) paths.push(trail.join(".") || "(root)"); + return; + } + if (isMap(node)) { + for (const pair of node.items) { + if (!isPair(pair) || !isScalar(pair.key)) continue; + walk(pair.value, [...trail, String(pair.key.value)]); + } + return; + } + if (isSeq(node)) { + let index = 0; + for (const item of node.items) { + walk(item, [...trail, String(index++)]); + } + } + } + walk(document.contents, []); + return paths; +} + +function ensureVersionsSeq(document: Document): YAMLSeq { + const existing = getVersionsSeq(document); + if (existing) return existing; + const seq = new YAMLSeq(); + document.set(VERSIONS_KEY, seq); + return seq; +} + +function findAnchor(seq: YAMLSeq, name: string): Scalar | undefined { + for (const item of seq.items) { + if (isScalar(item) && item.anchor === name) return item; + } + return undefined; +} + +function getVersionsSeq(document: Document): undefined | YAMLSeq { + const node = document.get(VERSIONS_KEY); + return isSeq(node) ? node : undefined; +} + +function replaceAliasesWithLiteral( + document: Document, + name: string, + literal: unknown, +): void { + function visit(node: unknown): void { + if (isMap(node)) { + for (const pair of node.items) { + if (!isPair(pair)) continue; + if (isAlias(pair.value) && pair.value.source === name) { + pair.value = new Scalar(literal); + } else { + visit(pair.value); + } + } + return; + } + if (isSeq(node)) { + for (let index = 0; index < node.items.length; index++) { + const item = node.items[index]; + if (isAlias(item) && item.source === name) { + node.items[index] = new Scalar(literal); + } else { + visit(item); + } + } + } + } + visit(document.contents); +} diff --git a/core/anchor-pnpm/src/cli.ts b/core/anchor-pnpm/src/cli.ts new file mode 100644 index 00000000..397fc9b3 --- /dev/null +++ b/core/anchor-pnpm/src/cli.ts @@ -0,0 +1,192 @@ +#!/usr/bin/env node + +import { cli, command } from "cleye"; + +import { add, applySyncProposal, list, remove, sync, update } from "./anchors"; +import { + loadWorkspace, + resolveWorkspacePath, + saveWorkspace, +} from "./workspace"; + +type GlobalFlags = { + dryRun: boolean; + verbose: boolean; + workspace: string | undefined; +}; + +const globalFlags = { + dryRun: { + alias: "d", + default: false, + description: "Preview the rewritten YAML without writing", + type: Boolean, + }, + verbose: { + alias: "v", + default: false, + description: "Show detailed output", + type: Boolean, + }, + workspace: { + alias: "w", + description: "Path to pnpm-workspace.yaml (default: auto-detect)", + type: String, + }, +} as const; + +const listCommand = command( + { + flags: globalFlags, + name: "list", + }, + async (argv) => { + const filepath = await resolveWorkspacePath(argv.flags.workspace); + const document = await loadWorkspace(filepath); + const entries = list(document); + if (entries.length === 0) { + // eslint-disable-next-line no-console + console.log("(no anchors)"); + return; + } + const longest = Math.max(...entries.map((entry) => entry.name.length)); + for (const entry of entries) { + // eslint-disable-next-line no-console + console.log(` &${entry.name.padEnd(longest)} ${entry.value}`); + } + }, +); + +const addCommand = command( + { + flags: globalFlags, + name: "add", + parameters: ["", ""], + }, + async (argv) => { + const { name, version } = argv._; + const filepath = await resolveWorkspacePath(argv.flags.workspace); + const document = await loadWorkspace(filepath); + const result = add(document, name, version); + if (!result.added) { + throw new Error(`anchor &${name} already exists`); + } + await commit(filepath, document, argv.flags); + }, +); + +const updateCommand = command( + { + flags: globalFlags, + name: "update", + parameters: ["", ""], + }, + async (argv) => { + const { name, version } = argv._; + const filepath = await resolveWorkspacePath(argv.flags.workspace); + const document = await loadWorkspace(filepath); + const result = update(document, name, version); + if (!result.updated) { + throw new Error(`anchor &${name} not found in __versions`); + } + await commit(filepath, document, argv.flags); + }, +); + +const removeCommand = command( + { + flags: { + ...globalFlags, + force: { + alias: "f", + default: false, + description: "Inline alias references before removing", + type: Boolean, + }, + }, + name: "remove", + parameters: [""], + }, + async (argv) => { + const { name } = argv._; + const filepath = await resolveWorkspacePath(argv.flags.workspace); + const document = await loadWorkspace(filepath); + const result = remove(document, name, { force: argv.flags.force }); + if (!result.removed && result.dangling.length > 0) { + throw new Error( + `anchor &${name} still has alias references at: ${result.dangling.join(", ")}. Pass --force to inline them.`, + ); + } + if (!result.removed) { + throw new Error(`anchor &${name} not found`); + } + await commit(filepath, document, argv.flags); + }, +); + +const syncCommand = command( + { + flags: { + ...globalFlags, + apply: { + default: false, + description: "Apply all proposals (otherwise prints only)", + type: Boolean, + }, + }, + name: "sync", + }, + async (argv) => { + const filepath = await resolveWorkspacePath(argv.flags.workspace); + const document = await loadWorkspace(filepath); + const proposals = sync(document); + if (proposals.length === 0) { + // eslint-disable-next-line no-console + console.log("No catalog groups with repeated versions."); + return; + } + for (const proposal of proposals) { + // eslint-disable-next-line no-console + console.log( + `&${proposal.anchorName} = ${proposal.version} (catalog: ${proposal.catalog}, keys: ${proposal.keys.join(", ")})`, + ); + } + if (!argv.flags.apply) { + // eslint-disable-next-line no-console + console.log( + "\nDry run. Pass --apply to write these anchors + aliases.", + ); + return; + } + for (const proposal of proposals) applySyncProposal(document, proposal); + await commit(filepath, document, argv.flags); + }, +); + +void cli({ + commands: [ + listCommand, + addCommand, + updateCommand, + removeCommand, + syncCommand, + ], + name: "anchor-pnpm", +}); + +async function commit( + filepath: string, + document: Awaited>, + flags: GlobalFlags, +): Promise { + if (flags.dryRun) { + // eslint-disable-next-line no-console + console.log(document.toString()); + return; + } + await saveWorkspace(filepath, document); + if (flags.verbose) { + // eslint-disable-next-line no-console + console.log(`Wrote ${filepath}`); + } +} diff --git a/core/anchor-pnpm/src/index.ts b/core/anchor-pnpm/src/index.ts new file mode 100644 index 00000000..48932f8b --- /dev/null +++ b/core/anchor-pnpm/src/index.ts @@ -0,0 +1,6 @@ +export { add, list, remove, sync, update } from "./anchors"; +export { + loadWorkspace, + resolveWorkspacePath, + saveWorkspace, +} from "./workspace"; diff --git a/core/anchor-pnpm/src/workspace.ts b/core/anchor-pnpm/src/workspace.ts new file mode 100644 index 00000000..62a797aa --- /dev/null +++ b/core/anchor-pnpm/src/workspace.ts @@ -0,0 +1,26 @@ +import { findRoot } from "@manypkg/find-root"; +import * as fs from "node:fs/promises"; +import path from "node:path"; +import { Document, parseDocument } from "yaml"; + +export const WORKSPACE_FILE = "pnpm-workspace.yaml"; + +export async function loadWorkspace( + filepath: string, +): Promise { + const source = await fs.readFile(filepath, "utf8"); + return parseDocument(source); +} + +export async function resolveWorkspacePath(override?: string): Promise { + if (override) return path.resolve(override); + const { rootDir } = await findRoot(process.cwd()); + return path.join(rootDir, WORKSPACE_FILE); +} + +export async function saveWorkspace( + filepath: string, + document: Document, +): Promise { + await fs.writeFile(filepath, document.toString(), "utf8"); +} diff --git a/core/anchor-pnpm/test/anchors.test.ts b/core/anchor-pnpm/test/anchors.test.ts new file mode 100644 index 00000000..5d058c04 --- /dev/null +++ b/core/anchor-pnpm/test/anchors.test.ts @@ -0,0 +1,101 @@ +import { describe, expect, it } from "vitest"; +import { parseDocument } from "yaml"; + +import { + add, + applySyncProposal, + list, + remove, + sync, + update, +} from "../src/anchors"; + +const SAMPLE = `__versions: + - &vitest 4.1.0 + +catalogs: + vitest: + vitest: *vitest + "@vitest/ui": *vitest + alpine: + alpinejs: 3.15.8 + "@alpinejs/focus": 3.15.8 +`; + +describe("anchors", () => { + it("lists existing anchor entries", () => { + const document = parseDocument(SAMPLE); + expect(list(document)).toEqual([{ name: "vitest", value: "4.1.0" }]); + }); + + it("adds a new anchor to __versions", () => { + const document = parseDocument(SAMPLE); + const result = add(document, "alpine", "3.15.8"); + expect(result.added).toBe(true); + expect(list(document)).toContainEqual({ + name: "alpine", + value: "3.15.8", + }); + }); + + it("returns added=false when the anchor already exists", () => { + const document = parseDocument(SAMPLE); + expect(add(document, "vitest", "4.2.0").added).toBe(false); + }); + + it("updates the scalar value of an existing anchor", () => { + const document = parseDocument(SAMPLE); + const result = update(document, "vitest", "4.2.0"); + expect(result.updated).toBe(true); + expect(document.toString()).toContain("&vitest 4.2.0"); + }); + + it("refuses to remove an anchor with dangling aliases", () => { + const document = parseDocument(SAMPLE); + const result = remove(document, "vitest"); + expect(result.removed).toBe(false); + expect(result.dangling.length).toBeGreaterThan(0); + }); + + it("force-removes by inlining aliases with literal values", () => { + const document = parseDocument(SAMPLE); + const result = remove(document, "vitest", { force: true }); + expect(result.removed).toBe(true); + const output = document.toString(); + expect(output).not.toContain("&vitest"); + expect(output).not.toContain("*vitest"); + expect(output).toContain("vitest: 4.1.0"); + }); + + it("detects repeated catalog versions via sync", () => { + const document = parseDocument(SAMPLE); + const proposals = sync(document); + expect(proposals).toEqual([ + expect.objectContaining({ + anchorName: "alpine", + catalog: "alpine", + version: "3.15.8", + }), + ]); + }); + + it("applySyncProposal writes the anchor and replaces literals with aliases", () => { + const document = parseDocument(SAMPLE); + applySyncProposal(document, { + anchorName: "alpine", + catalog: "alpine", + keys: ["alpinejs", "@alpinejs/focus"], + version: "3.15.8", + }); + const output = document.toString(); + expect(output).toContain("&alpine 3.15.8"); + expect(output).toContain("alpinejs: *alpine"); + }); + + it("round-trips: serializing a document with existing aliases preserves them", () => { + const document = parseDocument(SAMPLE); + const output = document.toString(); + expect(output).toContain("&vitest"); + expect(output).toContain("*vitest"); + }); +}); diff --git a/core/anchor-pnpm/tsconfig.json b/core/anchor-pnpm/tsconfig.json new file mode 100644 index 00000000..79101018 --- /dev/null +++ b/core/anchor-pnpm/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": ["../../tsconfig.base.json"], + "include": ["./src/**/*", "tsdown.config.ts"], + "compilerOptions": { + "composite": true + } +} diff --git a/core/anchor-pnpm/tsdown.config.ts b/core/anchor-pnpm/tsdown.config.ts new file mode 100644 index 00000000..3384ab49 --- /dev/null +++ b/core/anchor-pnpm/tsdown.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "tsdown"; +import ApiSnapshot from "tsnapi/rolldown"; + +export default defineConfig({ + attw: { profile: "esm-only" }, + deps: { skipNodeModulesBundle: true }, + dts: true, + entry: { + cli: "./src/cli.ts", + index: "./src/index.ts", + }, + exports: { bin: true, enabled: true }, + format: "esm", + plugins: [ApiSnapshot()], + target: "esnext", +}); diff --git a/core/anchor-pnpm/vitest.config.ts b/core/anchor-pnpm/vitest.config.ts new file mode 100644 index 00000000..77330a54 --- /dev/null +++ b/core/anchor-pnpm/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: true, + include: ["test/**/*.test.ts"], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39a0cdbc..726413bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -793,6 +793,25 @@ importers: specifier: 'catalog:' version: 2.1.0 + core/anchor-pnpm: + dependencies: + '@manypkg/find-root': + specifier: catalog:manypkg + version: 3.1.0 + cleye: + specifier: catalog:cli + version: 2.6.0 + yaml: + specifier: 'catalog:' + version: 2.8.2 + zod: + specifier: catalog:schema + version: 4.2.1 + devDependencies: + tsdown: + specifier: 'catalog:' + version: 0.21.10(@arethetypeswrong/core@0.18.2)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3) + core/astro-iconify-svgmap: devDependencies: '@iconify/types':