dslinter 0.0.8 → 0.0.10

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.0.10
4
+
5
+ [compare changes](https://github.com/jrmybtlr/DSLint/compare/v0.0.9...v0.0.10)
6
+
7
+ ### 💅 Refactors
8
+
9
+ - Rename dslint to dslinter and update related configurations ([9f15898](https://github.com/jrmybtlr/DSLint/commit/9f15898))
10
+
11
+ ### ❤️ Contributors
12
+
13
+ - Jeremy Butler <jeremy.butler@laravel.com>
14
+
3
15
  ## v0.0.8
4
16
 
5
17
  [compare changes](https://github.com/jrmybtlr/DSLint/compare/v0.0.7...v0.0.8)
@@ -53,7 +65,6 @@
53
65
 
54
66
  ## v0.0.2
55
67
 
56
-
57
68
  ### 🚀 Enhancements
58
69
 
59
70
  - Add DSLint MVP — JSX/Vue scan, usage rollup, governance rules ([4d64b76](https://github.com/jrmybtlr/DSLint/commit/4d64b76))
@@ -93,4 +104,3 @@
93
104
 
94
105
  - Jeremy Butler <jeremy.butler@laravel.com>
95
106
  - Cursor Agent ([@cursoragent](https://github.com/cursoragent))
96
-
package/README.md CHANGED
@@ -39,29 +39,34 @@ Environment variables:
39
39
 
40
40
  **`oxlint`** on npm ships **Node native addons** as **`optionalDependencies`** (`@oxlint/binding-darwin-arm64`, …) built with **napi-rs** — each package is a small prebuilt library loaded by Node.
41
41
 
42
- **`dslint`** is a **standalone executable**. The practical pattern here is **download on install** from **GitHub Releases** (similar in spirit to tools that pull a platform binary once), instead of publishing dozens of `@dslinter/binding-*` packages.
42
+ **`dslinter`** is a **standalone executable**. The practical pattern here is **download on install** from **GitHub Releases** (similar in spirit to tools that pull a platform binary once), instead of publishing dozens of `@dslinter/binding-*` packages.
43
+
44
+ ### Do not `cargo install dslint`
45
+
46
+ The crates.io crate **`dslint`** (v0.0.x) is a **different project** (design-file linting). It is **not** this design-system scanner. Installing it will break `npx dslinter` if it ends up on your `PATH`.
47
+
48
+ Use **`cargo install --git https://github.com/jrmybtlr/DSLint dslinter --locked`** or set **`DSLINT_BIN`** to a local `target/release/dslinter` build.
43
49
 
44
50
  ### If there is no matching release asset yet
45
51
 
46
- You’ll see a **warning** during install (install still succeeds) and **`dslinter`** falls back to **`dslint` on your PATH**, or prints install hints if missing:
52
+ You’ll see a **warning** during install (install still succeeds). **`dslinter`** will try to download on first run; if no GitHub release exists yet, you get a clear error (not a silent fallback to the wrong `dslint` on crates.io):
47
53
 
48
54
  ```bash
49
55
  npx dslinter /path/to/repo --json -o dslint-report.json
50
56
  ```
51
57
 
52
- | Distribution | How users get `dslint` |
58
+ | Distribution | How users get the scanner |
53
59
  |--------------|-------------------------|
54
- | **npm + GitHub Releases** | Default: postinstall download when release `vX.Y.Z` includes your platform asset. |
55
- | **GitHub Releases** | Manual download of `dslint-*` from the release; add to `PATH`. |
56
- | **crates.io** | `cargo install dslint` once published. |
60
+ | **npm + GitHub Releases** | Default: download when release `vX.Y.Z` includes your platform asset. |
61
+ | **GitHub Releases** | Manual download of `dslinter-*` from the release; run directly or set `DSLINT_BIN`. |
62
+ | **From source** | `cargo install --git https://github.com/jrmybtlr/DSLint dslinter --locked` (not `cargo install dslint`). |
57
63
 
58
64
  Typical usage:
59
65
 
60
66
  ```bash
61
- dslint /path/to/repo --json -o dslint-report.json
62
- # or --serve for live reload while developing a dashboard
63
- # or equivalently (vendored or PATH):
64
67
  dslinter /path/to/repo --json -o dslint-report.json
68
+ # or --serve for live reload while developing a dashboard
69
+ # (npm `npx dslinter` runs the same binary)
65
70
  ```
66
71
 
67
72
  ## Styles (Tailwind v4)
package/bin/dslinter.mjs CHANGED
@@ -1,37 +1,70 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Runs the `dslint` scanner: prefers a vendored binary from postinstall, else `dslint` on PATH.
3
+ * Runs the design-system scanner (`dslinter` binary from GitHub Releases or DSLINT_BIN).
4
+ * Does NOT use bare `dslint` on PATH — that name is a different crate on crates.io.
4
5
  */
5
6
  import { spawnSync } from "node:child_process";
6
7
  import { existsSync } from "node:fs";
7
8
  import { dirname, join } from "node:path";
8
9
  import { fileURLToPath } from "node:url";
9
- import { vendorBinaryPath } from "../scripts/resolve-dslint-binary.mjs";
10
+ import { ensureDslintBinary } from "../scripts/ensure-dslint.mjs";
11
+ import {
12
+ SCANNER_VERSION_MARKER,
13
+ vendorBinaryPath,
14
+ } from "../scripts/resolve-dslint-binary.mjs";
10
15
 
11
16
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
17
  const packageRoot = join(__dirname, "..");
13
18
 
14
19
  const args = process.argv.slice(2);
15
20
 
16
- const vendored = vendorBinaryPath(packageRoot);
17
- const cmd = vendored && existsSync(vendored) ? vendored : "dslint";
18
- const child = spawnSync(cmd, args, { stdio: "inherit" });
21
+ function isOurScanner(binary) {
22
+ const help = spawnSync(binary, ["--help"], { encoding: "utf8" });
23
+ const out = `${help.stdout ?? ""}${help.stderr ?? ""}`;
24
+ return out.includes(SCANNER_VERSION_MARKER);
25
+ }
19
26
 
20
- if (child.error && "code" in child.error && child.error.code === "ENOENT") {
21
- process.stderr.write(`dslint: command not found.
27
+ async function resolveCommand() {
28
+ const fromEnv = process.env.DSLINT_BIN?.trim();
29
+ if (fromEnv) {
30
+ if (!existsSync(fromEnv)) {
31
+ process.stderr.write(`dslinter: DSLINT_BIN not found: ${fromEnv}\n`);
32
+ process.exit(127);
33
+ }
34
+ if (!isOurScanner(fromEnv)) {
35
+ process.stderr.write(
36
+ `dslinter: DSLINT_BIN does not look like the DSLint design-system scanner.\n` +
37
+ ` Expected output containing "${SCANNER_VERSION_MARKER}".\n`,
38
+ );
39
+ process.exit(1);
40
+ }
41
+ return fromEnv;
42
+ }
43
+
44
+ const vendored = vendorBinaryPath(packageRoot);
45
+ if (!existsSync(vendored)) {
46
+ await ensureDslintBinary(packageRoot, { quiet: true });
47
+ }
48
+ if (existsSync(vendored)) {
49
+ return vendored;
50
+ }
22
51
 
23
- The dslinter package can download a prebuilt dslint on npm install when a matching
24
- GitHub release exists (same tag as this package version, e.g. v0.0.6). See:
25
- https://github.com/jrmybtlr/DSLint/releases
52
+ if (process.env.DSLINT_ALLOW_PATH === "1") {
53
+ const onPath = spawnSync("dslinter", ["--version"], { encoding: "utf8" });
54
+ if (onPath.status === 0 && `${onPath.stdout}`.includes(SCANNER_VERSION_MARKER)) {
55
+ return "dslinter";
56
+ }
57
+ }
26
58
 
27
- Otherwise install dslint on PATH:
59
+ await import("../scripts/print-missing-scanner.mjs");
60
+ process.exit(127);
61
+ }
28
62
 
29
- cargo install dslint
30
- # or from this repo:
31
- cargo install --path .
63
+ const cmd = await resolveCommand();
64
+ const child = spawnSync(cmd, args, { stdio: "inherit" });
32
65
 
33
- Skip auto-download (air-gapped): DSLINT_SKIP_DOWNLOAD=1
34
- `);
66
+ if (child.error && "code" in child.error && child.error.code === "ENOENT") {
67
+ process.stderr.write(`dslinter: failed to execute ${cmd}\n`);
35
68
  process.exit(127);
36
69
  }
37
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dslinter",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "DSLinter dashboard UI: playground shell, token wall, and governance panels (consumes dslint-report.json).",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,16 +1,18 @@
1
1
  /**
2
- * Best-effort download of the prebuilt `dslint` CLI for this platform.
3
- * Runs on `npm install dslinter` (postinstall). Exits 0 even when the binary
4
- * is missing (no release yet / offline) so installs never fail.
2
+ * Best-effort download of the prebuilt `dslinter` CLI for this platform.
3
+ * Used by postinstall and on first `dslinter` / `npx dslinter` invocation.
5
4
  */
6
5
  import { readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
7
6
  import { chmod, mkdir, stat } from "node:fs/promises";
8
7
  import { dirname, join } from "node:path";
9
8
  import { fileURLToPath } from "node:url";
10
- import { releaseAssetBaseName, vendorBinaryPath } from "./resolve-dslint-binary.mjs";
9
+ import {
10
+ releaseAssetBaseName,
11
+ vendorBinaryPath,
12
+ } from "./resolve-dslint-binary.mjs";
11
13
 
12
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
- const packageRoot = join(__dirname, "..");
15
+ const defaultPackageRoot = join(__dirname, "..");
14
16
 
15
17
  async function pathExists(p) {
16
18
  try {
@@ -21,8 +23,10 @@ async function pathExists(p) {
21
23
  }
22
24
  }
23
25
 
24
- function readPackageVersion() {
25
- const pkg = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
26
+ function readPackageVersion(packageRoot) {
27
+ const pkg = JSON.parse(
28
+ readFileSync(join(packageRoot, "package.json"), "utf8"),
29
+ );
26
30
  return pkg.version;
27
31
  }
28
32
 
@@ -32,65 +36,99 @@ function releaseTag(version) {
32
36
  return `v${version}`;
33
37
  }
34
38
 
35
- async function main() {
36
- if (process.env.DSLINT_SKIP_DOWNLOAD === "1") return;
39
+ function releaseRepo() {
40
+ return process.env.DSLINT_GITHUB_REPO?.trim() || "jrmybtlr/DSLint";
41
+ }
42
+
43
+ function assetUrl(tag, asset) {
44
+ return `https://github.com/${releaseRepo()}/releases/download/${tag}/${asset}`;
45
+ }
46
+
47
+ /**
48
+ * @param {string} packageRoot
49
+ * @param {{ quiet?: boolean }} [opts]
50
+ * @returns {Promise<boolean>} true if vendored binary exists after this call
51
+ */
52
+ export async function ensureDslintBinary(packageRoot = defaultPackageRoot, opts = {}) {
53
+ const { quiet = false } = opts;
54
+ const log = quiet ? () => {} : console.warn.bind(console);
55
+
56
+ if (process.env.DSLINT_SKIP_DOWNLOAD === "1") {
57
+ return pathExists(vendorBinaryPath(packageRoot));
58
+ }
37
59
 
38
60
  const dest = vendorBinaryPath(packageRoot);
39
- if (await pathExists(dest)) return;
61
+ if (await pathExists(dest)) return true;
40
62
 
41
63
  const asset = releaseAssetBaseName();
42
64
  if (!asset) {
43
- console.warn(
44
- `[dslinter] No prebuilt dslint for ${process.platform}-${process.arch}. Install Rust and put dslint on PATH, or set DSLINT_SKIP_DOWNLOAD=1.`,
65
+ log(
66
+ `[dslinter] No prebuilt scanner for ${process.platform}-${process.arch}.`,
45
67
  );
46
- return;
68
+ return false;
47
69
  }
48
70
 
49
- const version = readPackageVersion();
50
- const tag = releaseTag(version);
51
- const repo = process.env.DSLINT_GITHUB_REPO?.trim() || "jrmybtlr/DSLint";
52
- const url = `https://github.com/${repo}/releases/download/${tag}/${asset}`;
71
+ const version = readPackageVersion(packageRoot);
72
+ const tagsToTry = [releaseTag(version)];
73
+ if (process.env.DSLINT_USE_LATEST_RELEASE !== "0") {
74
+ tagsToTry.push("latest");
75
+ }
53
76
 
54
77
  const vendorDir = join(packageRoot, "vendor");
55
78
  await mkdir(vendorDir, { recursive: true });
56
-
57
79
  const tmp = `${dest}.part`;
58
80
 
59
- try {
60
- const res = await fetch(url, { redirect: "follow" });
61
- if (res.status === 404) {
62
- console.warn(
63
- `[dslinter] No GitHub release asset at ${url}\n` +
64
- ` Create release ${tag} with ${asset} (see .github/workflows/release-dslint-binaries.yml), or install dslint via cargo / PATH.`,
65
- );
66
- return;
67
- }
68
- if (!res.ok) {
69
- console.warn(`[dslinter] Download failed (${res.status}): ${url}`);
70
- return;
71
- }
72
-
73
- const buf = Buffer.from(await res.arrayBuffer());
74
- writeFileSync(tmp, buf);
75
-
81
+ for (const tag of tagsToTry) {
82
+ const url = assetUrl(tag, asset);
76
83
  try {
77
- if (await pathExists(dest)) unlinkSync(dest);
78
- } catch {
79
- /* ignore */
80
- }
81
- renameSync(tmp, dest);
84
+ const res = await fetch(url, { redirect: "follow" });
85
+ if (res.status === 404) continue;
86
+ if (!res.ok) {
87
+ log(`[dslinter] Download failed (${res.status}): ${url}`);
88
+ continue;
89
+ }
82
90
 
83
- if (process.platform !== "win32") {
84
- await chmod(dest, 0o755);
85
- }
86
- } catch (err) {
87
- console.warn(`[dslinter] Could not download dslint: ${err instanceof Error ? err.message : err}`);
88
- try {
89
- unlinkSync(tmp);
90
- } catch {
91
- /* ignore */
91
+ const buf = Buffer.from(await res.arrayBuffer());
92
+ writeFileSync(tmp, buf);
93
+ try {
94
+ if (await pathExists(dest)) unlinkSync(dest);
95
+ } catch {
96
+ /* ignore */
97
+ }
98
+ renameSync(tmp, dest);
99
+ if (process.platform !== "win32") {
100
+ await chmod(dest, 0o755);
101
+ }
102
+ if (tag === "latest" && !quiet) {
103
+ log(
104
+ `[dslinter] Installed scanner from latest GitHub release (no asset for npm v${version}).`,
105
+ );
106
+ }
107
+ return true;
108
+ } catch (err) {
109
+ log(
110
+ `[dslinter] Could not download: ${err instanceof Error ? err.message : err}`,
111
+ );
112
+ try {
113
+ unlinkSync(tmp);
114
+ } catch {
115
+ /* ignore */
116
+ }
92
117
  }
93
118
  }
119
+
120
+ log(
121
+ `[dslinter] No GitHub release with asset "${asset}" (tried ${tagsToTry.join(", ")}).\n` +
122
+ ` Publish tag ${releaseTag(version)} with workflow release-dslint-binaries.yml, or set DSLINT_BIN to a local build.`,
123
+ );
124
+ return false;
94
125
  }
95
126
 
96
- await main();
127
+ const isMain =
128
+ process.argv[1] &&
129
+ fileURLToPath(import.meta.url) === process.argv[1];
130
+
131
+ if (isMain) {
132
+ const ok = await ensureDslintBinary();
133
+ process.exit(ok ? 0 : 0);
134
+ }
@@ -0,0 +1,25 @@
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/DSLint 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/DSLint/releases
25
+ `);
@@ -1,5 +1,8 @@
1
1
  import { join } from "node:path";
2
2
 
3
+ /** CLI binary name (avoids collision with unrelated `dslint` on crates.io). */
4
+ export const CLI_BINARY_NAME = "dslinter";
5
+
3
6
  /**
4
7
  * Maps the current OS/arch to the GitHub release asset basename (must match CI upload names).
5
8
  * @param {NodeJS.Process} [proc]
@@ -7,11 +10,21 @@ import { join } from "node:path";
7
10
  */
8
11
  export function releaseAssetBaseName(proc = process) {
9
12
  const { platform, arch } = proc;
10
- if (platform === "darwin" && arch === "arm64") return "dslint-aarch64-apple-darwin";
11
- if (platform === "darwin" && arch === "x64") return "dslint-x86_64-apple-darwin";
12
- if (platform === "linux" && arch === "x64") return "dslint-x86_64-unknown-linux-gnu";
13
- if (platform === "linux" && arch === "arm64") return "dslint-aarch64-unknown-linux-gnu";
14
- if (platform === "win32" && arch === "x64") return "dslint-x86_64-pc-windows-msvc.exe";
13
+ if (platform === "darwin" && arch === "arm64") {
14
+ return "dslinter-aarch64-apple-darwin";
15
+ }
16
+ if (platform === "darwin" && arch === "x64") {
17
+ return "dslinter-x86_64-apple-darwin";
18
+ }
19
+ if (platform === "linux" && arch === "x64") {
20
+ return "dslinter-x86_64-unknown-linux-gnu";
21
+ }
22
+ if (platform === "linux" && arch === "arm64") {
23
+ return "dslinter-aarch64-unknown-linux-gnu";
24
+ }
25
+ if (platform === "win32" && arch === "x64") {
26
+ return "dslinter-x86_64-pc-windows-msvc.exe";
27
+ }
15
28
  return null;
16
29
  }
17
30
 
@@ -20,6 +33,10 @@ export function releaseAssetBaseName(proc = process) {
20
33
  * @param {NodeJS.Process} [proc]
21
34
  */
22
35
  export function vendorBinaryPath(packageRoot, proc = process) {
23
- const name = proc.platform === "win32" ? "dslint.exe" : "dslint";
36
+ const name =
37
+ proc.platform === "win32" ? `${CLI_BINARY_NAME}.exe` : CLI_BINARY_NAME;
24
38
  return join(packageRoot, "vendor", name);
25
39
  }
40
+
41
+ /** Our scanner prints this in `dslinter --version` help output (clap about line). */
42
+ export const SCANNER_VERSION_MARKER = "design system linting";
@@ -10,7 +10,7 @@ import {
10
10
  } from "@/components/ui/command";
11
11
 
12
12
  import type { PlaygroundEntry } from "../types/playground";
13
- import type { HashRoute } from "./hashRoute";
13
+ import type { HashRoute } from "../shell/hashRoute";
14
14
 
15
15
  type Props = {
16
16
  entries: PlaygroundEntry[];
@@ -3,8 +3,8 @@ import { IconMoon, IconSearch, IconSun } from "@/components/icons";
3
3
  import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
4
4
 
5
5
  import type { PlaygroundEntry } from "../types/playground";
6
- import type { HashRoute } from "./hashRoute";
7
- import type { DashboardThemePreference } from "./DashboardLayout";
6
+ import type { HashRoute } from "../shell/hashRoute";
7
+ import type { DashboardThemePreference } from "../shell/DashboardLayout";
8
8
 
9
9
  type Props = {
10
10
  entries: PlaygroundEntry[];
@@ -10,7 +10,7 @@ import {
10
10
  import type { UsageLocation, WorkspaceReport } from "../types/report";
11
11
  import { usageMap } from "./aggregate";
12
12
  import { shortPath } from "./paths";
13
- import { EmptyCard } from "../shell/EmptyCard";
13
+ import { EmptyCard } from "../components/EmptyCard";
14
14
  import { InlineCode } from "@/components/InlineCode";
15
15
 
16
16
  function formatCallSiteProps(loc: UsageLocation): string {
@@ -1,4 +1,4 @@
1
- import { Section } from "../shell/Section";
1
+ import { Section } from "../components/Section";
2
2
  import {
3
3
  Table,
4
4
  TableBody,
@@ -1,6 +1,7 @@
1
1
  import { join } from "node:path";
2
2
  import { describe, expect, it } from "vitest";
3
3
  import {
4
+ CLI_BINARY_NAME,
4
5
  releaseAssetBaseName,
5
6
  vendorBinaryPath,
6
7
  } from "../scripts/resolve-dslint-binary.mjs";
@@ -12,19 +13,19 @@ function proc(platform: string, arch: string): NodeJS.Process {
12
13
  describe("releaseAssetBaseName", () => {
13
14
  it("maps darwin arm64", () => {
14
15
  expect(releaseAssetBaseName(proc("darwin", "arm64"))).toBe(
15
- "dslint-aarch64-apple-darwin",
16
+ "dslinter-aarch64-apple-darwin",
16
17
  );
17
18
  });
18
19
 
19
20
  it("maps linux x64", () => {
20
21
  expect(releaseAssetBaseName(proc("linux", "x64"))).toBe(
21
- "dslint-x86_64-unknown-linux-gnu",
22
+ "dslinter-x86_64-unknown-linux-gnu",
22
23
  );
23
24
  });
24
25
 
25
26
  it("maps win32 x64", () => {
26
27
  expect(releaseAssetBaseName(proc("win32", "x64"))).toBe(
27
- "dslint-x86_64-pc-windows-msvc.exe",
28
+ "dslinter-x86_64-pc-windows-msvc.exe",
28
29
  );
29
30
  });
30
31
 
@@ -34,15 +35,15 @@ describe("releaseAssetBaseName", () => {
34
35
  });
35
36
 
36
37
  describe("vendorBinaryPath", () => {
37
- it("uses dslint.exe on Windows", () => {
38
+ it("uses dslinter.exe on Windows", () => {
38
39
  expect(vendorBinaryPath(join("/", "pkg"), proc("win32", "x64"))).toBe(
39
- join("/", "pkg", "vendor", "dslint.exe"),
40
+ join("/", "pkg", "vendor", `${CLI_BINARY_NAME}.exe`),
40
41
  );
41
42
  });
42
43
 
43
- it("uses dslint on Unix", () => {
44
+ it("uses dslinter on Unix", () => {
44
45
  expect(vendorBinaryPath(join("/", "pkg"), proc("linux", "x64"))).toBe(
45
- join("/", "pkg", "vendor", "dslint"),
46
+ join("/", "pkg", "vendor", CLI_BINARY_NAME),
46
47
  );
47
48
  });
48
49
  });
@@ -12,11 +12,11 @@ import type { TokenCatalog } from "../types/tokenCatalog";
12
12
  import type { DslinterReportState } from "../dashboard/useWorkspaceReport";
13
13
  import { Button } from "@/components/ui/button";
14
14
  import { cn } from "../lib/utils";
15
- import { ComponentPlaygroundPane } from "./ComponentPlaygroundPane";
16
- import { GovernancePane } from "./GovernancePane";
17
- import { Sidebar } from "./Sidebar";
18
- import { TokensPane } from "./TokensPane";
19
- import { DashboardCommandPalette } from "./DashboardCommandPalette";
15
+ import { ComponentPlaygroundPane } from "../components/ComponentPlaygroundPane";
16
+ import { GovernancePane } from "../components/GovernancePane";
17
+ import { Sidebar } from "../components/Sidebar";
18
+ import { TokensPane } from "../components/TokensPane";
19
+ import { DashboardCommandPalette } from "../components/DashboardCommandPalette";
20
20
  import { useHashRoute } from "./useHashRoute";
21
21
 
22
22
  const STORAGE_KEY = "dslinter-dashboard-theme";
File without changes
File without changes
File without changes
File without changes
File without changes