canicode 0.12.1 → 0.12.3
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/index.js +155 -44
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +109 -40
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +113 -41
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
- package/skills/canicode-roundtrip/SKILL.md +102 -0
- package/skills/canicode-roundtrip/helpers-bootstrap.js +1 -1
- package/skills/canicode-roundtrip/helpers-installer.js +2 -2
- package/skills/canicode-roundtrip/helpers.js +429 -0
- package/skills/cursor/canicode-roundtrip/SKILL.md +102 -0
- package/skills/cursor/canicode-roundtrip/helpers-bootstrap.js +1 -1
- package/skills/cursor/canicode-roundtrip/helpers-installer.js +2 -2
- package/skills/cursor/canicode-roundtrip/helpers.js +429 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { GetFileResponse, Node } from '@figma/rest-api-spec';
|
|
3
3
|
|
|
4
|
-
var version = "0.12.
|
|
4
|
+
var version = "0.12.3";
|
|
5
5
|
|
|
6
6
|
declare const SeveritySchema: z.ZodEnum<{
|
|
7
7
|
blocking: "blocking";
|
|
@@ -480,6 +480,26 @@ interface RuleContext {
|
|
|
480
480
|
* sync with `AnalysisNode.type` without a translation layer.
|
|
481
481
|
*/
|
|
482
482
|
rootNodeType: string;
|
|
483
|
+
/**
|
|
484
|
+
* #557: Root node of the current analysis subtree. Set by
|
|
485
|
+
* `RuleEngine.analyze` to the same node `traverseAndCheck` walks — i.e.
|
|
486
|
+
* `file.document` for full-file analysis or the resolved subtree when
|
|
487
|
+
* `targetNodeId` scopes the run.
|
|
488
|
+
*
|
|
489
|
+
* Distinct from `file.document`, which is **always** the full file root.
|
|
490
|
+
* Rules that build scope-wide indices (e.g. Stage 3 fingerprint pass in
|
|
491
|
+
* `missing-component`) MUST walk `analysisRoot`, not `file.document` —
|
|
492
|
+
* walking the full file under a scoped run produces silent false
|
|
493
|
+
* negatives because the index's first-occurrence id may resolve to a
|
|
494
|
+
* node outside the current scope, and the in-scope duplicate then never
|
|
495
|
+
* matches as "first" so the issue never surfaces.
|
|
496
|
+
*
|
|
497
|
+
* Optional purely so existing rule unit tests that construct
|
|
498
|
+
* `RuleContext` literals do not have to backfill the field. The engine
|
|
499
|
+
* always sets it; rule code reading this should default to
|
|
500
|
+
* `context.file.document` for safety on the unit-test path.
|
|
501
|
+
*/
|
|
502
|
+
analysisRoot?: AnalysisNode;
|
|
483
503
|
/**
|
|
484
504
|
* ADR-022: lookup canicode-authored acknowledgments by `(nodeId, ruleId)`.
|
|
485
505
|
* The rule engine builds this from `RuleEngineOptions.acknowledgments` and
|
|
@@ -519,6 +539,19 @@ interface RuleViolation {
|
|
|
519
539
|
* string keeps lowercase prose.
|
|
520
540
|
*/
|
|
521
541
|
suggestedName?: string;
|
|
542
|
+
/**
|
|
543
|
+
* Phase 3 (#508 / #560): when a rule emits a single issue that represents
|
|
544
|
+
* a group of N nodes (e.g. `missing-component:structure-repetition` —
|
|
545
|
+
* one issue per fingerprint group covering N qualifying FRAMEs), this
|
|
546
|
+
* lists every node id in the group **including** `nodeId`. Document
|
|
547
|
+
* order, scope-aware. Apply primitives (componentize + replace-with-
|
|
548
|
+
* instance) iterate this list to drive the full componentize+swap loop
|
|
549
|
+
* from a single user answer.
|
|
550
|
+
*
|
|
551
|
+
* Optional: rules that emit one issue per node (the typical path) leave
|
|
552
|
+
* this undefined.
|
|
553
|
+
*/
|
|
554
|
+
groupMembers?: string[];
|
|
522
555
|
}
|
|
523
556
|
/**
|
|
524
557
|
* Rule check function signature
|
|
@@ -836,6 +869,7 @@ declare const GotchaSurveyQuestionSchema: z.ZodObject<{
|
|
|
836
869
|
sourceChildId: z.ZodOptional<z.ZodString>;
|
|
837
870
|
replicas: z.ZodOptional<z.ZodNumber>;
|
|
838
871
|
replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
872
|
+
groupMembers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
839
873
|
}, z.core.$strip>;
|
|
840
874
|
type GotchaSurveyQuestion = z.infer<typeof GotchaSurveyQuestionSchema>;
|
|
841
875
|
/**
|
|
@@ -903,6 +937,7 @@ declare const SurveyQuestionBatchSchema: z.ZodObject<{
|
|
|
903
937
|
sourceChildId: z.ZodOptional<z.ZodString>;
|
|
904
938
|
replicas: z.ZodOptional<z.ZodNumber>;
|
|
905
939
|
replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
940
|
+
groupMembers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
906
941
|
}, z.core.$strip>>;
|
|
907
942
|
totalScenes: z.ZodNumber;
|
|
908
943
|
}, z.core.$strip>;
|
|
@@ -967,6 +1002,7 @@ declare const SurveyQuestionGroupSchema: z.ZodObject<{
|
|
|
967
1002
|
sourceChildId: z.ZodOptional<z.ZodString>;
|
|
968
1003
|
replicas: z.ZodOptional<z.ZodNumber>;
|
|
969
1004
|
replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1005
|
+
groupMembers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
970
1006
|
}, z.core.$strip>>;
|
|
971
1007
|
totalScenes: z.ZodNumber;
|
|
972
1008
|
}, z.core.$strip>>;
|
|
@@ -1033,6 +1069,7 @@ declare const GroupedSurveySchema: z.ZodObject<{
|
|
|
1033
1069
|
sourceChildId: z.ZodOptional<z.ZodString>;
|
|
1034
1070
|
replicas: z.ZodOptional<z.ZodNumber>;
|
|
1035
1071
|
replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1072
|
+
groupMembers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1036
1073
|
}, z.core.$strip>>;
|
|
1037
1074
|
totalScenes: z.ZodNumber;
|
|
1038
1075
|
}, z.core.$strip>>;
|
|
@@ -1098,6 +1135,7 @@ declare const GotchaSurveySchema: z.ZodObject<{
|
|
|
1098
1135
|
sourceChildId: z.ZodOptional<z.ZodString>;
|
|
1099
1136
|
replicas: z.ZodOptional<z.ZodNumber>;
|
|
1100
1137
|
replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1138
|
+
groupMembers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1101
1139
|
}, z.core.$strip>>;
|
|
1102
1140
|
groupedQuestions: z.ZodObject<{
|
|
1103
1141
|
groups: z.ZodArray<z.ZodObject<{
|
|
@@ -1160,6 +1198,7 @@ declare const GotchaSurveySchema: z.ZodObject<{
|
|
|
1160
1198
|
sourceChildId: z.ZodOptional<z.ZodString>;
|
|
1161
1199
|
replicas: z.ZodOptional<z.ZodNumber>;
|
|
1162
1200
|
replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1201
|
+
groupMembers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1163
1202
|
}, z.core.$strip>>;
|
|
1164
1203
|
totalScenes: z.ZodNumber;
|
|
1165
1204
|
}, z.core.$strip>>;
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import 'crypto';
|
|
|
6
6
|
import { homedir } from 'os';
|
|
7
7
|
|
|
8
8
|
// package.json
|
|
9
|
-
var version = "0.12.
|
|
9
|
+
var version = "0.12.3";
|
|
10
10
|
var SeveritySchema = z.enum([
|
|
11
11
|
"blocking",
|
|
12
12
|
"risk",
|
|
@@ -423,7 +423,25 @@ var GotchaSurveyQuestionSchema = z.object({
|
|
|
423
423
|
// `[nodeId, ...replicaNodeIds]` so the same answer lands on every replica.
|
|
424
424
|
// Single-instance questions omit both fields.
|
|
425
425
|
replicas: z.number().int().min(2).optional(),
|
|
426
|
-
replicaNodeIds: z.array(z.string()).optional()
|
|
426
|
+
replicaNodeIds: z.array(z.string()).optional(),
|
|
427
|
+
/**
|
|
428
|
+
* Phase 3 (#508 / #560 / delta 4a): every node id in the rule's emitted
|
|
429
|
+
* group, including `nodeId`. Currently only populated for
|
|
430
|
+
* `missing-component:structure-repetition` (Stage 3) — one question per
|
|
431
|
+
* fingerprint group, the apply step (delta 4b) iterates this list to
|
|
432
|
+
* componentize the first member and swap the rest.
|
|
433
|
+
*
|
|
434
|
+
* Distinct from `replicas` / `replicaNodeIds`: those collapse N
|
|
435
|
+
* instance-child questions that pre-existed as separate violations into
|
|
436
|
+
* one. `groupMembers` carries N original group members from a single
|
|
437
|
+
* group-shaped violation that never became N separate issues. The apply
|
|
438
|
+
* step distinguishes by which field is set.
|
|
439
|
+
*/
|
|
440
|
+
// `.min(2)` locks the contract: a group-shaped emit always carries at
|
|
441
|
+
// least 2 members (Stage 3 short-circuits below `structureMinRepetitions`
|
|
442
|
+
// = 2). A future rule emitting `[]` or `[oneId]` is a programming error,
|
|
443
|
+
// not a runtime case to handle gracefully.
|
|
444
|
+
groupMembers: z.array(z.string()).min(2).optional()
|
|
427
445
|
});
|
|
428
446
|
var SurveyQuestionBatchSchema = z.object({
|
|
429
447
|
ruleId: z.string(),
|
|
@@ -995,6 +1013,7 @@ var RuleEngine = class {
|
|
|
995
1013
|
analysisState,
|
|
996
1014
|
scope,
|
|
997
1015
|
rootNodeType,
|
|
1016
|
+
rootNode,
|
|
998
1017
|
void 0,
|
|
999
1018
|
void 0
|
|
1000
1019
|
);
|
|
@@ -1031,7 +1050,7 @@ var RuleEngine = class {
|
|
|
1031
1050
|
/**
|
|
1032
1051
|
* Recursively traverse the tree and run rules
|
|
1033
1052
|
*/
|
|
1034
|
-
traverseAndCheck(node, file, rules, maxDepth, issues, failedRules, depth, path, ancestorTypes, componentDepth, analysisState, scope, rootNodeType, parent, siblings) {
|
|
1053
|
+
traverseAndCheck(node, file, rules, maxDepth, issues, failedRules, depth, path, ancestorTypes, componentDepth, analysisState, scope, rootNodeType, analysisRoot, parent, siblings) {
|
|
1035
1054
|
const nodePath = [...path, node.name];
|
|
1036
1055
|
const isComponentBoundary = node.type === "COMPONENT" || node.type === "COMPONENT_SET" || node.type === "INSTANCE";
|
|
1037
1056
|
const currentComponentDepth = isComponentBoundary ? 0 : componentDepth;
|
|
@@ -1054,6 +1073,7 @@ var RuleEngine = class {
|
|
|
1054
1073
|
analysisState,
|
|
1055
1074
|
scope,
|
|
1056
1075
|
rootNodeType,
|
|
1076
|
+
analysisRoot,
|
|
1057
1077
|
findAcknowledgment: (nodeId, ruleId) => acknowledgmentsByKey.get(`${normalizeNodeId(nodeId)}::${ruleId}`)
|
|
1058
1078
|
};
|
|
1059
1079
|
for (const rule of rules) {
|
|
@@ -1103,6 +1123,7 @@ var RuleEngine = class {
|
|
|
1103
1123
|
analysisState,
|
|
1104
1124
|
scope,
|
|
1105
1125
|
rootNodeType,
|
|
1126
|
+
analysisRoot,
|
|
1106
1127
|
node,
|
|
1107
1128
|
node.children
|
|
1108
1129
|
);
|
|
@@ -2545,8 +2566,8 @@ var missingComponentMsg = {
|
|
|
2545
2566
|
message: `"${name}" appears ${count} times`,
|
|
2546
2567
|
suggestion: `Extract as a reusable component`
|
|
2547
2568
|
}),
|
|
2548
|
-
structureRepetition: (name,
|
|
2549
|
-
message: `"${name}" and ${
|
|
2569
|
+
structureRepetition: (name, matchCount) => ({
|
|
2570
|
+
message: `"${name}" and ${matchCount} other frame(s) share the same internal structure`,
|
|
2550
2571
|
suggestion: `Extract a shared component from the repeated structure`
|
|
2551
2572
|
}),
|
|
2552
2573
|
styleOverride: (componentName, overrides) => ({
|
|
@@ -3281,6 +3302,41 @@ function getSeenStage1(context) {
|
|
|
3281
3302
|
function getSeenStage4(context) {
|
|
3282
3303
|
return getAnalysisState(context, SEEN_STAGE4_KEY, () => /* @__PURE__ */ new Set());
|
|
3283
3304
|
}
|
|
3305
|
+
function stage3GroupsKey(maxDepth) {
|
|
3306
|
+
return `missing-component:stage3Groups:depth=${maxDepth}`;
|
|
3307
|
+
}
|
|
3308
|
+
function nodeQualifiesForStage3(node, parent, insideInstance) {
|
|
3309
|
+
if (insideInstance) return false;
|
|
3310
|
+
if (node.type !== "FRAME") return false;
|
|
3311
|
+
if (parent?.type === "COMPONENT_SET") return false;
|
|
3312
|
+
if (!node.children || node.children.length === 0) return false;
|
|
3313
|
+
return true;
|
|
3314
|
+
}
|
|
3315
|
+
function buildStage3Groups(root, maxFingerprintDepth) {
|
|
3316
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3317
|
+
const walk = (node, parent, ancestorIsInstance) => {
|
|
3318
|
+
const insideInstance = ancestorIsInstance || node.type === "INSTANCE";
|
|
3319
|
+
if (nodeQualifiesForStage3(node, parent, ancestorIsInstance)) {
|
|
3320
|
+
const fp = buildFingerprint(node, maxFingerprintDepth);
|
|
3321
|
+
const existing = groups.get(fp);
|
|
3322
|
+
if (existing) existing.memberIds.push(node.id);
|
|
3323
|
+
else groups.set(fp, { memberIds: [node.id] });
|
|
3324
|
+
}
|
|
3325
|
+
if (node.children) {
|
|
3326
|
+
for (const child of node.children) walk(child, node, insideInstance);
|
|
3327
|
+
}
|
|
3328
|
+
};
|
|
3329
|
+
walk(root, null, false);
|
|
3330
|
+
return groups;
|
|
3331
|
+
}
|
|
3332
|
+
function getStage3Groups(context, maxFingerprintDepth) {
|
|
3333
|
+
const root = context.analysisRoot ?? context.file.document;
|
|
3334
|
+
return getAnalysisState(
|
|
3335
|
+
context,
|
|
3336
|
+
stage3GroupsKey(maxFingerprintDepth),
|
|
3337
|
+
() => buildStage3Groups(root, maxFingerprintDepth)
|
|
3338
|
+
);
|
|
3339
|
+
}
|
|
3284
3340
|
var missingComponentDef = {
|
|
3285
3341
|
id: "missing-component",
|
|
3286
3342
|
name: "Missing Component",
|
|
@@ -3295,7 +3351,8 @@ var missingComponentCheck = (node, context, options) => {
|
|
|
3295
3351
|
const matchingComponent = Object.values(components).find(
|
|
3296
3352
|
(c) => c.name.toLowerCase() === node.name.toLowerCase()
|
|
3297
3353
|
);
|
|
3298
|
-
const
|
|
3354
|
+
const scopeRoot = context.analysisRoot ?? context.file.document;
|
|
3355
|
+
const frameNames = collectFrameNames(scopeRoot);
|
|
3299
3356
|
const sameNameFrames = frameNames.get(node.name);
|
|
3300
3357
|
const firstFrame = sameNameFrames?.[0];
|
|
3301
3358
|
if (matchingComponent) {
|
|
@@ -3323,37 +3380,32 @@ var missingComponentCheck = (node, context, options) => {
|
|
|
3323
3380
|
};
|
|
3324
3381
|
}
|
|
3325
3382
|
}
|
|
3326
|
-
if (isInsideInstance(context))
|
|
3327
|
-
|
|
3328
|
-
|
|
3383
|
+
if (!nodeQualifiesForStage3(node, context.parent ?? null, isInsideInstance(context))) {
|
|
3384
|
+
return null;
|
|
3385
|
+
}
|
|
3329
3386
|
const structureMinRepetitions = options?.["structureMinRepetitions"] ?? getRuleOption("missing-component", "structureMinRepetitions", 2);
|
|
3330
3387
|
const maxFingerprintDepth = options?.["maxFingerprintDepth"] ?? getRuleOption("missing-component", "maxFingerprintDepth", 3);
|
|
3388
|
+
const groups = getStage3Groups(context, maxFingerprintDepth);
|
|
3331
3389
|
const fingerprint = buildFingerprint(node, maxFingerprintDepth);
|
|
3332
|
-
const
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
);
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
)
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
nodePath: context.path.join(" > "),
|
|
3352
|
-
...missingComponentMsg.structureRepetition(node.name, count - 1)
|
|
3353
|
-
};
|
|
3354
|
-
}
|
|
3355
|
-
}
|
|
3356
|
-
return null;
|
|
3390
|
+
const group = groups.get(fingerprint);
|
|
3391
|
+
if (!group) return null;
|
|
3392
|
+
if (group.memberIds.length < structureMinRepetitions) return null;
|
|
3393
|
+
if (group.memberIds[0] !== node.id) return null;
|
|
3394
|
+
return {
|
|
3395
|
+
ruleId: missingComponentDef.id,
|
|
3396
|
+
subType: "structure-repetition",
|
|
3397
|
+
nodeId: node.id,
|
|
3398
|
+
nodePath: context.path.join(" > "),
|
|
3399
|
+
...missingComponentMsg.structureRepetition(
|
|
3400
|
+
node.name,
|
|
3401
|
+
group.memberIds.length - 1
|
|
3402
|
+
),
|
|
3403
|
+
// #560 / delta 4a: surface the full group so the apply step can drive
|
|
3404
|
+
// the componentize+swap loop from a single user answer. `nodeId` is
|
|
3405
|
+
// the document-order first member; the rest are siblings or cross-
|
|
3406
|
+
// parent matches found by the scope-wide pass (#557).
|
|
3407
|
+
groupMembers: [...group.memberIds]
|
|
3408
|
+
};
|
|
3357
3409
|
}
|
|
3358
3410
|
if (node.type === "INSTANCE" && node.componentId) {
|
|
3359
3411
|
const seenStage4 = getSeenStage4(context);
|
|
@@ -3442,6 +3494,7 @@ var variantStructureMismatch = defineRule({
|
|
|
3442
3494
|
});
|
|
3443
3495
|
var CODE_CONNECT_SETUP_KEY = "unmapped-component:setup-detected";
|
|
3444
3496
|
var CODE_CONNECT_MAPPINGS_KEY = "unmapped-component:mappings";
|
|
3497
|
+
var SEEN_MAIN_IDS_KEY = "unmapped-component:seen-main-ids";
|
|
3445
3498
|
function codeConnectIsSetUp(context) {
|
|
3446
3499
|
return getAnalysisState(context, CODE_CONNECT_SETUP_KEY, () => {
|
|
3447
3500
|
return existsSync(join(process.cwd(), "figma.config.json"));
|
|
@@ -3454,6 +3507,9 @@ function codeConnectMappings(context) {
|
|
|
3454
3507
|
() => parseCodeConnectMappings(process.cwd())
|
|
3455
3508
|
);
|
|
3456
3509
|
}
|
|
3510
|
+
function seenMainIds(context) {
|
|
3511
|
+
return getAnalysisState(context, SEEN_MAIN_IDS_KEY, () => /* @__PURE__ */ new Set());
|
|
3512
|
+
}
|
|
3457
3513
|
var unmappedComponentDef = {
|
|
3458
3514
|
id: "unmapped-component",
|
|
3459
3515
|
name: "Unmapped Component",
|
|
@@ -3463,20 +3519,33 @@ var unmappedComponentDef = {
|
|
|
3463
3519
|
fix: "Run /canicode-roundtrip on this component to register a mapping. Figma's get_code_connect_map will skip if a mapping already exists."
|
|
3464
3520
|
};
|
|
3465
3521
|
var unmappedComponentCheck = (node, context) => {
|
|
3466
|
-
if (node.type !== "COMPONENT" && node.type !== "COMPONENT_SET") return null;
|
|
3467
|
-
if (isInsideInstance(context)) return null;
|
|
3468
3522
|
if (!codeConnectIsSetUp(context)) return null;
|
|
3523
|
+
let mainId = null;
|
|
3524
|
+
let mainName = node.name;
|
|
3525
|
+
if (node.type === "COMPONENT" || node.type === "COMPONENT_SET") {
|
|
3526
|
+
if (isInsideInstance(context)) return null;
|
|
3527
|
+
mainId = node.id;
|
|
3528
|
+
} else if (node.type === "INSTANCE" && node.componentId) {
|
|
3529
|
+
mainId = node.componentId;
|
|
3530
|
+
const meta = context.file.components[node.componentId];
|
|
3531
|
+
if (meta?.name) mainName = meta.name;
|
|
3532
|
+
} else {
|
|
3533
|
+
return null;
|
|
3534
|
+
}
|
|
3535
|
+
const seen = seenMainIds(context);
|
|
3536
|
+
if (seen.has(mainId)) return null;
|
|
3537
|
+
seen.add(mainId);
|
|
3469
3538
|
const mappings = codeConnectMappings(context);
|
|
3470
|
-
if (mappings.mappedNodeIds.has(
|
|
3471
|
-
const ack = context.findAcknowledgment(
|
|
3539
|
+
if (mappings.mappedNodeIds.has(mainId)) return null;
|
|
3540
|
+
const ack = context.findAcknowledgment(mainId, unmappedComponentDef.id);
|
|
3472
3541
|
if (ack && isRuleOptOutIntent(ack.intent) && ack.intent.ruleId === unmappedComponentDef.id) {
|
|
3473
3542
|
return null;
|
|
3474
3543
|
}
|
|
3475
3544
|
return {
|
|
3476
3545
|
ruleId: unmappedComponentDef.id,
|
|
3477
|
-
nodeId:
|
|
3546
|
+
nodeId: mainId,
|
|
3478
3547
|
nodePath: context.path.join(" > "),
|
|
3479
|
-
...unmappedComponentMsg(
|
|
3548
|
+
...unmappedComponentMsg(mainName)
|
|
3480
3549
|
};
|
|
3481
3550
|
};
|
|
3482
3551
|
var unmappedComponent = defineRule({
|