@ggui-ai/ui-gen 0.14.0 → 0.16.0
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/adapters/generation-dispatch.d.ts +21 -4
- package/dist/adapters/generation-dispatch.js +915 -351
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +28 -23
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +17 -12
- package/dist/advanced/index.js +968 -375
- package/dist/advanced/index.js.map +1 -1
- package/dist/boilerplate.d.ts +16 -1
- package/dist/boilerplate.js +579 -245
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +17 -3
- package/dist/check/index.js +34 -26
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-D6TKuv9V.d.ts → create-ui-generator-CCIeQaEe.d.ts} +77 -10
- package/dist/design-mode-BHEE7flj.d.ts +67 -0
- package/dist/{dispatch-BuZfTh9r.d.ts → dispatch-BA5dJcg-.d.ts} +4 -1
- package/dist/evaluation/axis-checks/index.d.ts +4 -3
- package/dist/evaluation/axis-checks/index.js +3 -1
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +2 -1
- package/dist/evaluation/axis-checks/registry.js +1 -0
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +143 -3
- package/dist/evaluation/index.js +3866 -92
- package/dist/evaluation/index.js.map +1 -1
- package/dist/{generate-BTfvELgl.d.ts → generate-8L_bc2iP.d.ts} +10 -1
- package/dist/harness/check/runtime-render/index.d.ts +2 -1
- package/dist/harness/index.d.ts +2 -1
- package/dist/harness/index.js +589 -248
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/llm-trace-sink.d.ts +3 -3
- package/dist/harness/llm-trace-sink.js.map +1 -1
- package/dist/harness/prompts.d.ts +2 -1
- package/dist/harness/prompts.js +3 -0
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +17 -5
- package/dist/harness/result-types.js.map +1 -1
- package/dist/harness/runtime.d.ts +19 -3
- package/dist/harness/runtime.js +602 -261
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +28 -1
- package/dist/harness/types-public.js +589 -248
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +2 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +975 -373
- package/dist/index.js.map +1 -1
- package/dist/patch.d.ts +2 -1
- package/dist/{prompts-BVhtH3M1.d.ts → prompts-k6m6reqe.d.ts} +11 -1
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/tools/get-primitives-ts.d.ts +1 -1
- package/dist/tools/get-primitives-ts.js +1 -1
- package/dist/tools/get-primitives-ts.js.map +1 -1
- package/dist/{types-public-CkNWEov9.d.ts → types-public-D24JLJgl.d.ts} +49 -5
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +4 -4
- package/dist/validation/index.js.map +1 -1
- package/dist/workflows.d.ts +2 -1
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +57 -4
- package/src/adapters/provider-router.ts +7 -6
- package/src/adapters/tools.ts +5 -5
- package/src/advanced/generator.ts +11 -10
- package/src/advanced/index.ts +5 -4
- package/src/boilerplate/free-design-prompt.ts +329 -0
- package/src/boilerplate/generate.ts +10 -1
- package/src/boilerplate/hard-sections.ts +242 -0
- package/src/boilerplate/render.ts +32 -8
- package/src/boilerplate/system-prompt.ts +81 -229
- package/src/boilerplate/templates/base-free.tsx.tmpl +24 -0
- package/src/check/run-tier0.ts +80 -20
- package/src/check/type-checker.ts +12 -2
- package/src/coding-agent/tools.ts +8 -2
- package/src/contract-context.ts +28 -0
- package/src/create-harness.ts +11 -0
- package/src/create-ui-generator.ts +71 -25
- package/src/design-mode.ts +182 -0
- package/src/design-system-docs.ts +17 -13
- package/src/evaluation/axis-checks/dispatch.ts +4 -0
- package/src/evaluation/axis-checks/extras.ts +5 -0
- package/src/evaluation/cost-tracker.ts +2 -3
- package/src/evaluation/index.ts +46 -2
- package/src/evaluation/llm-evaluator.ts +77 -21
- package/src/evaluation/types-public.ts +103 -8
- package/src/evaluation/visual-evaluator.ts +301 -29
- package/src/harness/coding/dupe-break.ts +0 -0
- package/src/harness/coding/init-session.ts +1 -0
- package/src/harness/coding/run-coding-turn.ts +3 -0
- package/src/harness/coding/run-eval-round.ts +15 -2
- package/src/harness/llm-trace-sink.ts +3 -3
- package/src/harness/result-types.ts +15 -3
- package/src/harness/runtime.ts +18 -0
- package/src/harness/types-public.ts +27 -0
- package/src/index.ts +26 -0
- package/src/internal/extract-call-signatures.ts +2 -2
- package/src/providers/bedrock.ts +4 -4
- package/src/run-check.ts +1 -0
- package/src/tools/get-primitives-ts.ts +1 -1
- package/src/validation/allowed-imports.ts +8 -2
- package/src/validation/primitives.ts +1 -1
package/dist/check/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataContract, JsonObject, PropsSpec, ActionSpec, StreamSpec } from '@ggui-ai/protocol';
|
|
2
|
-
import {
|
|
2
|
+
import { D as DesignMode } from '../design-mode-BHEE7flj.js';
|
|
3
|
+
import { b as EvalResult, E as EvalIssue } from '../types-public-D24JLJgl.js';
|
|
3
4
|
export { j as jsonSchemaTypeToTs } from '../json-schema-ts-CbB78b3n.js';
|
|
4
5
|
import '../axes-CzLEMDeB.js';
|
|
5
6
|
import '../types-BOvHNG7K.js';
|
|
@@ -223,10 +224,23 @@ declare function runTier0Checks(sourceCode: string, contract?: DataContract, bui
|
|
|
223
224
|
* resolves against the real declaration (strict option/return
|
|
224
225
|
* narrowing). Standard-library-only callers omit it.
|
|
225
226
|
*/
|
|
226
|
-
gadgetTypes?: Readonly<Record<string, string
|
|
227
|
+
gadgetTypes?: Readonly<Record<string, string>>,
|
|
228
|
+
/**
|
|
229
|
+
* Which triad produced the source. `constrained` (default) runs every
|
|
230
|
+
* leg exactly as before. `free` gates OFF the design-VOCABULARY legs —
|
|
231
|
+
* `raw-spacing`, `raw-pixels`, `numeric-spacing-prop`, the
|
|
232
|
+
* `stack-row-*` prop checks and `clickable-wrapper` — because raw
|
|
233
|
+
* elements, inline CSS and literal spacing are first-class there. It
|
|
234
|
+
* KEEPS every contract leg: `hex-color`, `hardcoded-color-fn`,
|
|
235
|
+
* `named-color`, `token-fallback`, `off-manifest-token`,
|
|
236
|
+
* `asset-color-pair` (color stays on the closed token manifest),
|
|
237
|
+
* imports, security, Props / default-export, wire_* and gadget checks,
|
|
238
|
+
* and the double-wired-action detector (retargeted at raw elements).
|
|
239
|
+
*/
|
|
240
|
+
designMode?: DesignMode): Promise<EvalIssue[]>;
|
|
227
241
|
/**
|
|
228
242
|
* Run tier 0 checks and return a full EvalResult with pass list.
|
|
229
243
|
*/
|
|
230
|
-
declare function runTier0(sourceCode: string, contract?: DataContract): Promise<EvalResult>;
|
|
244
|
+
declare function runTier0(sourceCode: string, contract?: DataContract, designMode?: DesignMode): Promise<EvalResult>;
|
|
231
245
|
|
|
232
246
|
export { type ContractIssue, type ReactLintDiagnostic, type TypeCheckDiagnostic, type TypeCheckResult, type WireCallSite, type WireImportReport, type WireImportSite, type WireKind, type WirePreservationReport, checkWireImports, checkWirePreservation, collectExpectedWires, extractPropsInterface, extractWireCallSites, extractWireImports, inferPropsSpecFromSampleData, lintReactHooks, propsSpecToTypeScript, runTier0, runTier0Checks, typecheck, validateActionSpecConformance, validateAllContracts, validatePropsAgainstSchema, validateStreamSpecConformance };
|
package/dist/check/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import ts4 from 'typescript';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
|
+
import { FORBIDDEN_IMPORT_SPECIFIERS } from '@ggui-ai/design/rendering';
|
|
5
6
|
import { Linter } from 'eslint';
|
|
6
7
|
import { listContractGadgets, HOOK_NAME_RE } from '@ggui-ai/protocol';
|
|
7
8
|
import { consumedTokenManifest } from '@ggui-ai/design/themes';
|
|
@@ -395,8 +396,6 @@ function inferJsonSchema(value) {
|
|
|
395
396
|
}
|
|
396
397
|
return { type: "string" };
|
|
397
398
|
}
|
|
398
|
-
|
|
399
|
-
// src/validation/allowed-imports.ts
|
|
400
399
|
var ALLOWED_IMPORT_BASES = [
|
|
401
400
|
"react",
|
|
402
401
|
"react-dom",
|
|
@@ -405,13 +404,14 @@ var ALLOWED_IMPORT_BASES = [
|
|
|
405
404
|
"@ggui-ai/gadgets"
|
|
406
405
|
];
|
|
407
406
|
function isAllowedImport(specifier, gadgetPackages) {
|
|
407
|
+
if (FORBIDDEN_IMPORT_SPECIFIERS.includes(specifier)) return false;
|
|
408
408
|
for (const base of ALLOWED_IMPORT_BASES) {
|
|
409
409
|
if (specifier === base || specifier.startsWith(`${base}/`)) return true;
|
|
410
410
|
}
|
|
411
411
|
return gadgetPackages?.has(specifier) ?? false;
|
|
412
412
|
}
|
|
413
413
|
function describeAllowedImports() {
|
|
414
|
-
return "react, @ggui-ai/design, @ggui-ai/wire, @ggui-ai/gadgets, or a gadget package declared on the contract";
|
|
414
|
+
return "react, @ggui-ai/design, @ggui-ai/wire, @ggui-ai/gadgets, or a gadget package declared on the contract \u2014 never @ggui-ai/wire/internal (the writer side of the wire; components read useRender().isConnected)";
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
// src/check/type-checker.ts
|
|
@@ -565,7 +565,8 @@ async function loadVfs() {
|
|
|
565
565
|
function resolveRelativeInVfs(vfs, containingFile, importPath) {
|
|
566
566
|
const normalized = containingFile.startsWith("/") ? containingFile.slice(1) : containingFile;
|
|
567
567
|
const dir = path.posix.dirname(normalized);
|
|
568
|
-
const
|
|
568
|
+
const specifier = importPath.replace(/\.(?:m|c)?js$/, "");
|
|
569
|
+
const resolved2 = path.posix.normalize(`${dir}/${specifier}`);
|
|
569
570
|
const withDts = `${resolved2}.d.ts`;
|
|
570
571
|
if (vfs.has(withDts)) return withDts;
|
|
571
572
|
const withIndexDts = `${resolved2}/index.d.ts`;
|
|
@@ -876,6 +877,9 @@ function generateReactFix(ruleId, message) {
|
|
|
876
877
|
return "Fix the React violation.";
|
|
877
878
|
}
|
|
878
879
|
|
|
880
|
+
// src/design-mode.ts
|
|
881
|
+
var DEFAULT_DESIGN_MODE = "constrained";
|
|
882
|
+
|
|
879
883
|
// src/evaluation/types-public.ts
|
|
880
884
|
function priorityForIssue(category) {
|
|
881
885
|
if (category === "interactivity" || category === "accessibility" || category === "layout" || category === "loading" || category === "visual") {
|
|
@@ -903,10 +907,11 @@ function stripComments(code) {
|
|
|
903
907
|
}
|
|
904
908
|
return out;
|
|
905
909
|
}
|
|
906
|
-
function detectCertainDoubleWiredActions(sourceCode, actionBindings) {
|
|
910
|
+
function detectCertainDoubleWiredActions(sourceCode, actionBindings, designMode = DEFAULT_DESIGN_MODE) {
|
|
907
911
|
const issues = [];
|
|
908
912
|
if (actionBindings.length === 0) return issues;
|
|
909
913
|
const bindings = new Set(actionBindings);
|
|
914
|
+
const anyElementIsInteractive = designMode === "free";
|
|
910
915
|
const TRAIT_HOST_TAGS = /* @__PURE__ */ new Set(["Card", "Box", "Stack", "Row"]);
|
|
911
916
|
const TRAITS_THAT_FIRE = /* @__PURE__ */ new Set(["Clickable", "Pressable"]);
|
|
912
917
|
const INTERACTIVE_DESCENDANTS = /* @__PURE__ */ new Set([
|
|
@@ -942,7 +947,9 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings) {
|
|
|
942
947
|
}
|
|
943
948
|
function isTraitHostWithFiringAs(node) {
|
|
944
949
|
const tag = tagNameText(node);
|
|
945
|
-
if (tag === void 0
|
|
950
|
+
if (tag === void 0) return false;
|
|
951
|
+
if (anyElementIsInteractive) return true;
|
|
952
|
+
if (!TRAIT_HOST_TAGS.has(tag)) return false;
|
|
946
953
|
const asExpr = attrExpression(node, "as");
|
|
947
954
|
return asExpr !== void 0 && ts4.isIdentifier(asExpr) && TRAITS_THAT_FIRE.has(asExpr.text);
|
|
948
955
|
}
|
|
@@ -971,7 +978,7 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings) {
|
|
|
971
978
|
const opening = ts4.isJsxElement(node) ? node.openingElement : ts4.isJsxSelfClosingElement(node) ? node : void 0;
|
|
972
979
|
if (opening !== void 0) {
|
|
973
980
|
const tag = tagNameText(opening);
|
|
974
|
-
if (tag !== void 0 && INTERACTIVE_DESCENDANTS.has(tag)) {
|
|
981
|
+
if (tag !== void 0 && (anyElementIsInteractive || INTERACTIVE_DESCENDANTS.has(tag))) {
|
|
975
982
|
for (const attrName of HANDLER_ATTRS) {
|
|
976
983
|
const callee = extractCalleeName(attrExpression(opening, attrName));
|
|
977
984
|
if (callee === expectedBinding) {
|
|
@@ -997,14 +1004,15 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings) {
|
|
|
997
1004
|
if (match !== void 0) {
|
|
998
1005
|
const outerTag = tagNameText(node.openingElement);
|
|
999
1006
|
const outerLine = sf.getLineAndCharacterOfPosition(node.openingElement.getStart()).line + 1;
|
|
1007
|
+
const outerLabel = anyElementIsInteractive ? `<${outerTag}>` : `<${outerTag} as={...}>`;
|
|
1000
1008
|
issues.push({
|
|
1001
1009
|
tier: 0,
|
|
1002
1010
|
result: "fail",
|
|
1003
1011
|
category: "interactivity",
|
|
1004
1012
|
subcategory: "double-wired-action:certain",
|
|
1005
1013
|
severity: "critical",
|
|
1006
|
-
description: `Nested-interactive double-wire: outer
|
|
1007
|
-
fix: `Pick ONE surface for the gesture: either drop \`as={...}\` + onClick on the outer <${outerTag}> and let the inner <${match.tag}> own the gesture, OR remove the inner <${match.tag}> and let the outer <${outerTag} as={...}> own it. Don't wire both to the same useAction binding.`,
|
|
1014
|
+
description: `Nested-interactive double-wire: outer ${outerLabel} at line ${outerLine} and inner <${match.tag}> at line ${match.line} both dispatch the same useAction binding '${outerCallee}'. One user click on the inner control fires its handler AND bubbles to the outer handler \u2014 '${outerCallee}' dispatches TWICE, the action runs back-to-back, and a toggle-style action silently reverts the user's change.`,
|
|
1015
|
+
fix: anyElementIsInteractive ? `Pick ONE gesture surface for '${outerCallee}': either remove the onClick from the outer <${outerTag}> and let the inner <${match.tag}> own the gesture, OR drop the inner <${match.tag}>'s handler and let the outer <${outerTag}> own it. Never wire both to the same useAction binding \u2014 stopPropagation is not a fix.` : `Pick ONE surface for the gesture: either drop \`as={...}\` + onClick on the outer <${outerTag}> and let the inner <${match.tag}> own the gesture, OR remove the inner <${match.tag}> and let the outer <${outerTag} as={...}> own it. Don't wire both to the same useAction binding.`,
|
|
1008
1016
|
line: outerLine
|
|
1009
1017
|
});
|
|
1010
1018
|
}
|
|
@@ -1041,9 +1049,10 @@ function isGadgetExportImported(source, pkg, exportName) {
|
|
|
1041
1049
|
}
|
|
1042
1050
|
return false;
|
|
1043
1051
|
}
|
|
1044
|
-
async function runTier0Checks(sourceCode, contract, buildErrors, gadgetTypes) {
|
|
1052
|
+
async function runTier0Checks(sourceCode, contract, buildErrors, gadgetTypes, designMode = DEFAULT_DESIGN_MODE) {
|
|
1045
1053
|
const issues = [];
|
|
1046
1054
|
const lines = sourceCode.split("\n");
|
|
1055
|
+
const enforceDesignVocabulary = designMode === "constrained";
|
|
1047
1056
|
if (buildErrors && buildErrors.length > 0) {
|
|
1048
1057
|
const errorDetail = buildErrors.map((e) => {
|
|
1049
1058
|
const match = e.match(/<stdin>:(\d+):\d+: ERROR: (.+)/);
|
|
@@ -1172,7 +1181,7 @@ ${errorDetail}`,
|
|
|
1172
1181
|
subcategory: "hardcoded-color-fn",
|
|
1173
1182
|
severity: "critical",
|
|
1174
1183
|
description: `Hardcoded ${colorFnMatch[1]}() breaks theme switching \u2014 use design tokens.`,
|
|
1175
|
-
fix: "Replace with a primitive variant OR a
|
|
1184
|
+
fix: "Replace with a primitive variant OR a layering role: var(--ggui-color-container), var(--ggui-color-onContainer), var(--ggui-color-outline), etc.",
|
|
1176
1185
|
line: lineNum
|
|
1177
1186
|
});
|
|
1178
1187
|
}
|
|
@@ -1189,13 +1198,13 @@ ${errorDetail}`,
|
|
|
1189
1198
|
subcategory: "off-manifest-token",
|
|
1190
1199
|
severity: "critical",
|
|
1191
1200
|
description: `Token "${name}" is not in the design system's token manifest \u2014 no theme defines it, so it renders unset.`,
|
|
1192
|
-
fix: `Choose a token that exists: the closed vocabulary is the manifest the design-system docs list (colors: var(--ggui-color-primary-500), var(--ggui-color-
|
|
1201
|
+
fix: `Choose a token that exists: the closed vocabulary is the manifest the design-system docs list (colors: var(--ggui-color-primary-500), var(--ggui-color-onContainer), \u2026; spacing: var(--ggui-spacing-4); type: var(--ggui-font-size-sm)). Never invent a name and never add a literal fallback.`,
|
|
1193
1202
|
line: lineNum
|
|
1194
1203
|
});
|
|
1195
1204
|
}
|
|
1196
|
-
const rawSpacingMatch = line.match(
|
|
1205
|
+
const rawSpacingMatch = enforceDesignVocabulary ? line.match(
|
|
1197
1206
|
/\b(gap|padding|paddingX|paddingY|margin|radius)\s*=\s*["'][\d.]+(?:px|rem|em)["']/
|
|
1198
|
-
);
|
|
1207
|
+
) : null;
|
|
1199
1208
|
if (rawSpacingMatch) {
|
|
1200
1209
|
issues.push({
|
|
1201
1210
|
tier: 0,
|
|
@@ -1289,12 +1298,12 @@ ${errorDetail}`,
|
|
|
1289
1298
|
subcategory: "named-color",
|
|
1290
1299
|
severity: "critical",
|
|
1291
1300
|
description: `Hardcoded CSS named color "${named}" breaks theme switching \u2014 use design tokens.`,
|
|
1292
|
-
fix: `Replace with a typed slot (Text tone="muted" / Box surface="accent" / Badge variant="success") OR a semantic token: var(--ggui-color-
|
|
1301
|
+
fix: `Replace with a typed slot (Text tone="muted" / Box surface="accent" / Badge variant="success") OR a semantic token: var(--ggui-color-onSunken), var(--ggui-color-primary-500), etc. The keyword "${named}" maps to a fixed RGB; the operator's theme has no effect on it.`,
|
|
1293
1302
|
line: lineNum
|
|
1294
1303
|
});
|
|
1295
1304
|
}
|
|
1296
1305
|
}
|
|
1297
|
-
const pxMatch = line.match(/(?:padding|margin|gap|borderRadius)\s*:\s*['"]?\d+px/);
|
|
1306
|
+
const pxMatch = enforceDesignVocabulary ? line.match(/(?:padding|margin|gap|borderRadius)\s*:\s*['"]?\d+px/) : null;
|
|
1298
1307
|
if (pxMatch && !line.includes("var(--ggui-")) {
|
|
1299
1308
|
issues.push({
|
|
1300
1309
|
tier: 0,
|
|
@@ -1302,11 +1311,11 @@ ${errorDetail}`,
|
|
|
1302
1311
|
category: "tokens",
|
|
1303
1312
|
subcategory: "raw-pixels",
|
|
1304
1313
|
description: "Raw pixel value in spacing \u2014 must use design tokens",
|
|
1305
|
-
fix:
|
|
1314
|
+
fix: 'Replace with a bare var(--ggui-spacing-*) token \u2014 no literal fallback (the runtime injects every token) \u2014 or the scale name on the primitive prop (gap="md", padding="lg")',
|
|
1306
1315
|
line: lineNum
|
|
1307
1316
|
});
|
|
1308
1317
|
}
|
|
1309
|
-
const numericSpacingMatch = line.match(/\b(padding|paddingX|paddingY|gap|margin)\s*=\s*\{?\s*(\d+)\s*\}?/);
|
|
1318
|
+
const numericSpacingMatch = enforceDesignVocabulary ? line.match(/\b(padding|paddingX|paddingY|gap|margin)\s*=\s*\{?\s*(\d+)\s*\}?/) : null;
|
|
1310
1319
|
if (numericSpacingMatch && !line.includes("var(--ggui-")) {
|
|
1311
1320
|
issues.push({
|
|
1312
1321
|
tier: 0,
|
|
@@ -1320,7 +1329,7 @@ ${errorDetail}`,
|
|
|
1320
1329
|
}
|
|
1321
1330
|
}
|
|
1322
1331
|
const clickablePattern = /<(?:Card|Box|Stack|Row)\s[^>]*onClick/;
|
|
1323
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1332
|
+
for (let i = 0; enforceDesignVocabulary && i < lines.length; i++) {
|
|
1324
1333
|
if (clickablePattern.test(lines[i]) && !lines[i].includes("as={Clickable}") && !lines[i].includes("as={Pressable}")) {
|
|
1325
1334
|
issues.push({
|
|
1326
1335
|
tier: 0,
|
|
@@ -1333,9 +1342,8 @@ ${errorDetail}`,
|
|
|
1333
1342
|
});
|
|
1334
1343
|
}
|
|
1335
1344
|
}
|
|
1336
|
-
const
|
|
1337
|
-
|
|
1338
|
-
].map((m) => m[1]).filter((name) => typeof name === "string");
|
|
1345
|
+
const bindingRe = designMode === "free" ? /(?:const|let)\s+(\w+)\s*=\s*useAction\s*(?:<[^>]*>)?\s*\(/g : /(?:const|let)\s+(\w+)\s*=\s*useAction\s*\(/g;
|
|
1346
|
+
const actionBindings = [...sourceCode.matchAll(bindingRe)].map((m) => m[1]).filter((name) => typeof name === "string");
|
|
1339
1347
|
for (const binding of actionBindings) {
|
|
1340
1348
|
const callSites = (sourceCode.match(new RegExp(`\\b${binding}\\s*\\(`, "g")) ?? []).length;
|
|
1341
1349
|
if (callSites >= 2) {
|
|
@@ -1351,7 +1359,7 @@ ${errorDetail}`,
|
|
|
1351
1359
|
}
|
|
1352
1360
|
if (actionBindings.length > 0) {
|
|
1353
1361
|
issues.push(
|
|
1354
|
-
...detectCertainDoubleWiredActions(sourceCode, actionBindings)
|
|
1362
|
+
...detectCertainDoubleWiredActions(sourceCode, actionBindings, designMode)
|
|
1355
1363
|
);
|
|
1356
1364
|
}
|
|
1357
1365
|
if (/\buseRender\s*\(/.test(sourceCode)) {
|
|
@@ -1396,7 +1404,7 @@ ${errorDetail}`,
|
|
|
1396
1404
|
const ALIGN_VALUES = /* @__PURE__ */ new Set(["start", "center", "end", "stretch"]);
|
|
1397
1405
|
const JUSTIFY_VALUES = /* @__PURE__ */ new Set(["start", "center", "end", "between", "around", "evenly"]);
|
|
1398
1406
|
const tagRegex = /<(Stack|Row)\b([^>]*)>/gs;
|
|
1399
|
-
for (const tag of sourceCode.matchAll(tagRegex)) {
|
|
1407
|
+
for (const tag of enforceDesignVocabulary ? sourceCode.matchAll(tagRegex) : []) {
|
|
1400
1408
|
const tagName = tag[1];
|
|
1401
1409
|
const attrs = tag[2];
|
|
1402
1410
|
const tagLine = sourceCode.slice(0, tag.index).split("\n").length;
|
|
@@ -1682,8 +1690,8 @@ Import \`${exportName}\` ONLY from '${use.package}' \u2014 that is the package i
|
|
|
1682
1690
|
}
|
|
1683
1691
|
return issues;
|
|
1684
1692
|
}
|
|
1685
|
-
async function runTier0(sourceCode, contract) {
|
|
1686
|
-
const issues = await runTier0Checks(sourceCode, contract);
|
|
1693
|
+
async function runTier0(sourceCode, contract, designMode = DEFAULT_DESIGN_MODE) {
|
|
1694
|
+
const issues = await runTier0Checks(sourceCode, contract, void 0, void 0, designMode);
|
|
1687
1695
|
const pass = [];
|
|
1688
1696
|
const failedCategories = new Set(issues.map((i) => i.category));
|
|
1689
1697
|
const tier0Categories = ["compile", "security", "contract", "types", "imports", "tokens"];
|