flet-box 1.1.2 → 1.1.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/AGENTS_FOR_CODE_GEN.md +284 -0
- package/CONTRIBUTING.md +238 -0
- package/README.md +2 -2
- package/bin/cli.js +88 -1
- package/bin/commands/auditFix.js +181 -27
- package/bin/commands/checkApi.js +14 -3
- package/bin/commands/checkStructure.js +368 -0
- package/bin/commands/checkSyntax.js +293 -0
- package/bin/commands/checkUi.js +666 -14
- package/bin/commands/create.js +14 -0
- package/bin/commands/createBundle.js +2 -2
- package/bin/commands/metrics.js +448 -0
- package/bin/commands/release.js +18 -0
- package/bin/commands/report.js +849 -0
- package/bin/commands/tools.js +76 -0
- package/bin/commands/unpack.js +39 -11
- package/bin/commands/verify.js +879 -0
- package/bin/mcp.js +280 -0
- package/bin/utils/contract.js +27 -0
- package/bin/utils/dtsProps.js +5 -1
- package/bin/utils/migrationGuide.js +146 -0
- package/bin/utils/moduleExports.js +9 -5
- package/bin/utils/templates.js +225 -18
- package/bin/utils/toolkit.js +191 -0
- package/docs/README.md +5 -1
- package/docs/arquitectura.md +1 -1
- package/docs/cli/README.md +168 -2
- package/docs/guides/README.md +6 -0
- package/docs/guides/accessibility.md +2 -1
- package/docs/guides/ai-toolkit.md +166 -0
- package/docs/guides/compatibility.md +1 -1
- package/docs/guides/migration.md +92 -0
- package/docs/guides/minification-and-protection.md +2 -1
- package/docs/guides/mobile-and-platforms.md +1 -1
- package/docs/guides/online-updates.md +2 -0
- package/docs/server/deployment.md +1 -1
- package/docs/tools/styling.md +2 -2
- package/docs/tools/theme.md +139 -4
- package/docs/widget/Accordion.md +74 -8
- package/docs/widget/AdMob.md +68 -3
- package/docs/widget/AdSense.md +67 -2
- package/docs/widget/AlertDialog.md +1 -1
- package/docs/widget/AnimatedBox.md +80 -39
- package/docs/widget/AnimatedText.md +74 -18
- package/docs/widget/Audio.md +73 -14
- package/docs/widget/Avatar.md +69 -4
- package/docs/widget/Badge.md +68 -3
- package/docs/widget/BottomSheet.md +2 -2
- package/docs/widget/Button.md +69 -3
- package/docs/widget/Card.md +69 -4
- package/docs/widget/Carousel.md +67 -2
- package/docs/widget/Chart.md +67 -2
- package/docs/widget/Checkbox.md +74 -8
- package/docs/widget/Chip.md +68 -3
- package/docs/widget/CircularBar.md +74 -15
- package/docs/widget/CircularChart.md +71 -10
- package/docs/widget/CodeViewer.md +67 -2
- package/docs/widget/Column.md +68 -3
- package/docs/widget/Container.md +68 -3
- package/docs/widget/DataTable.md +67 -2
- package/docs/widget/Divider.md +67 -2
- package/docs/widget/DraggBox.md +67 -2
- package/docs/widget/Dropdown.md +76 -10
- package/docs/widget/DroppBox.md +67 -2
- package/docs/widget/FloatingActionButton.md +68 -3
- package/docs/widget/GridView.md +67 -2
- package/docs/widget/Icon.md +71 -5
- package/docs/widget/Image.md +68 -3
- package/docs/widget/Input.md +68 -3
- package/docs/widget/Inspector.md +2 -2
- package/docs/widget/InstallButton.md +73 -13
- package/docs/widget/ListTile.md +69 -3
- package/docs/widget/ListView.md +67 -2
- package/docs/widget/Maps.md +68 -3
- package/docs/widget/Markdown.md +74 -15
- package/docs/widget/MatrixRain.md +72 -11
- package/docs/widget/Menu.md +209 -0
- package/docs/widget/Modal.md +3 -3
- package/docs/widget/Pagination.md +68 -3
- package/docs/widget/ParallaxBox.md +72 -12
- package/docs/widget/ProgressBar.md +69 -4
- package/docs/widget/QRCode.md +67 -2
- package/docs/widget/README.md +54 -50
- package/docs/widget/Radio.md +76 -10
- package/docs/widget/Rating.md +70 -6
- package/docs/widget/Row.md +68 -3
- package/docs/widget/Skeleton.md +68 -3
- package/docs/widget/Slider.md +75 -9
- package/docs/widget/SnackBar.md +2 -2
- package/docs/widget/Stack.md +68 -3
- package/docs/widget/Stepper.md +68 -3
- package/docs/widget/Switch.md +74 -8
- package/docs/widget/Text.md +71 -4
- package/docs/widget/ThemePicker.md +197 -0
- package/docs/widget/Tooltip.md +72 -7
- package/docs/widget/TreeView.md +70 -10
- package/docs/widget/Video.md +71 -10
- package/package.json +10 -3
- package/skills/fletbox-design/SKILL.md +89 -5
- package/skills/fletbox-motion/SKILL.md +1 -1
- package/skills/fletbox-rules/SKILL.md +56 -4
- package/src/core/runApp.js +12 -0
- package/src/index.d.ts +374 -101
- package/src/index.js +9 -0
- package/src/navigations/AdaptiveScaffold.js +2 -2
- package/src/navigations/BottomNavigation.js +1 -1
- package/src/navigations/Scaffold.js +18 -6
- package/src/tools/useState.js +11 -4
- package/src/utils/colors.js +275 -0
- package/src/utils/errorBoundary.js +288 -0
- package/src/utils/index.js +10 -2
- package/src/utils/stackPosition.js +39 -24
- package/src/utils/syntaxHighlight.js +4 -1
- package/src/utils/themes.js +279 -3
- package/src/widget-factory/addChildren.js +17 -10
- package/src/widget-factory/assignProps.js +18 -6
- package/src/widget-factory/createWidget.js +7 -0
- package/src/widget-factory/deprecations.js +77 -0
- package/src/widget-factory/translateProps.js +11 -1
- package/src/widget-factory/warnings.js +1 -27
- package/src/widget-factory/widgetFactory.js +59 -17
- package/src/widgets/Accordion.js +21 -7
- package/src/widgets/Badge.js +11 -2
- package/src/widgets/Button.js +15 -3
- package/src/widgets/Carousel.js +12 -4
- package/src/widgets/Chart.js +7 -2
- package/src/widgets/Checkbox.js +1 -2
- package/src/widgets/Chip.js +24 -10
- package/src/widgets/CircularBar.js +9 -3
- package/src/widgets/CircularChart.js +5 -3
- package/src/widgets/Container.js +6 -0
- package/src/widgets/Divider.js +3 -1
- package/src/widgets/DraggBox.js +9 -0
- package/src/widgets/Dropdown.js +3 -3
- package/src/widgets/DroppBox.js +5 -0
- package/src/widgets/FloatingActionButton.js +1 -1
- package/src/widgets/Icon.js +5 -1
- package/src/widgets/InstallButton.js +4 -2
- package/src/widgets/Markdown.js +171 -103
- package/src/widgets/Menu.js +340 -0
- package/src/widgets/Modal.js +7 -1
- package/src/widgets/Pagination.js +10 -5
- package/src/widgets/ProgressBar.js +5 -4
- package/src/widgets/QRCode.js +4 -0
- package/src/widgets/Radio.js +1 -2
- package/src/widgets/Rating.js +9 -0
- package/src/widgets/Slider.js +4 -1
- package/src/widgets/SnackBar.js +2 -2
- package/src/widgets/Switch.js +1 -2
- package/src/widgets/Text.js +9 -5
- package/src/widgets/ThemePicker.js +234 -0
- package/src/widgets/TreeView.js +2 -1
- package/src/widgets/index.js +2 -0
- package/docs/CONTRIBUTING.md +0 -156
package/bin/commands/auditFix.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `flet-box audit-fix` — write what `check-api` only reports.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Three repairs, all anchored to spans the gate already knows how to find:
|
|
5
5
|
*
|
|
6
|
-
* imports
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* props
|
|
11
|
-
*
|
|
12
|
-
*
|
|
6
|
+
* imports a relative path that does not resolve, a name the target module
|
|
7
|
+
* never exports, a default import from a module without a default,
|
|
8
|
+
* and identifiers used with no import at all (a flet-box export, or
|
|
9
|
+
* a Node builtin used as `path.join(…)`).
|
|
10
|
+
* props a key inside `Widget({ … })` that neither the widget's declared
|
|
11
|
+
* props, the structural props, nor the engine registry knows —
|
|
12
|
+
* `chilldren` → `children`, `postion` → `position`.
|
|
13
|
+
* migrations a key inside `Widget({ … })` that the widget still honours but no
|
|
14
|
+
* longer recommends — `Radio({ onSelect })` → `Radio({ onPress })`.
|
|
13
15
|
*
|
|
14
16
|
* A rewrite only happens when the intended name is unambiguous: one candidate,
|
|
15
17
|
* edit distance small enough that no real name could collide with it. Anything
|
|
@@ -23,6 +25,7 @@ import { c, banner, section } from "../utils/colors.js";
|
|
|
23
25
|
import { neutralise } from "../utils/neutralise.js";
|
|
24
26
|
import { resolveLocalModule, readModuleExports } from "../utils/moduleExports.js";
|
|
25
27
|
import { resolveInterfaceProps } from "../utils/dtsProps.js";
|
|
28
|
+
import { deprecationFor } from "../../src/widget-factory/deprecations.js";
|
|
26
29
|
import {
|
|
27
30
|
STRUCTURAL_PROPS,
|
|
28
31
|
collectSources,
|
|
@@ -252,9 +255,13 @@ const strictMatch = (wanted, candidates) => {
|
|
|
252
255
|
/**
|
|
253
256
|
* Top-level keys of an object literal, with their spans.
|
|
254
257
|
*
|
|
258
|
+
* `entryEnd` is where the whole entry ends — the key *and* its value — which
|
|
259
|
+
* is what a removal has to take, since deleting a key without its value would
|
|
260
|
+
* leave the value behind.
|
|
261
|
+
*
|
|
255
262
|
* @param {string} code comment/string-free source
|
|
256
263
|
* @param {number} open index of `{`
|
|
257
|
-
* @returns {{ keys: Array<{ name: string, start: number, end: number }>, opaque: boolean }}
|
|
264
|
+
* @returns {{ keys: Array<{ name: string, start: number, end: number, entryEnd: number }>, opaque: boolean }}
|
|
258
265
|
*/
|
|
259
266
|
const literalKeySpans = (code, open) => {
|
|
260
267
|
const keys = [];
|
|
@@ -268,6 +275,7 @@ const literalKeySpans = (code, open) => {
|
|
|
268
275
|
const at = entryStart + (text ? lead : 0);
|
|
269
276
|
current = "";
|
|
270
277
|
if (!text) return;
|
|
278
|
+
const entryEnd = at + text.length;
|
|
271
279
|
if (text.startsWith("...")) {
|
|
272
280
|
opaque = true;
|
|
273
281
|
return;
|
|
@@ -275,9 +283,9 @@ const literalKeySpans = (code, open) => {
|
|
|
275
283
|
const key = /^(?:async\s+)?['"]?([A-Za-z_$][\w$]*)['"]?\s*:/.exec(text);
|
|
276
284
|
if (key) {
|
|
277
285
|
const start = at + text.indexOf(key[1]);
|
|
278
|
-
keys.push({ name: key[1], start, end: start + key[1].length });
|
|
286
|
+
keys.push({ name: key[1], start, end: start + key[1].length, entryEnd });
|
|
279
287
|
} else if (/^[A-Za-z_$][\w$]*$/.test(text)) {
|
|
280
|
-
keys.push({ name: text, start: at, end: at + text.length });
|
|
288
|
+
keys.push({ name: text, start: at, end: at + text.length, entryEnd });
|
|
281
289
|
}
|
|
282
290
|
};
|
|
283
291
|
for (let i = open; i < code.length; i++) {
|
|
@@ -518,20 +526,15 @@ const planImports = (file, code, api, allSources) => {
|
|
|
518
526
|
};
|
|
519
527
|
|
|
520
528
|
/**
|
|
521
|
-
*
|
|
529
|
+
* The widgets an app imported from flet-box, as `local name → exported name`.
|
|
522
530
|
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
* @
|
|
531
|
+
* Only capitalised exports are widgets; the rest of the barrel is functions
|
|
532
|
+
* and tools, and a call to one of those never carries a props object.
|
|
533
|
+
*
|
|
534
|
+
* @param {string} withStrings Neutralised source, strings kept.
|
|
535
|
+
* @returns {Map<string, string>}
|
|
527
536
|
*/
|
|
528
|
-
const
|
|
529
|
-
const edits = [];
|
|
530
|
-
const skipped = [];
|
|
531
|
-
const rel = path.relative(api.root, file);
|
|
532
|
-
const withStrings = neutralise(code, { keepStrings: true });
|
|
533
|
-
const body = neutralise(code);
|
|
534
|
-
|
|
537
|
+
const fletWidgetImports = (withStrings) => {
|
|
535
538
|
const localToWidget = new Map();
|
|
536
539
|
for (const match of withStrings.matchAll(IMPORT_RE)) {
|
|
537
540
|
if (match[2] !== "flet-box") continue;
|
|
@@ -546,6 +549,54 @@ const planProps = (file, code, api) => {
|
|
|
546
549
|
if (/^[A-Z]/.test(exported)) localToWidget.set(local, exported);
|
|
547
550
|
}
|
|
548
551
|
}
|
|
552
|
+
return localToWidget;
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* The value written after a key of an object literal, or null.
|
|
557
|
+
*
|
|
558
|
+
* @param {string} code Source with the strings kept — a neutralised string
|
|
559
|
+
* keeps its quotes but loses its content, and the content is the value.
|
|
560
|
+
* @param {{ end: number }} key The key's span.
|
|
561
|
+
* @returns {string|null}
|
|
562
|
+
*/
|
|
563
|
+
const keyValue = (code, key) => {
|
|
564
|
+
const match = /^\s*:\s*['"]?([^'"\s,}]+)/.exec(code.slice(key.end, key.end + 80));
|
|
565
|
+
return match ? match[1] : null;
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* The span that removes one entry of an object literal, comma included.
|
|
570
|
+
*
|
|
571
|
+
* @param {Array<{ start: number, end: number, entryEnd: number }>} keys
|
|
572
|
+
* @param {number} index The entry to remove.
|
|
573
|
+
* @returns {{ start: number, end: number }}
|
|
574
|
+
*/
|
|
575
|
+
const removalSpan = (keys, index) => {
|
|
576
|
+
const key = keys[index];
|
|
577
|
+
const next = keys[index + 1];
|
|
578
|
+
if (next) return { start: key.start, end: next.start };
|
|
579
|
+
const prev = keys[index - 1];
|
|
580
|
+
if (prev) return { start: prev.entryEnd, end: key.entryEnd };
|
|
581
|
+
return { start: key.start, end: key.entryEnd };
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Plan the prop-key rewrites for one file.
|
|
586
|
+
*
|
|
587
|
+
* @param {string} file
|
|
588
|
+
* @param {string} code
|
|
589
|
+
* @param {object} api
|
|
590
|
+
* @returns {{ edits: Edit[], skipped: string[] }}
|
|
591
|
+
*/
|
|
592
|
+
const planProps = (file, code, api) => {
|
|
593
|
+
const edits = [];
|
|
594
|
+
const skipped = [];
|
|
595
|
+
const rel = path.relative(api.root, file);
|
|
596
|
+
const withStrings = neutralise(code, { keepStrings: true });
|
|
597
|
+
const body = neutralise(code);
|
|
598
|
+
|
|
599
|
+
const localToWidget = fletWidgetImports(withStrings);
|
|
549
600
|
|
|
550
601
|
for (const [local, exported] of localToWidget) {
|
|
551
602
|
const iface = `${exported}Props`;
|
|
@@ -579,6 +630,103 @@ const planProps = (file, code, api) => {
|
|
|
579
630
|
return { edits, skipped };
|
|
580
631
|
};
|
|
581
632
|
|
|
633
|
+
/**
|
|
634
|
+
* Plan the renames an app can make without touching a line by hand.
|
|
635
|
+
*
|
|
636
|
+
* A prop the framework still honours but no longer recommends is not broken
|
|
637
|
+
* code, so `check-api` has nothing to say about it — but every screen that
|
|
638
|
+
* keeps the old name keeps an old spelling forever. The table in
|
|
639
|
+
* `src/widget-factory/deprecations.js` is the list, and it is per widget
|
|
640
|
+
* because the same name means different things in different widgets.
|
|
641
|
+
*
|
|
642
|
+
* The rewrite only happens when it is provably the same call: the widget has to
|
|
643
|
+
* declare the old name, it has to declare the new one, and the call must not
|
|
644
|
+
* already carry the new one (two callbacks for one event is a decision, not a
|
|
645
|
+
* rename).
|
|
646
|
+
*
|
|
647
|
+
* A row with no target (`to: null`) is a prop nobody reads any more: the key
|
|
648
|
+
* is removed, together with the comma that held it.
|
|
649
|
+
*
|
|
650
|
+
* @param {string} file
|
|
651
|
+
* @param {string} code
|
|
652
|
+
* @param {object} api
|
|
653
|
+
* @returns {{ edits: Edit[], skipped: string[] }}
|
|
654
|
+
*/
|
|
655
|
+
const planMigrations = (file, code, api) => {
|
|
656
|
+
const edits = [];
|
|
657
|
+
const skipped = [];
|
|
658
|
+
const rel = path.relative(api.root, file);
|
|
659
|
+
const withStrings = neutralise(code, { keepStrings: true });
|
|
660
|
+
const body = neutralise(code);
|
|
661
|
+
|
|
662
|
+
for (const [local, exported] of fletWidgetImports(withStrings)) {
|
|
663
|
+
const iface = `${exported}Props`;
|
|
664
|
+
if (!api.interfaces.has(iface)) continue;
|
|
665
|
+
const declared = resolveInterfaceProps(api.interfaces, iface);
|
|
666
|
+
const callRe = new RegExp(`\\b${escapeRe(local)}\\s*\\(\\s*\\{`, "g");
|
|
667
|
+
let call;
|
|
668
|
+
while ((call = callRe.exec(body))) {
|
|
669
|
+
const open = call.index + call[0].length - 1;
|
|
670
|
+
const { keys, opaque } = literalKeySpans(body, open);
|
|
671
|
+
if (opaque) continue;
|
|
672
|
+
const byName = new Map(keys.map((entry) => [entry.name, entry]));
|
|
673
|
+
|
|
674
|
+
for (const key of keys) {
|
|
675
|
+
const rule = deprecationFor(exported, key.name);
|
|
676
|
+
if (!rule || !rule.to) continue;
|
|
677
|
+
if (!declared.has(rule.from) || !declared.has(rule.to)) {
|
|
678
|
+
skipped.push(`${rel}: ${exported}({ ${key.name} }) — the widget does not declare the pair`);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
const inert = (rule.guard || []).find((name) => byName.has(name));
|
|
682
|
+
if (inert) {
|
|
683
|
+
skipped.push(
|
|
684
|
+
`${rel}: ${exported}({ ${key.name} }) — ${inert} is already there, so the old name does nothing`,
|
|
685
|
+
);
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
let target = rule.to;
|
|
689
|
+
if (rule.alternate) {
|
|
690
|
+
const side = byName.get(rule.alternate.prop);
|
|
691
|
+
if (side && keyValue(withStrings, side) === rule.alternate.value) {
|
|
692
|
+
target = rule.alternate.to;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (byName.has(target)) {
|
|
696
|
+
skipped.push(
|
|
697
|
+
`${rel}: ${exported}({ ${key.name} }) — ${target} is already there; two callbacks for one event is your call`,
|
|
698
|
+
);
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
edits.push({
|
|
702
|
+
start: key.start,
|
|
703
|
+
end: key.end,
|
|
704
|
+
text: target,
|
|
705
|
+
note: `${exported}({ ${c("brightRed", key.name)} }) → ${c("brightGreen", target)}`,
|
|
706
|
+
kind: "migration",
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// The removals run after the renames: a prop that only meant something
|
|
711
|
+
// next to another one goes away with it.
|
|
712
|
+
for (let index = 0; index < keys.length; index++) {
|
|
713
|
+
const key = keys[index];
|
|
714
|
+
const rule = deprecationFor(exported, key.name);
|
|
715
|
+
if (!rule || rule.to) continue;
|
|
716
|
+
if (!declared.has(rule.from)) continue;
|
|
717
|
+
edits.push({
|
|
718
|
+
...removalSpan(keys, index),
|
|
719
|
+
text: "",
|
|
720
|
+
note: `${exported}({ ${c("brightRed", key.name)} }) → ${c("brightGreen", "removed")}`,
|
|
721
|
+
kind: "migration",
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return { edits, skipped };
|
|
728
|
+
};
|
|
729
|
+
|
|
582
730
|
/**
|
|
583
731
|
* Apply planned edits to one file.
|
|
584
732
|
*
|
|
@@ -615,13 +763,14 @@ export const auditFix = async (argv = [], io = {}) => {
|
|
|
615
763
|
return i >= 0 && argv[i + 1] ? path.resolve(argv[i + 1]) : process.cwd();
|
|
616
764
|
})();
|
|
617
765
|
const onlyFlag = argv.includes("--only") ? argv[argv.indexOf("--only") + 1] : null;
|
|
618
|
-
if (onlyFlag && !["imports", "props"].includes(onlyFlag)) {
|
|
619
|
-
print(c("red", `--only expects "imports" or "
|
|
766
|
+
if (onlyFlag && !["imports", "props", "migrations"].includes(onlyFlag)) {
|
|
767
|
+
print(c("red", `--only expects "imports", "props" or "migrations", got "${onlyFlag}"`));
|
|
620
768
|
return { exitCode: 1, changed: [], untouched: [] };
|
|
621
769
|
}
|
|
622
770
|
const only = {
|
|
623
771
|
imports: !onlyFlag || onlyFlag === "imports",
|
|
624
772
|
props: !onlyFlag || onlyFlag === "props",
|
|
773
|
+
migrations: !onlyFlag || onlyFlag === "migrations",
|
|
625
774
|
};
|
|
626
775
|
|
|
627
776
|
let api;
|
|
@@ -637,7 +786,11 @@ export const auditFix = async (argv = [], io = {}) => {
|
|
|
637
786
|
const files = collectSources(root);
|
|
638
787
|
const changed = [];
|
|
639
788
|
const untouched = [];
|
|
640
|
-
const passes = [
|
|
789
|
+
const passes = [
|
|
790
|
+
only.imports && "imports",
|
|
791
|
+
only.props && "props",
|
|
792
|
+
only.migrations && "migrations",
|
|
793
|
+
].filter(Boolean);
|
|
641
794
|
|
|
642
795
|
print(banner("🔧 FletBox audit fix", `${files.length} file(s) · ${passes.join(" + ")}`));
|
|
643
796
|
|
|
@@ -651,6 +804,7 @@ export const auditFix = async (argv = [], io = {}) => {
|
|
|
651
804
|
const runs = [];
|
|
652
805
|
if (only.imports) runs.push(() => planImports(file, text, api, files));
|
|
653
806
|
if (only.props) runs.push(() => planProps(file, text, api));
|
|
807
|
+
if (only.migrations) runs.push(() => planMigrations(file, text, api));
|
|
654
808
|
for (const run of runs) {
|
|
655
809
|
const plan = run();
|
|
656
810
|
untouched.push(...plan.skipped);
|
|
@@ -673,7 +827,7 @@ export const auditFix = async (argv = [], io = {}) => {
|
|
|
673
827
|
|
|
674
828
|
print(
|
|
675
829
|
changed.length
|
|
676
|
-
? `\n${c("brightGreen", `${changed.length} file(s) rewritten`)} ${c("gray", "— run npm run
|
|
830
|
+
? `\n${c("brightGreen", `${changed.length} file(s) rewritten`)} ${c("gray", "— run npm run verify to confirm, and git diff to review.")}`
|
|
677
831
|
: c("gray", "\nNothing to rewrite — every import resolves and every prop is known."),
|
|
678
832
|
);
|
|
679
833
|
return { exitCode: 0, changed, untouched };
|
package/bin/commands/checkApi.js
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
import { c, banner, section } from "../utils/colors.js";
|
|
38
38
|
import { neutralise } from "../utils/neutralise.js";
|
|
39
39
|
import { resolveLocalModule, readModuleExports } from "../utils/moduleExports.js";
|
|
40
|
+
import { deprecationFor } from "../../src/widget-factory/deprecations.js";
|
|
40
41
|
|
|
41
42
|
const require = createRequire(import.meta.url);
|
|
42
43
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -538,7 +539,7 @@ const ERROR_CODES = new Set([
|
|
|
538
539
|
"MISSING_EXPORT",
|
|
539
540
|
]);
|
|
540
541
|
|
|
541
|
-
const SEVERITY = {
|
|
542
|
+
export const SEVERITY = {
|
|
542
543
|
WRONG_STACK: "error",
|
|
543
544
|
UNKNOWN_EXPORT: "error",
|
|
544
545
|
UNKNOWN_PROP: "error",
|
|
@@ -600,7 +601,7 @@ const entry = (prop, info, api) => {
|
|
|
600
601
|
* @param {string} projectRoot
|
|
601
602
|
* @returns {string} written path
|
|
602
603
|
*/
|
|
603
|
-
const emitCheatsheet = (api, projectRoot) => {
|
|
604
|
+
export const emitCheatsheet = (api, projectRoot) => {
|
|
604
605
|
const widgetNames = api.exports
|
|
605
606
|
.filter((name) => /^[A-Z]/.test(name) && api.interfaces.has(`${name}Props`))
|
|
606
607
|
.sort();
|
|
@@ -614,6 +615,10 @@ const emitCheatsheet = (api, projectRoot) => {
|
|
|
614
615
|
`Regenerate with \`flet-box check-api --emit-api\`.`,
|
|
615
616
|
);
|
|
616
617
|
out.push(`Widgets: ${widgetNames.length} · engine props: ${api.ALL_PROPS.length}`);
|
|
618
|
+
out.push(
|
|
619
|
+
`A prop marked \`(deprecated → x)\` still works and is not the name to write; ` +
|
|
620
|
+
`\`npm run fix\` renames the call it can prove.`,
|
|
621
|
+
);
|
|
617
622
|
out.push(``);
|
|
618
623
|
out.push(`## Common props (every widget)`);
|
|
619
624
|
out.push(
|
|
@@ -629,7 +634,13 @@ const emitCheatsheet = (api, projectRoot) => {
|
|
|
629
634
|
[...own]
|
|
630
635
|
.map(([prop, info]) => {
|
|
631
636
|
const type = info.type.length > 46 ? `${info.type.slice(0, 43)}…` : info.type;
|
|
632
|
-
|
|
637
|
+
// The cheatsheet is the first file an agent reads, so a renamed prop has
|
|
638
|
+
// to say so here. Without the marker `onPress` and `onToggle` look like
|
|
639
|
+
// two equally good names, and half the calls built from this file carry
|
|
640
|
+
// the one the framework is trying to retire.
|
|
641
|
+
const rule = deprecationFor(name, prop);
|
|
642
|
+
const mark = rule ? ` (deprecated → ${rule.to ?? "removed"})` : "";
|
|
643
|
+
return `${prop}${info.optional ? "?" : ""}: ${type}${mark}`;
|
|
633
644
|
})
|
|
634
645
|
.join(" · "),
|
|
635
646
|
);
|