dslinter 0.0.16 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,5 @@
1
+ import type { ReactNode } from "react";
2
+
1
3
  export function Section({
2
4
  id,
3
5
  children,
@@ -6,10 +8,10 @@ export function Section({
6
8
  actions,
7
9
  }: {
8
10
  id: string;
9
- children: React.ReactNode;
11
+ children: ReactNode;
10
12
  title: string;
11
13
  description: string;
12
- actions?: React.ReactNode;
14
+ actions?: ReactNode;
13
15
  }) {
14
16
  return (
15
17
  <section id={id} className="scroll-mt-20">
@@ -6,15 +6,6 @@ import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
6
6
  const THEME = "github-dark";
7
7
  const LANG = "tsx";
8
8
 
9
- /** When true, run Twoslash (CDN ATA + hover) before highlighting. */
10
- export function usageSnippetNeedsTwoslash(source: string): boolean {
11
- return (
12
- /\^\?/.test(source) ||
13
- /\/\/\s*@(?:errors|error|log|warn|filename|noErrors)/m.test(source) ||
14
- /\/\/\s*---cut---/.test(source)
15
- );
16
- }
17
-
18
9
  let highlighterPromise: Promise<HighlighterCore> | null = null;
19
10
 
20
11
  function getHighlighter(): Promise<HighlighterCore> {
@@ -32,22 +23,12 @@ function assertNotAborted(signal: AbortSignal | undefined): void {
32
23
  if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
33
24
  }
34
25
 
35
- /**
36
- * Renders usage source to Shiki HTML. Twoslash runs only when {@link usageSnippetNeedsTwoslash} is true
37
- * (loaded in a separate async chunk so the default bundle stays smaller).
38
- */
26
+ /** Renders usage source to Shiki HTML. */
39
27
  export async function renderPlaygroundUsageHtml(
40
28
  source: string,
41
29
  signal?: AbortSignal,
42
30
  ): Promise<string> {
43
31
  const highlighter = await getHighlighter();
44
32
  assertNotAborted(signal);
45
-
46
- if (!usageSnippetNeedsTwoslash(source)) {
47
- return highlighter.codeToHtml(source, { lang: LANG, theme: THEME });
48
- }
49
-
50
- const { renderUsageWithTwoslash } = await import("./playgroundUsageTwoslash");
51
- assertNotAborted(signal);
52
- return renderUsageWithTwoslash(highlighter, source, signal);
33
+ return highlighter.codeToHtml(source, { lang: LANG, theme: THEME });
53
34
  }
@@ -1,187 +0,0 @@
1
- /**
2
- * Best-effort download of the prebuilt `dslinter` CLI for this platform.
3
- */
4
- import { readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
5
- import { chmod, mkdir, stat } from "node:fs/promises";
6
- import { dirname, join } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- import {
9
- fetchReleasesForVersion,
10
- githubAuthToken,
11
- pickReleaseAsset,
12
- tryDirectAssetDownload,
13
- } from "./github-release.mjs";
14
- import {
15
- githubRepoFromPackage,
16
- releaseAssetCandidateNames,
17
- vendorBinaryPath,
18
- } from "./resolve-dslint-binary.mjs";
19
-
20
- const __dirname = dirname(fileURLToPath(import.meta.url));
21
- const defaultPackageRoot = join(__dirname, "..");
22
-
23
- async function pathExists(p) {
24
- try {
25
- await stat(p);
26
- return true;
27
- } catch {
28
- return false;
29
- }
30
- }
31
-
32
- function readPackageVersion(packageRoot) {
33
- const pkg = JSON.parse(
34
- readFileSync(join(packageRoot, "package.json"), "utf8"),
35
- );
36
- return pkg.version;
37
- }
38
-
39
- function releaseRepo(packageRoot) {
40
- const override = process.env.DSLINT_GITHUB_REPO?.trim();
41
- if (override) return override;
42
- return githubRepoFromPackage(packageRoot);
43
- }
44
-
45
- /**
46
- * @param {string} dest
47
- * @param {{ buf: Buffer }} payload
48
- */
49
- async function writeVendorBinary(dest, { buf }) {
50
- const tmp = `${dest}.part`;
51
- writeFileSync(tmp, buf);
52
- try {
53
- if (await pathExists(dest)) unlinkSync(dest);
54
- } catch {
55
- /* ignore */
56
- }
57
- renameSync(tmp, dest);
58
- if (process.platform !== "win32") {
59
- await chmod(dest, 0o755);
60
- }
61
- }
62
-
63
- /**
64
- * @param {string} packageRoot
65
- * @param {{ quiet?: boolean }} [opts]
66
- */
67
- export async function ensureDslintBinary(packageRoot = defaultPackageRoot, opts = {}) {
68
- const { quiet = false } = opts;
69
- const log = quiet ? () => {} : console.warn.bind(console);
70
-
71
- if (process.env.DSLINT_SKIP_DOWNLOAD === "1") {
72
- return pathExists(vendorBinaryPath(packageRoot));
73
- }
74
-
75
- const dest = vendorBinaryPath(packageRoot);
76
- if (await pathExists(dest)) return true;
77
-
78
- const candidates = releaseAssetCandidateNames();
79
- if (candidates.length === 0) {
80
- log(
81
- `[dslinter] No prebuilt scanner for ${process.platform}-${process.arch}.`,
82
- );
83
- return false;
84
- }
85
-
86
- const version = readPackageVersion(packageRoot);
87
- const repo = releaseRepo(packageRoot);
88
- await mkdir(join(packageRoot, "vendor"), { recursive: true });
89
-
90
- const direct = await tryDirectAssetDownload(repo, version, candidates, log);
91
- if (direct) {
92
- await writeVendorBinary(dest, direct);
93
- if (direct.tag !== `v${version}` && !quiet) {
94
- log(
95
- `[dslinter] Installed ${direct.name} from release ${direct.tag} (npm v${version}).`,
96
- );
97
- }
98
- return true;
99
- }
100
-
101
- let apiDenied = false;
102
- let tagExistsWithoutBinaries = null;
103
- try {
104
- const {
105
- releases,
106
- apiDenied: denied,
107
- apiError,
108
- tagExistsWithoutBinaries: emptyTag,
109
- } = await fetchReleasesForVersion(repo, version);
110
- apiDenied = denied;
111
- tagExistsWithoutBinaries = emptyTag ?? null;
112
-
113
- if (apiError) {
114
- log(
115
- `[dslinter] GitHub API error: ${apiError.message}`,
116
- );
117
- }
118
-
119
- for (const release of releases) {
120
- const asset = pickReleaseAsset(release, candidates);
121
- if (!asset) continue;
122
-
123
- const headers = {};
124
- const token = githubAuthToken();
125
- if (token) headers.Authorization = `Bearer ${token}`;
126
-
127
- const res = await fetch(asset.browser_download_url, {
128
- headers,
129
- redirect: "follow",
130
- });
131
- if (!res.ok) continue;
132
-
133
- const buf = Buffer.from(await res.arrayBuffer());
134
- await writeVendorBinary(dest, { buf });
135
- if (release.tag_name !== `v${version}` && !quiet) {
136
- log(
137
- `[dslinter] Installed ${asset.name} from release ${release.tag_name} (npm v${version}).`,
138
- );
139
- }
140
- return true;
141
- }
142
- } catch (err) {
143
- log(
144
- `[dslinter] GitHub API: ${err instanceof Error ? err.message : err}`,
145
- );
146
- }
147
-
148
- const tag = `v${version}`;
149
- const releasePage = `https://github.com/${repo}/releases/tag/${tag}`;
150
-
151
- if (tagExistsWithoutBinaries) {
152
- log(
153
- `[dslinter] GitHub release ${tagExistsWithoutBinaries} exists but has no scanner binaries attached.\n` +
154
- ` Run the "Release dslinter binaries" workflow on ${repo} (Actions → workflow_dispatch, tag ${tagExistsWithoutBinaries}),\n` +
155
- ` or push a new tag so CI uploads assets like ${candidates[0]}.\n` +
156
- ` ${releasePage}`,
157
- );
158
- return false;
159
- }
160
-
161
- if (apiDenied || !githubAuthToken()) {
162
- log(
163
- `[dslinter] Cannot reach ${repo} releases without authentication (GitHub returned 404).\n` +
164
- ` If you can open ${releasePage} in a browser, the repo is likely private.\n` +
165
- ` Create a fine-grained or classic token with repo read access, then:\n` +
166
- ` export GITHUB_TOKEN=ghp_...\n` +
167
- ` npx dslinter\n` +
168
- ` Or install from source: cargo install --git https://github.com/${repo} dslinter --locked`,
169
- );
170
- return false;
171
- }
172
-
173
- log(
174
- `[dslinter] No release with asset ${candidates.join(" or ")} on ${repo} for ${tag}.\n` +
175
- ` Create ${releasePage} and run release-dslint-binaries.yml, or set DSLINT_BIN.`,
176
- );
177
- return false;
178
- }
179
-
180
- const isMain =
181
- process.argv[1] &&
182
- fileURLToPath(import.meta.url) === process.argv[1];
183
-
184
- if (isMain) {
185
- await ensureDslintBinary();
186
- process.exit(0);
187
- }
@@ -1,219 +0,0 @@
1
- /**
2
- * Resolve and download GitHub release assets (API + direct URLs; supports private repos with a token).
3
- */
4
-
5
- const API = "https://api.github.com";
6
-
7
- export function githubAuthToken() {
8
- return (
9
- process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || null
10
- );
11
- }
12
-
13
- export function downloadAuthHeaders() {
14
- const token = githubAuthToken();
15
- return token ? { Authorization: `Bearer ${token}` } : {};
16
- }
17
-
18
- function apiHeaders() {
19
- return {
20
- Accept: "application/vnd.github+json",
21
- "User-Agent": "dslinter-npm",
22
- "X-GitHub-Api-Version": "2022-11-28",
23
- ...downloadAuthHeaders(),
24
- };
25
- }
26
-
27
- /**
28
- * @param {string} repo `owner/name`
29
- * @param {string} tag `v0.0.12` or `latest`
30
- * @param {string} assetName
31
- */
32
- export function directAssetUrl(repo, tag, assetName) {
33
- const file = encodeURIComponent(assetName);
34
- if (tag === "latest") {
35
- return `https://github.com/${repo}/releases/latest/download/${file}`;
36
- }
37
- return `https://github.com/${repo}/releases/download/${encodeURIComponent(tag)}/${file}`;
38
- }
39
-
40
- /**
41
- * @param {string} npmVersion
42
- */
43
- export function releaseTagsToTry(npmVersion) {
44
- const override = process.env.DSLINT_RELEASE_TAG?.trim();
45
- if (override) {
46
- return [override.startsWith("v") ? override : `v${override}`];
47
- }
48
- return [`v${npmVersion}`, npmVersion, "latest"];
49
- }
50
-
51
- /**
52
- * @param {string} repo
53
- * @param {string} path
54
- * @returns {Promise<{ data: unknown } | { notFound: true } | { error: Error }>}
55
- */
56
- async function githubGet(repo, path) {
57
- const res = await fetch(`${API}/repos/${repo}${path}`, {
58
- headers: apiHeaders(),
59
- redirect: "follow",
60
- });
61
- if (res.status === 404) return { notFound: true };
62
- if (!res.ok) {
63
- const body = await res.text().catch(() => "");
64
- return {
65
- error: new Error(`GitHub API ${res.status} for ${path}: ${body.slice(0, 200)}`),
66
- };
67
- }
68
- return { data: await res.json() };
69
- }
70
-
71
- /**
72
- * @param {string} repo
73
- * @param {string} tag e.g. `v0.0.12` or `latest`
74
- */
75
- export async function fetchRelease(repo, tag) {
76
- const path =
77
- tag === "latest" ? "/releases/latest" : `/releases/tags/${encodeURIComponent(tag)}`;
78
- let result = await githubGet(repo, path);
79
- if (result.notFound && tag.startsWith("v")) {
80
- result = await githubGet(
81
- repo,
82
- `/releases/tags/${encodeURIComponent(tag.slice(1))}`,
83
- );
84
- }
85
- if ("data" in result) return result.data;
86
- return null;
87
- }
88
-
89
- /**
90
- * @param {string} repo
91
- * @param {string} npmVersion
92
- */
93
- /** @param {unknown} release */
94
- function hasScannerAssets(release) {
95
- const names = release?.assets?.map((a) => a.name) ?? [];
96
- return names.some((n) => n.startsWith("dslinter-") || n.startsWith("dslint-"));
97
- }
98
-
99
- export async function fetchReleasesForVersion(repo, npmVersion) {
100
- const out = [];
101
- const seen = new Set();
102
- /** @type {string | null} */
103
- let tagExistsWithoutBinaries = null;
104
-
105
- const consider = (release) => {
106
- if (!release?.tag_name) return;
107
- const tag = String(release.tag_name);
108
- const matches =
109
- tag === `v${npmVersion}` || tag === npmVersion;
110
- if (matches && !hasScannerAssets(release)) {
111
- tagExistsWithoutBinaries = tag;
112
- }
113
- };
114
-
115
- const push = (release) => {
116
- if (!hasScannerAssets(release)) return;
117
- const key = release.id ?? release.tag_name;
118
- if (seen.has(key)) return;
119
- seen.add(key);
120
- out.push(release);
121
- };
122
-
123
- const vRelease = await fetchRelease(repo, `v${npmVersion}`);
124
- consider(vRelease);
125
- push(vRelease);
126
-
127
- const bareRelease = await fetchRelease(repo, npmVersion);
128
- consider(bareRelease);
129
- push(bareRelease);
130
-
131
- const latestRelease = await fetchRelease(repo, "latest");
132
- push(latestRelease);
133
-
134
- if (out.length > 0) {
135
- return { releases: out, apiDenied: false, tagExistsWithoutBinaries };
136
- }
137
-
138
- const listResult = await githubGet(repo, "/releases?per_page=30");
139
- if ("error" in listResult) {
140
- return { releases: out, apiDenied: false, apiError: listResult.error };
141
- }
142
- if (listResult.notFound) {
143
- return { releases: out, apiDenied: !githubAuthToken() };
144
- }
145
-
146
- const list = listResult.data;
147
- if (!Array.isArray(list)) return { releases: out, apiDenied: false };
148
-
149
- for (const release of list) {
150
- const tag = String(release.tag_name ?? "");
151
- if (tag === `v${npmVersion}` || tag === npmVersion) {
152
- push(release);
153
- }
154
- }
155
- if (out.length > 0) {
156
- return { releases: out, apiDenied: false, tagExistsWithoutBinaries };
157
- }
158
-
159
- for (const release of list) {
160
- const names = release.assets?.map((a) => a.name) ?? [];
161
- if (names.some((n) => n.startsWith("dslinter-") || n.startsWith("dslint-"))) {
162
- push(release);
163
- break;
164
- }
165
- }
166
-
167
- return { releases: out, apiDenied: false, tagExistsWithoutBinaries };
168
- }
169
-
170
- /**
171
- * @param {string[]} candidateNames
172
- * @param {{ assets: { name: string; browser_download_url: string }[] }} release
173
- */
174
- export function pickReleaseAsset(release, candidateNames) {
175
- for (const name of candidateNames) {
176
- const asset = release.assets.find((a) => a.name === name);
177
- if (asset?.browser_download_url) return asset;
178
- }
179
- return null;
180
- }
181
-
182
- /**
183
- * Download via public/private release URLs (no API metadata required).
184
- * @param {(msg: string) => void} log
185
- */
186
- export async function tryDirectAssetDownload(repo, npmVersion, candidateNames, log) {
187
- const verbose = process.env.DSLINT_VERBOSE === "1";
188
- const headers = downloadAuthHeaders();
189
- const tags = releaseTagsToTry(npmVersion);
190
-
191
- for (const tag of tags) {
192
- for (const name of candidateNames) {
193
- const url = directAssetUrl(repo, tag, name);
194
- if (verbose) log(`[dslinter] GET ${url}`);
195
- try {
196
- const res = await fetch(url, { headers, redirect: "follow" });
197
- if (res.status === 404) continue;
198
- if (!res.ok) {
199
- if (verbose) log(`[dslinter] ${res.status} ${url}`);
200
- continue;
201
- }
202
- const contentType = res.headers.get("content-type") ?? "";
203
- if (contentType.includes("text/html")) continue;
204
-
205
- const buf = Buffer.from(await res.arrayBuffer());
206
- if (buf.length < 512) continue;
207
-
208
- return { buf, tag, name, url };
209
- } catch (err) {
210
- if (verbose) {
211
- log(
212
- `[dslinter] ${url}: ${err instanceof Error ? err.message : err}`,
213
- );
214
- }
215
- }
216
- }
217
- }
218
- return null;
219
- }
@@ -1,32 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { githubRepoFromPackage } from "./resolve-dslint-binary.mjs";
5
-
6
- const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
7
- const version = JSON.parse(
8
- readFileSync(join(packageRoot, "package.json"), "utf8"),
9
- ).version;
10
- const repo = githubRepoFromPackage(packageRoot);
11
-
12
- process.stderr.write(`dslinter: scanner binary not available.
13
-
14
- Do NOT run: cargo install dslint
15
- That installs a different package on crates.io (design-file linter).
16
-
17
- Install the design-system scanner from this repo instead:
18
-
19
- cargo install --git https://github.com/${repo} dslinter --locked
20
- export DSLINT_BIN="$(command -v dslinter)"
21
- npx dslinter ...
22
-
23
- If releases exist at https://github.com/${repo}/releases/tag/v${version} but
24
- download failed, the repo may be private — set a GitHub token then retry:
25
-
26
- export GITHUB_TOKEN=ghp_... # needs read access to ${repo}
27
- npx dslinter
28
-
29
- Or point at a local build: DSLINT_BIN=/path/to/dslinter
30
-
31
- Tip: DSLINT_VERBOSE=1 shows each download URL tried.
32
- `);
@@ -1,80 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
-
4
- /** CLI binary name (avoids collision with unrelated `dslint` on crates.io). */
5
- export const CLI_BINARY_NAME = "dslinter";
6
-
7
- /** Fallback when package.json has no parseable `repository` field. */
8
- export const DEFAULT_GITHUB_REPO = "jrmybtlr/DSLinter";
9
-
10
- /**
11
- * @param {string | { type?: string; url?: string } | undefined} repository
12
- * @returns {string | null} `owner/repo`
13
- */
14
- export function parseGitHubRepo(repository) {
15
- if (!repository) return null;
16
- const url = typeof repository === "string" ? repository : repository.url;
17
- if (!url) return null;
18
- const m = String(url).match(/github\.com[/:]([^/]+)\/([^/.]+?)(?:\.git)?\/?$/i);
19
- return m ? `${m[1]}/${m[2]}` : null;
20
- }
21
-
22
- /**
23
- * @param {string} packageRoot
24
- */
25
- export function githubRepoFromPackage(packageRoot) {
26
- try {
27
- const pkg = JSON.parse(
28
- readFileSync(join(packageRoot, "package.json"), "utf8"),
29
- );
30
- return parseGitHubRepo(pkg.repository) ?? DEFAULT_GITHUB_REPO;
31
- } catch {
32
- return DEFAULT_GITHUB_REPO;
33
- }
34
- }
35
-
36
- /**
37
- * Maps the current OS/arch to the GitHub release asset basename (must match CI upload names).
38
- * @param {NodeJS.Process} [proc]
39
- * @returns {string | null}
40
- */
41
- export function releaseAssetBaseName(proc = process) {
42
- const { platform, arch } = proc;
43
- if (platform === "darwin" && arch === "arm64") {
44
- return "dslinter-aarch64-apple-darwin";
45
- }
46
- if (platform === "darwin" && arch === "x64") {
47
- return "dslinter-x86_64-apple-darwin";
48
- }
49
- if (platform === "linux" && arch === "x64") {
50
- return "dslinter-x86_64-unknown-linux-gnu";
51
- }
52
- if (platform === "linux" && arch === "arm64") {
53
- return "dslinter-aarch64-unknown-linux-gnu";
54
- }
55
- if (platform === "win32" && arch === "x64") {
56
- return "dslinter-x86_64-pc-windows-msvc.exe";
57
- }
58
- return null;
59
- }
60
-
61
- /** Primary GitHub asset name plus legacy `dslint-*` names from older releases. */
62
- export function releaseAssetCandidateNames(proc = process) {
63
- const primary = releaseAssetBaseName(proc);
64
- if (!primary) return [];
65
- const legacy = primary.replace(/^dslinter/, "dslint");
66
- return legacy === primary ? [primary] : [primary, legacy];
67
- }
68
-
69
- /**
70
- * @param {string} packageRoot — directory containing package.json
71
- * @param {NodeJS.Process} [proc]
72
- */
73
- export function vendorBinaryPath(packageRoot, proc = process) {
74
- const name =
75
- proc.platform === "win32" ? `${CLI_BINARY_NAME}.exe` : CLI_BINARY_NAME;
76
- return join(packageRoot, "vendor", name);
77
- }
78
-
79
- /** Our scanner prints this in `dslinter --version` help output (clap about line). */
80
- export const SCANNER_VERSION_MARKER = "design system linting";
@@ -1,127 +0,0 @@
1
- import { join } from "node:path";
2
- import { describe, expect, it } from "vitest";
3
- import {
4
- directAssetUrl,
5
- pickReleaseAsset,
6
- releaseTagsToTry,
7
- } from "../scripts/github-release.mjs";
8
- import {
9
- CLI_BINARY_NAME,
10
- DEFAULT_GITHUB_REPO,
11
- parseGitHubRepo,
12
- releaseAssetBaseName,
13
- releaseAssetCandidateNames,
14
- vendorBinaryPath,
15
- } from "../scripts/resolve-dslint-binary.mjs";
16
-
17
- function proc(platform: string, arch: string): NodeJS.Process {
18
- return { platform, arch } as NodeJS.Process;
19
- }
20
-
21
- describe("parseGitHubRepo", () => {
22
- it("parses https repository url", () => {
23
- expect(
24
- parseGitHubRepo("https://github.com/jrmybtlr/DSLinter.git"),
25
- ).toBe("jrmybtlr/DSLinter");
26
- });
27
-
28
- it("parses repository object", () => {
29
- expect(
30
- parseGitHubRepo({
31
- type: "git",
32
- url: "git+https://github.com/jrmybtlr/DSLinter.git",
33
- }),
34
- ).toBe("jrmybtlr/DSLinter");
35
- });
36
-
37
- it("defaults constant points at DSLinter", () => {
38
- expect(DEFAULT_GITHUB_REPO).toBe("jrmybtlr/DSLinter");
39
- });
40
- });
41
-
42
- describe("directAssetUrl", () => {
43
- it("uses releases/latest/download for latest tag", () => {
44
- expect(
45
- directAssetUrl("jrmybtlr/DSLinter", "latest", "dslinter-aarch64-apple-darwin"),
46
- ).toBe(
47
- "https://github.com/jrmybtlr/DSLinter/releases/latest/download/dslinter-aarch64-apple-darwin",
48
- );
49
- });
50
-
51
- it("uses releases/download for version tags", () => {
52
- expect(
53
- directAssetUrl("jrmybtlr/DSLinter", "v0.0.12", "dslinter-aarch64-apple-darwin"),
54
- ).toBe(
55
- "https://github.com/jrmybtlr/DSLinter/releases/download/v0.0.12/dslinter-aarch64-apple-darwin",
56
- );
57
- });
58
- });
59
-
60
- describe("releaseTagsToTry", () => {
61
- it("tries v-prefixed tag first", () => {
62
- expect(releaseTagsToTry("0.0.12")).toEqual(["v0.0.12", "0.0.12", "latest"]);
63
- });
64
- });
65
-
66
- describe("releaseAssetCandidateNames", () => {
67
- it("includes legacy dslint asset name", () => {
68
- expect(releaseAssetCandidateNames(proc("darwin", "arm64"))).toEqual([
69
- "dslinter-aarch64-apple-darwin",
70
- "dslint-aarch64-apple-darwin",
71
- ]);
72
- });
73
- });
74
-
75
- describe("pickReleaseAsset", () => {
76
- it("prefers primary name then legacy", () => {
77
- const release = {
78
- assets: [
79
- {
80
- name: "dslint-aarch64-apple-darwin",
81
- browser_download_url: "https://example.com/legacy",
82
- },
83
- ],
84
- };
85
- expect(
86
- pickReleaseAsset(release, releaseAssetCandidateNames(proc("darwin", "arm64"))),
87
- ).toMatchObject({ name: "dslint-aarch64-apple-darwin" });
88
- });
89
- });
90
-
91
- describe("releaseAssetBaseName", () => {
92
- it("maps darwin arm64", () => {
93
- expect(releaseAssetBaseName(proc("darwin", "arm64"))).toBe(
94
- "dslinter-aarch64-apple-darwin",
95
- );
96
- });
97
-
98
- it("maps linux x64", () => {
99
- expect(releaseAssetBaseName(proc("linux", "x64"))).toBe(
100
- "dslinter-x86_64-unknown-linux-gnu",
101
- );
102
- });
103
-
104
- it("maps win32 x64", () => {
105
- expect(releaseAssetBaseName(proc("win32", "x64"))).toBe(
106
- "dslinter-x86_64-pc-windows-msvc.exe",
107
- );
108
- });
109
-
110
- it("returns null for unknown", () => {
111
- expect(releaseAssetBaseName(proc("freebsd", "x64"))).toBeNull();
112
- });
113
- });
114
-
115
- describe("vendorBinaryPath", () => {
116
- it("uses dslinter.exe on Windows", () => {
117
- expect(vendorBinaryPath(join("/", "pkg"), proc("win32", "x64"))).toBe(
118
- join("/", "pkg", "vendor", `${CLI_BINARY_NAME}.exe`),
119
- );
120
- });
121
-
122
- it("uses dslinter on Unix", () => {
123
- expect(vendorBinaryPath(join("/", "pkg"), proc("linux", "x64"))).toBe(
124
- join("/", "pkg", "vendor", CLI_BINARY_NAME),
125
- );
126
- });
127
- });