dslinter 0.0.12 → 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,165 +0,0 @@
1
- /**
2
- * Best-effort download of the prebuilt `dslinter` CLI for this platform.
3
- * Used by postinstall and on first `dslinter` / `npx dslinter` invocation.
4
- */
5
- import { readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
6
- import { chmod, mkdir, stat } from "node:fs/promises";
7
- import { dirname, join } from "node:path";
8
- import { fileURLToPath } from "node:url";
9
- import {
10
- fetchReleasesForVersion,
11
- pickReleaseAsset,
12
- } from "./github-release.mjs";
13
- import {
14
- githubRepoFromPackage,
15
- releaseAssetCandidateNames,
16
- vendorBinaryPath,
17
- } from "./resolve-dslint-binary.mjs";
18
-
19
- const __dirname = dirname(fileURLToPath(import.meta.url));
20
- const defaultPackageRoot = join(__dirname, "..");
21
-
22
- async function pathExists(p) {
23
- try {
24
- await stat(p);
25
- return true;
26
- } catch {
27
- return false;
28
- }
29
- }
30
-
31
- function readPackageVersion(packageRoot) {
32
- const pkg = JSON.parse(
33
- readFileSync(join(packageRoot, "package.json"), "utf8"),
34
- );
35
- return pkg.version;
36
- }
37
-
38
- function releaseRepo(packageRoot) {
39
- const override = process.env.DSLINT_GITHUB_REPO?.trim();
40
- if (override) return override;
41
- return githubRepoFromPackage(packageRoot);
42
- }
43
-
44
- /**
45
- * @param {string} packageRoot
46
- * @param {{ quiet?: boolean }} [opts]
47
- * @returns {Promise<boolean>} true if vendored binary exists after this call
48
- */
49
- export async function ensureDslintBinary(packageRoot = defaultPackageRoot, opts = {}) {
50
- const { quiet = false } = opts;
51
- const log = quiet ? () => {} : console.warn.bind(console);
52
- const verbose = process.env.DSLINT_VERBOSE === "1";
53
-
54
- if (process.env.DSLINT_SKIP_DOWNLOAD === "1") {
55
- return pathExists(vendorBinaryPath(packageRoot));
56
- }
57
-
58
- const dest = vendorBinaryPath(packageRoot);
59
- if (await pathExists(dest)) return true;
60
-
61
- const candidates = releaseAssetCandidateNames();
62
- if (candidates.length === 0) {
63
- log(
64
- `[dslinter] No prebuilt scanner for ${process.platform}-${process.arch}.`,
65
- );
66
- return false;
67
- }
68
-
69
- const version = readPackageVersion(packageRoot);
70
- const repo = releaseRepo(packageRoot);
71
- const vendorDir = join(packageRoot, "vendor");
72
- await mkdir(vendorDir, { recursive: true });
73
- const tmp = `${dest}.part`;
74
-
75
- let releases;
76
- try {
77
- releases = await fetchReleasesForVersion(repo, version);
78
- } catch (err) {
79
- log(
80
- `[dslinter] Could not query GitHub releases for ${repo}: ${err instanceof Error ? err.message : err}`,
81
- );
82
- if (process.env.GITHUB_TOKEN || process.env.GH_TOKEN) {
83
- log(" (token was set but request still failed — check repo access)");
84
- } else {
85
- log(
86
- " For private repos, set GITHUB_TOKEN or GH_TOKEN with read access to releases.",
87
- );
88
- }
89
- return false;
90
- }
91
-
92
- if (releases.length === 0) {
93
- log(
94
- `[dslinter] No GitHub release found on ${repo} for v${version}.\n` +
95
- ` Publish tag v${version} with workflow release-dslint-binaries.yml (assets: ${candidates.join(" or ")}).`,
96
- );
97
- return false;
98
- }
99
-
100
- for (const release of releases) {
101
- const asset = pickReleaseAsset(release, candidates);
102
- if (!asset) {
103
- if (verbose) {
104
- log(
105
- `[dslinter] Release ${release.tag_name} has no asset in ${candidates.join(", ")} (has: ${release.assets.map((a) => a.name).join(", ") || "none"})`,
106
- );
107
- }
108
- continue;
109
- }
110
-
111
- if (verbose) {
112
- log(`[dslinter] Downloading ${asset.name} from ${release.tag_name}…`);
113
- }
114
-
115
- try {
116
- const res = await fetch(asset.browser_download_url, { redirect: "follow" });
117
- if (!res.ok) {
118
- log(`[dslinter] Download failed (${res.status}): ${asset.browser_download_url}`);
119
- continue;
120
- }
121
-
122
- const buf = Buffer.from(await res.arrayBuffer());
123
- writeFileSync(tmp, buf);
124
- try {
125
- if (await pathExists(dest)) unlinkSync(dest);
126
- } catch {
127
- /* ignore */
128
- }
129
- renameSync(tmp, dest);
130
- if (process.platform !== "win32") {
131
- await chmod(dest, 0o755);
132
- }
133
- if (release.tag_name !== `v${version}` && !quiet) {
134
- log(
135
- `[dslinter] Installed scanner from release ${release.tag_name} (npm v${version}).`,
136
- );
137
- }
138
- return true;
139
- } catch (err) {
140
- log(
141
- `[dslinter] Could not download ${asset.name}: ${err instanceof Error ? err.message : err}`,
142
- );
143
- try {
144
- unlinkSync(tmp);
145
- } catch {
146
- /* ignore */
147
- }
148
- }
149
- }
150
-
151
- log(
152
- `[dslinter] Found releases on ${repo} but none include ${candidates.join(" or ")} for this platform.\n` +
153
- ` Upload platform binaries via .github/workflows/release-dslint-binaries.yml, or set DSLINT_BIN.`,
154
- );
155
- return false;
156
- }
157
-
158
- const isMain =
159
- process.argv[1] &&
160
- fileURLToPath(import.meta.url) === process.argv[1];
161
-
162
- if (isMain) {
163
- await ensureDslintBinary();
164
- process.exit(0);
165
- }
@@ -1,109 +0,0 @@
1
- /**
2
- * Resolve GitHub release assets via the REST API (works with exact names + legacy names).
3
- */
4
-
5
- const API = "https://api.github.com";
6
-
7
- function apiHeaders() {
8
- const headers = {
9
- Accept: "application/vnd.github+json",
10
- "User-Agent": "dslinter-npm",
11
- "X-GitHub-Api-Version": "2022-11-28",
12
- };
13
- const token =
14
- process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim();
15
- if (token) headers.Authorization = `Bearer ${token}`;
16
- return headers;
17
- }
18
-
19
- /**
20
- * @param {string} repo `owner/name`
21
- * @param {string} path
22
- */
23
- async function githubGet(repo, path) {
24
- const res = await fetch(`${API}/repos/${repo}${path}`, {
25
- headers: apiHeaders(),
26
- redirect: "follow",
27
- });
28
- if (res.status === 404) return null;
29
- if (!res.ok) {
30
- const body = await res.text().catch(() => "");
31
- throw new Error(`GitHub API ${res.status} for ${path}: ${body.slice(0, 200)}`);
32
- }
33
- return res.json();
34
- }
35
-
36
- /**
37
- * @param {string} repo
38
- * @param {string} tag e.g. `v0.0.11` or `latest`
39
- */
40
- export async function fetchRelease(repo, tag) {
41
- if (tag === "latest") {
42
- return githubGet(repo, "/releases/latest");
43
- }
44
- const byTag = await githubGet(repo, `/releases/tags/${encodeURIComponent(tag)}`);
45
- if (byTag) return byTag;
46
- // Some releases use tag without `v` prefix.
47
- if (tag.startsWith("v")) {
48
- return githubGet(repo, `/releases/tags/${encodeURIComponent(tag.slice(1))}`);
49
- }
50
- return null;
51
- }
52
-
53
- /**
54
- * @param {string} repo
55
- * @param {string} npmVersion e.g. `0.0.11`
56
- */
57
- export async function fetchReleasesForVersion(repo, npmVersion) {
58
- const out = [];
59
- const seen = new Set();
60
-
61
- const push = (release) => {
62
- if (!release?.assets?.length) return;
63
- const key = release.id ?? release.tag_name;
64
- if (seen.has(key)) return;
65
- seen.add(key);
66
- out.push(release);
67
- };
68
-
69
- push(await fetchRelease(repo, `v${npmVersion}`));
70
- push(await fetchRelease(repo, npmVersion));
71
- push(await fetchRelease(repo, "latest"));
72
-
73
- if (out.length > 0) return out;
74
-
75
- const list = await githubGet(repo, "/releases?per_page=30");
76
- if (!Array.isArray(list)) return out;
77
-
78
- for (const release of list) {
79
- const tag = String(release.tag_name ?? "");
80
- if (tag === `v${npmVersion}` || tag === npmVersion) {
81
- push(release);
82
- }
83
- }
84
- if (out.length > 0) return out;
85
-
86
- // Newest release that has any scanner-looking asset.
87
- for (const release of list) {
88
- const names = release.assets?.map((a) => a.name) ?? [];
89
- if (names.some((n) => n.startsWith("dslinter-") || n.startsWith("dslint-"))) {
90
- push(release);
91
- break;
92
- }
93
- }
94
-
95
- return out;
96
- }
97
-
98
- /**
99
- * @param {string[]} candidateNames
100
- * @param {{ assets: { name: string; browser_download_url: string }[] }} release
101
- * @returns {{ name: string; browser_download_url: string } | null}
102
- */
103
- export function pickReleaseAsset(release, candidateNames) {
104
- for (const name of candidateNames) {
105
- const asset = release.assets.find((a) => a.name === name);
106
- if (asset?.browser_download_url) return asset;
107
- }
108
- return null;
109
- }
@@ -1,27 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
-
5
- const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
6
- const version = JSON.parse(
7
- readFileSync(join(packageRoot, "package.json"), "utf8"),
8
- ).version;
9
-
10
- process.stderr.write(`dslinter: scanner binary not available.
11
-
12
- This npm package is NOT the same as \`cargo install dslint\` on crates.io (that is a
13
- different "design file" linter and will crash or misbehave).
14
-
15
- To run the design-system scanner:
16
-
17
- 1. Re-run after a GitHub release exists for v${version} (prebuilt download), or
18
- 2. Build from this repo and point at it:
19
- cargo install --git https://github.com/jrmybtlr/DSLinter dslinter --locked
20
- export DSLINT_BIN="$(command -v dslinter)"
21
- npx dslinter ...
22
- 3. Or set DSLINT_BIN to your local target/release/dslinter
23
-
24
- Releases: https://github.com/jrmybtlr/DSLinter/releases
25
-
26
- Tip: re-run with DSLINT_VERBOSE=1 to see which GitHub releases/assets were tried.
27
- `);
@@ -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,101 +0,0 @@
1
- import { join } from "node:path";
2
- import { describe, expect, it } from "vitest";
3
- import {
4
- pickReleaseAsset,
5
- } from "../scripts/github-release.mjs";
6
- import {
7
- CLI_BINARY_NAME,
8
- DEFAULT_GITHUB_REPO,
9
- parseGitHubRepo,
10
- releaseAssetBaseName,
11
- releaseAssetCandidateNames,
12
- vendorBinaryPath,
13
- } from "../scripts/resolve-dslint-binary.mjs";
14
-
15
- function proc(platform: string, arch: string): NodeJS.Process {
16
- return { platform, arch } as NodeJS.Process;
17
- }
18
-
19
- describe("parseGitHubRepo", () => {
20
- it("parses https repository url", () => {
21
- expect(
22
- parseGitHubRepo("https://github.com/jrmybtlr/DSLinter.git"),
23
- ).toBe("jrmybtlr/DSLinter");
24
- });
25
-
26
- it("parses repository object", () => {
27
- expect(
28
- parseGitHubRepo({
29
- type: "git",
30
- url: "git+https://github.com/jrmybtlr/DSLinter.git",
31
- }),
32
- ).toBe("jrmybtlr/DSLinter");
33
- });
34
-
35
- it("defaults constant points at DSLinter", () => {
36
- expect(DEFAULT_GITHUB_REPO).toBe("jrmybtlr/DSLinter");
37
- });
38
- });
39
-
40
- describe("releaseAssetCandidateNames", () => {
41
- it("includes legacy dslint asset name", () => {
42
- expect(releaseAssetCandidateNames(proc("darwin", "arm64"))).toEqual([
43
- "dslinter-aarch64-apple-darwin",
44
- "dslint-aarch64-apple-darwin",
45
- ]);
46
- });
47
- });
48
-
49
- describe("pickReleaseAsset", () => {
50
- it("prefers primary name then legacy", () => {
51
- const release = {
52
- assets: [
53
- {
54
- name: "dslint-aarch64-apple-darwin",
55
- browser_download_url: "https://example.com/legacy",
56
- },
57
- ],
58
- };
59
- expect(
60
- pickReleaseAsset(release, releaseAssetCandidateNames(proc("darwin", "arm64"))),
61
- ).toMatchObject({ name: "dslint-aarch64-apple-darwin" });
62
- });
63
- });
64
-
65
- describe("releaseAssetBaseName", () => {
66
- it("maps darwin arm64", () => {
67
- expect(releaseAssetBaseName(proc("darwin", "arm64"))).toBe(
68
- "dslinter-aarch64-apple-darwin",
69
- );
70
- });
71
-
72
- it("maps linux x64", () => {
73
- expect(releaseAssetBaseName(proc("linux", "x64"))).toBe(
74
- "dslinter-x86_64-unknown-linux-gnu",
75
- );
76
- });
77
-
78
- it("maps win32 x64", () => {
79
- expect(releaseAssetBaseName(proc("win32", "x64"))).toBe(
80
- "dslinter-x86_64-pc-windows-msvc.exe",
81
- );
82
- });
83
-
84
- it("returns null for unknown", () => {
85
- expect(releaseAssetBaseName(proc("freebsd", "x64"))).toBeNull();
86
- });
87
- });
88
-
89
- describe("vendorBinaryPath", () => {
90
- it("uses dslinter.exe on Windows", () => {
91
- expect(vendorBinaryPath(join("/", "pkg"), proc("win32", "x64"))).toBe(
92
- join("/", "pkg", "vendor", `${CLI_BINARY_NAME}.exe`),
93
- );
94
- });
95
-
96
- it("uses dslinter on Unix", () => {
97
- expect(vendorBinaryPath(join("/", "pkg"), proc("linux", "x64"))).toBe(
98
- join("/", "pkg", "vendor", CLI_BINARY_NAME),
99
- );
100
- });
101
- });