better-design 0.35.2 → 0.35.5

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/dist/index.js CHANGED
@@ -178,8 +178,8 @@ function appSupport() {
178
178
  return configHome();
179
179
  }
180
180
  }
181
- function dirExists(...segments) {
182
- return existsSync(path.join(...segments));
181
+ function dirExists(...segments2) {
182
+ return existsSync(path.join(...segments2));
183
183
  }
184
184
  function onPath(bin) {
185
185
  const dirs = (process.env.PATH ?? "").split(path.delimiter);
@@ -28889,9 +28889,9 @@ var init_schema3 = __esm({
28889
28889
  });
28890
28890
 
28891
28891
  // ../shared/src/mcp/feature-map/build.ts
28892
- function toRoute(segments) {
28892
+ function toRoute(segments2) {
28893
28893
  const out = [];
28894
- for (const seg of segments) {
28894
+ for (const seg of segments2) {
28895
28895
  if (/^\(.*\)$/.test(seg) || seg.startsWith("@")) continue;
28896
28896
  if (seg.startsWith("_")) return null;
28897
28897
  const param = /^\[{1,2}(?:\.\.\.)?([^\]]+)\]{1,2}$/.exec(seg);
@@ -29613,7 +29613,7 @@ var MCP_IMPLEMENTATION_VERSION;
29613
29613
  var init_version2 = __esm({
29614
29614
  "../shared/src/mcp/version.ts"() {
29615
29615
  "use strict";
29616
- MCP_IMPLEMENTATION_VERSION = "0.35.2";
29616
+ MCP_IMPLEMENTATION_VERSION = "0.35.5";
29617
29617
  }
29618
29618
  });
29619
29619
 
@@ -30550,7 +30550,10 @@ function maskTemplateImplementation(content, isMdx) {
30550
30550
  const mask = (value) => value.replace(/[^\r\n]/g, " ");
30551
30551
  let visible = content.replace(/^---\s*$[\s\S]*?^---\s*$/m, mask);
30552
30552
  if (isMdx) visible = maskMdxEsm(visible, mask);
30553
- visible = visible.replace(/<!--[\s\S]*?-->|<(script|style)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, mask);
30553
+ visible = visible.replace(
30554
+ /<!--[\s\S]*?-->|<(script|style|pre|code|blockquote)\b[^>]*>[\s\S]*?<\/\1\s*>/gi,
30555
+ mask
30556
+ );
30554
30557
  visible = visible.replace(/\/\*[\s\S]*?\*\//g, mask);
30555
30558
  visible = maskTags(visible, mask);
30556
30559
  visible = visible.replace(/\{\{([\s\S]*?)\}\}/g, (value, expression) => mask(value.slice(0, 2)) + revealRenderedExpression(expression, mask) + mask(value.slice(-2)));
@@ -30634,6 +30637,70 @@ var init_template_rules = __esm({
30634
30637
  }
30635
30638
  });
30636
30639
 
30640
+ // ../shared/src/lint/path-glob.ts
30641
+ function segments(path6) {
30642
+ const parts = [];
30643
+ for (const part of path6.replace(/\\/g, "/").split("/")) {
30644
+ if (part === "..") parts.pop();
30645
+ else if (part && part !== ".") parts.push(part);
30646
+ }
30647
+ return parts;
30648
+ }
30649
+ function segmentMatches(segment, pattern) {
30650
+ let s = 0;
30651
+ let p = 0;
30652
+ let star = -1;
30653
+ let mark = 0;
30654
+ while (s < segment.length) {
30655
+ if (pattern[p] === "?" || pattern[p] === segment[s]) {
30656
+ s += 1;
30657
+ p += 1;
30658
+ } else if (pattern[p] === "*") {
30659
+ star = p;
30660
+ p += 1;
30661
+ mark = s;
30662
+ } else if (star !== -1) {
30663
+ p = star + 1;
30664
+ mark += 1;
30665
+ s = mark;
30666
+ } else {
30667
+ return false;
30668
+ }
30669
+ }
30670
+ while (pattern[p] === "*") p += 1;
30671
+ return p === pattern.length;
30672
+ }
30673
+ function matchesPathGlob(path6, glob) {
30674
+ if (glob.length > MAX_GLOB_LENGTH || path6.length > MAX_MATCH_PATH_LENGTH) return false;
30675
+ const parts = segments(path6);
30676
+ let reachable = Array(parts.length + 1).fill(false);
30677
+ reachable[0] = true;
30678
+ for (const segment of segments(glob)) {
30679
+ const next = reachable.map(() => false);
30680
+ if (segment === "**") {
30681
+ let seen = false;
30682
+ reachable.forEach((value, j) => {
30683
+ seen = seen || value;
30684
+ next[j] = seen;
30685
+ });
30686
+ } else {
30687
+ for (let j = 1; j <= parts.length; j += 1) {
30688
+ next[j] = reachable[j - 1] && segmentMatches(parts[j - 1], segment);
30689
+ }
30690
+ }
30691
+ reachable = next;
30692
+ }
30693
+ return reachable[parts.length];
30694
+ }
30695
+ var MAX_GLOB_LENGTH, MAX_MATCH_PATH_LENGTH;
30696
+ var init_path_glob = __esm({
30697
+ "../shared/src/lint/path-glob.ts"() {
30698
+ "use strict";
30699
+ MAX_GLOB_LENGTH = 200;
30700
+ MAX_MATCH_PATH_LENGTH = 512;
30701
+ }
30702
+ });
30703
+
30637
30704
  // ../shared/src/lint/engine.ts
30638
30705
  import ts3 from "typescript";
30639
30706
  function severityFromFlag(flag, s) {
@@ -30741,7 +30808,13 @@ function elementHasOnlyIconChild(element) {
30741
30808
  if (!tagName) return false;
30742
30809
  return /icon$/i.test(tagName) || /^icon/i.test(tagName) || /^(Lucide|Phosphor|Tabler|Solar)/.test(tagName);
30743
30810
  }
30744
- function lintFile(input, config) {
30811
+ function withElementExemptions(path6, config) {
30812
+ const fromConfigFolder = config.pathsFrom ? `${config.pathsFrom}/${path6}` : path6;
30813
+ const exempt = config.rules.elementExemptions?.some((exemption) => matchesPathGlob(fromConfigFolder, exemption.path));
30814
+ return exempt ? { ...config, rules: { ...config.rules, elements: {} } } : config;
30815
+ }
30816
+ function lintFile(input, baseConfig) {
30817
+ const config = withElementExemptions(input.path, baseConfig);
30745
30818
  if (/\.css$/i.test(input.path)) return lintStylesheet(input, config);
30746
30819
  if (/\.(html|vue|svelte|mdx)$/i.test(input.path)) {
30747
30820
  return [...lintTemplate(input, config), ...lintCodeRules(input)];
@@ -31420,9 +31493,9 @@ function collectFontExports(statement, loaderBindings, exports, reExports) {
31420
31493
  }
31421
31494
  function resolveImportedModule(importerPath, specifier, modules) {
31422
31495
  const directory = importerPath.split("/").slice(0, -1).join("/");
31423
- const segments = importerPath.split("/");
31424
- const srcIndex = segments.lastIndexOf("src");
31425
- const aliasRoot = srcIndex >= 0 ? segments.slice(0, srcIndex + 1).join("/") : /^(?:apps|packages)\//.test(importerPath) ? segments.slice(0, 2).join("/") : "src";
31496
+ const segments2 = importerPath.split("/");
31497
+ const srcIndex = segments2.lastIndexOf("src");
31498
+ const aliasRoot = srcIndex >= 0 ? segments2.slice(0, srcIndex + 1).join("/") : /^(?:apps|packages)\//.test(importerPath) ? segments2.slice(0, 2).join("/") : "src";
31426
31499
  const bases = /^(?:@|~|#)\//.test(specifier) ? [`${aliasRoot}/${specifier.slice(2)}`, `src/${specifier.slice(2)}`, specifier.slice(2)] : specifier.startsWith(".") ? [`${directory}/${specifier}`] : [];
31427
31500
  for (const rawBase of bases) {
31428
31501
  const base = normalizeSourcePath(rawBase);
@@ -31439,13 +31512,13 @@ function resolveImportedModule(importerPath, specifier, modules) {
31439
31512
  return void 0;
31440
31513
  }
31441
31514
  function normalizeSourcePath(path6) {
31442
- const segments = [];
31515
+ const segments2 = [];
31443
31516
  for (const segment of path6.replace(/\\/g, "/").split("/")) {
31444
31517
  if (!segment || segment === ".") continue;
31445
- if (segment === "..") segments.pop();
31446
- else segments.push(segment);
31518
+ if (segment === "..") segments2.pop();
31519
+ else segments2.push(segment);
31447
31520
  }
31448
- return segments.join("/");
31521
+ return segments2.join("/");
31449
31522
  }
31450
31523
  function isInsideDocumentShellClass(node) {
31451
31524
  let current = node.parent;
@@ -31623,6 +31696,7 @@ var init_engine = __esm({
31623
31696
  init_code_rules();
31624
31697
  init_length_rules();
31625
31698
  init_template_rules();
31699
+ init_path_glob();
31626
31700
  NATIVE_HTML_TAGS = /* @__PURE__ */ new Set([
31627
31701
  "a",
31628
31702
  "abbr",
@@ -31784,6 +31858,10 @@ var init_engine = __esm({
31784
31858
 
31785
31859
  // ../shared/src/lint/config.ts
31786
31860
  import { z as z31 } from "zod";
31861
+ function parseReviewConfigOverride(value) {
31862
+ const parsed = reviewConfigOverrideSchema.safeParse(value);
31863
+ return parsed.success ? parsed.data : null;
31864
+ }
31787
31865
  function mergeReviewConfig(override) {
31788
31866
  if (!override) return DEFAULT_REVIEW_CONFIG;
31789
31867
  return {
@@ -31810,6 +31888,7 @@ var DEFAULT_VISUAL_MODEL_SCREENSHOTS, DEFAULT_ELEMENTS, DEFAULT_REVIEW_CONFIG, r
31810
31888
  var init_config = __esm({
31811
31889
  "../shared/src/lint/config.ts"() {
31812
31890
  "use strict";
31891
+ init_path_glob();
31813
31892
  DEFAULT_VISUAL_MODEL_SCREENSHOTS = 24;
31814
31893
  DEFAULT_ELEMENTS = {
31815
31894
  "app-shell": { use: "Sidebar", from: "@/components/ui/sidebar" },
@@ -31874,6 +31953,10 @@ var init_config = __esm({
31874
31953
  visual: visualReviewConfigSchema.optional(),
31875
31954
  rules: z31.object({
31876
31955
  elements: z31.record(z31.string(), z31.object({ use: z31.string(), from: z31.string() })).optional(),
31956
+ elementExemptions: z31.array(z31.object({
31957
+ path: z31.string().min(1).max(MAX_GLOB_LENGTH),
31958
+ reason: z31.string().trim().min(1).max(300)
31959
+ })).max(20).optional(),
31877
31960
  tokens: z31.record(z31.string(), z31.string()).optional(),
31878
31961
  wcag: z31.object({
31879
31962
  enabled: z31.boolean().optional(),
@@ -31947,12 +32030,13 @@ var init_lint = __esm({
31947
32030
  init_engine();
31948
32031
  init_config();
31949
32032
  init_report();
32033
+ init_path_glob();
31950
32034
  }
31951
32035
  });
31952
32036
 
31953
32037
  // src/review/config-loader.ts
31954
32038
  import { readFileSync as readFileSync3 } from "fs";
31955
- import { dirname, join, parse as parse2 } from "path";
32039
+ import { dirname, join, parse as parse2, relative } from "path";
31956
32040
  function isNotFound(cause) {
31957
32041
  return cause?.code === "ENOENT" || cause?.code === "ENOTDIR";
31958
32042
  }
@@ -31972,13 +32056,18 @@ function loadReviewConfig(cwd) {
31972
32056
  }
31973
32057
  }
31974
32058
  if (raw !== null) {
32059
+ let parsed;
31975
32060
  try {
31976
- return mergeReviewConfig(JSON.parse(raw));
32061
+ parsed = JSON.parse(raw);
31977
32062
  } catch (error) {
31978
32063
  throw new Error(
31979
32064
  `Could not parse ${path6}: ${error instanceof Error ? error.message : String(error)}`
31980
32065
  );
31981
32066
  }
32067
+ const override = parseReviewConfigOverride(parsed);
32068
+ if (!override) throw new Error(`${path6} does not match the review config schema.`);
32069
+ const pathsFrom = relative(dir, cwd).split("\\").join("/");
32070
+ return { ...mergeReviewConfig(override), ...pathsFrom && { pathsFrom } };
31982
32071
  }
31983
32072
  if (dir === root) return mergeReviewConfig();
31984
32073
  const parent = dirname(dir);
@@ -31998,7 +32087,7 @@ var init_config_loader = __esm({
31998
32087
  // src/review/file-sources.ts
31999
32088
  import { execFileSync } from "child_process";
32000
32089
  import { readdirSync, statSync } from "fs";
32001
- import { isAbsolute, join as join2, relative } from "path";
32090
+ import { isAbsolute, join as join2, relative as relative2 } from "path";
32002
32091
  function isReviewablePath(path6) {
32003
32092
  return REVIEWABLE_RE.test(path6);
32004
32093
  }
@@ -32020,7 +32109,7 @@ function expandPath(cwd, arg) {
32020
32109
  return files;
32021
32110
  }
32022
32111
  function filesFromArgs(cwd, args) {
32023
- return [...new Set(args.flatMap((arg) => expandPath(cwd, arg)))].map((abs) => relative(cwd, abs));
32112
+ return [...new Set(args.flatMap((arg) => expandPath(cwd, arg)))].map((abs) => relative2(cwd, abs));
32024
32113
  }
32025
32114
  function git(cwd, args) {
32026
32115
  const out = execFileSync("git", args, { cwd, encoding: "utf8" });
@@ -32052,7 +32141,7 @@ __export(hook_exports, {
32052
32141
  runHook: () => runHook
32053
32142
  });
32054
32143
  import { readFileSync as readFileSync4 } from "fs";
32055
- import { isAbsolute as isAbsolute2, resolve } from "path";
32144
+ import { isAbsolute as isAbsolute2, relative as relative3, resolve } from "path";
32056
32145
  async function runHook(stdin = process.stdin) {
32057
32146
  let payload;
32058
32147
  try {
@@ -32075,7 +32164,7 @@ async function runHook(stdin = process.stdin) {
32075
32164
  return 0;
32076
32165
  }
32077
32166
  try {
32078
- const findings = lintFile({ path: filePath, content }, loadReviewConfig(cwd));
32167
+ const findings = lintFile({ path: relative3(cwd, absolutePath), content }, loadReviewConfig(cwd));
32079
32168
  const actionable = findings.filter(
32080
32169
  (f) => f.severity === "critical" || f.severity === "serious"
32081
32170
  );