uidex 0.5.0 → 0.5.2
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/cli/cli.cjs +115 -14
- package/dist/cli/cli.cjs.map +1 -1
- package/dist/headless/index.cjs +22 -3
- package/dist/headless/index.cjs.map +1 -1
- package/dist/headless/index.js +22 -3
- package/dist/headless/index.js.map +1 -1
- package/dist/index.cjs +22 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +22 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +22 -3
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +22 -3
- package/dist/react/index.js.map +1 -1
- package/dist/scan/index.cjs +110 -9
- package/dist/scan/index.cjs.map +1 -1
- package/dist/scan/index.d.cts +1 -1
- package/dist/scan/index.d.ts +1 -1
- package/dist/scan/index.js +110 -9
- package/dist/scan/index.js.map +1 -1
- package/package.json +19 -26
package/dist/scan/index.cjs
CHANGED
|
@@ -412,6 +412,7 @@ var KIND_DISCRIMINATORS = [
|
|
|
412
412
|
"feature",
|
|
413
413
|
"primitive",
|
|
414
414
|
"widget",
|
|
415
|
+
"region",
|
|
415
416
|
"flow",
|
|
416
417
|
"notFlow"
|
|
417
418
|
];
|
|
@@ -433,12 +434,14 @@ var ALLOWED_FIELDS = {
|
|
|
433
434
|
]),
|
|
434
435
|
primitive: /* @__PURE__ */ new Set(["primitive", "name", "description"]),
|
|
435
436
|
widget: /* @__PURE__ */ new Set(["widget", "name", "acceptance", "description"]),
|
|
437
|
+
region: /* @__PURE__ */ new Set(["region", "name", "description"]),
|
|
436
438
|
flow: /* @__PURE__ */ new Set(["flow", "notFlow", "name", "description"])
|
|
437
439
|
};
|
|
438
440
|
var FALSEABLE = /* @__PURE__ */ new Set([
|
|
439
441
|
"page",
|
|
440
442
|
"feature",
|
|
441
|
-
"primitive"
|
|
443
|
+
"primitive",
|
|
444
|
+
"region"
|
|
442
445
|
]);
|
|
443
446
|
var ExtractError = class extends Error {
|
|
444
447
|
code;
|
|
@@ -2070,6 +2073,21 @@ function resolve2(ctx) {
|
|
|
2070
2073
|
};
|
|
2071
2074
|
registry.add(region);
|
|
2072
2075
|
}
|
|
2076
|
+
for (const ef of ctx.extracted) {
|
|
2077
|
+
const exp = exportFor(ef.file.displayPath, "region");
|
|
2078
|
+
if (!exp) continue;
|
|
2079
|
+
if (exp.id === false) continue;
|
|
2080
|
+
if (typeof exp.id === "string" && !registry.get("region", exp.id)) {
|
|
2081
|
+
const meta = buildMetaFromExport(exp);
|
|
2082
|
+
const region = {
|
|
2083
|
+
kind: "region",
|
|
2084
|
+
id: exp.id,
|
|
2085
|
+
loc: { file: ef.file.displayPath, line: exp.loc.line },
|
|
2086
|
+
...meta ? { meta } : {}
|
|
2087
|
+
};
|
|
2088
|
+
registry.add(region);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2073
2091
|
const primitiveConventions = conventions.primitives;
|
|
2074
2092
|
for (const ef of ctx.extracted) {
|
|
2075
2093
|
const file = ef.file.displayPath;
|
|
@@ -2378,17 +2396,38 @@ function audit(opts) {
|
|
|
2378
2396
|
}
|
|
2379
2397
|
}
|
|
2380
2398
|
if (lint) {
|
|
2399
|
+
const dynamicAttrRe = /\bdata-uidex(?:-(region|widget|primitive))?\s*=\s*\{/g;
|
|
2381
2400
|
for (const f of files) {
|
|
2382
|
-
const lines = f.content.split("\n");
|
|
2383
|
-
const candidateRe = /<(button|a|input|select|textarea)(?=[\s/>])([^>]*)>/g;
|
|
2384
2401
|
let m;
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
const idx = m.index;
|
|
2402
|
+
dynamicAttrRe.lastIndex = 0;
|
|
2403
|
+
while ((m = dynamicAttrRe.exec(f.content)) !== null) {
|
|
2404
|
+
const kind = m[1] ?? "element";
|
|
2389
2405
|
let line = 1;
|
|
2390
|
-
for (let i = 0; i <
|
|
2391
|
-
|
|
2406
|
+
for (let i = 0; i < m.index; i++) if (f.content[i] === "\n") line++;
|
|
2407
|
+
const attrName = m[1] ? `data-uidex-${m[1]}` : "data-uidex";
|
|
2408
|
+
diagnostics.push({
|
|
2409
|
+
code: "dynamic-attr",
|
|
2410
|
+
severity: "warning",
|
|
2411
|
+
message: `\`${attrName}={\u2026}\` uses a dynamic expression; the scanner cannot resolve the ${kind} id statically`,
|
|
2412
|
+
file: f.displayPath,
|
|
2413
|
+
line,
|
|
2414
|
+
hint: dynamicAttrHint(kind)
|
|
2415
|
+
});
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
if (lint) {
|
|
2420
|
+
for (const f of files) {
|
|
2421
|
+
const tagRe = /<(button|a|input|select|textarea)(?=[\s/>])/g;
|
|
2422
|
+
let m;
|
|
2423
|
+
while ((m = tagRe.exec(f.content)) !== null) {
|
|
2424
|
+
const afterTag = m.index + m[0].length;
|
|
2425
|
+
const closeIdx = findJsxOpeningEnd(f.content, afterTag);
|
|
2426
|
+
if (closeIdx === -1) continue;
|
|
2427
|
+
const attrs = f.content.slice(afterTag, closeIdx);
|
|
2428
|
+
if (attrs.includes("data-uidex")) continue;
|
|
2429
|
+
let line = 1;
|
|
2430
|
+
for (let i = 0; i < m.index; i++) if (f.content[i] === "\n") line++;
|
|
2392
2431
|
diagnostics.push({
|
|
2393
2432
|
code: "missing-element-annotation",
|
|
2394
2433
|
severity: "info",
|
|
@@ -2598,6 +2637,63 @@ function extractEntitiesArray(source) {
|
|
|
2598
2637
|
}
|
|
2599
2638
|
return null;
|
|
2600
2639
|
}
|
|
2640
|
+
function findJsxOpeningEnd(src, start) {
|
|
2641
|
+
let i = start;
|
|
2642
|
+
while (i < src.length) {
|
|
2643
|
+
const ch = src[i];
|
|
2644
|
+
if (ch === ">" || ch === "/" && src[i + 1] === ">") return i;
|
|
2645
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
2646
|
+
i = skipString2(src, i);
|
|
2647
|
+
} else if (ch === "{") {
|
|
2648
|
+
i = skipBraces(src, i);
|
|
2649
|
+
} else {
|
|
2650
|
+
i++;
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
return -1;
|
|
2654
|
+
}
|
|
2655
|
+
function skipString2(src, start) {
|
|
2656
|
+
const quote = src[start];
|
|
2657
|
+
let i = start + 1;
|
|
2658
|
+
while (i < src.length) {
|
|
2659
|
+
if (src[i] === "\\" && quote !== "`") {
|
|
2660
|
+
i += 2;
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
if (quote === "`" && src[i] === "$" && src[i + 1] === "{") {
|
|
2664
|
+
i = skipBraces(src, i + 1);
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
if (src[i] === quote) return i + 1;
|
|
2668
|
+
i++;
|
|
2669
|
+
}
|
|
2670
|
+
return i;
|
|
2671
|
+
}
|
|
2672
|
+
function skipBraces(src, start) {
|
|
2673
|
+
let depth = 1;
|
|
2674
|
+
let i = start + 1;
|
|
2675
|
+
while (i < src.length && depth > 0) {
|
|
2676
|
+
const ch = src[i];
|
|
2677
|
+
if (ch === "{") {
|
|
2678
|
+
depth++;
|
|
2679
|
+
i++;
|
|
2680
|
+
} else if (ch === "}") {
|
|
2681
|
+
depth--;
|
|
2682
|
+
i++;
|
|
2683
|
+
} else if (ch === '"' || ch === "'" || ch === "`") {
|
|
2684
|
+
i = skipString2(src, i);
|
|
2685
|
+
} else {
|
|
2686
|
+
i++;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
return i;
|
|
2690
|
+
}
|
|
2691
|
+
function dynamicAttrHint(kind) {
|
|
2692
|
+
if (kind === "region") {
|
|
2693
|
+
return `Use a string literal: \`data-uidex-region="id"\`, or declare the region via \`export const uidex = { region: "id" } as const satisfies Uidex.Region\` on the file that passes the region value`;
|
|
2694
|
+
}
|
|
2695
|
+
return `The scanner requires string-literal attribute values. If this component forwards the annotation via a prop, restructure so the caller provides the annotated element directly (e.g. via a slot or render prop) with a string-literal \`data-uidex\` attribute`;
|
|
2696
|
+
}
|
|
2601
2697
|
function stableStringify(value) {
|
|
2602
2698
|
return JSON.stringify(value, stableReplacer);
|
|
2603
2699
|
}
|
|
@@ -2751,6 +2847,11 @@ function emit(opts) {
|
|
|
2751
2847
|
lines.push(" acceptance?: readonly string[]");
|
|
2752
2848
|
lines.push(" description?: string");
|
|
2753
2849
|
lines.push(" }");
|
|
2850
|
+
lines.push(" export interface Region {");
|
|
2851
|
+
lines.push(" region: RegionId | false");
|
|
2852
|
+
lines.push(" name?: string");
|
|
2853
|
+
lines.push(" description?: string");
|
|
2854
|
+
lines.push(" }");
|
|
2754
2855
|
lines.push(" export interface Flow {");
|
|
2755
2856
|
lines.push(" flow: FlowId");
|
|
2756
2857
|
lines.push(" name?: string");
|