@motion-proto/live-tokens 0.97.0 → 0.99.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/.claude/skills/live-tokens-check-compliance/SKILL.md +5 -0
- package/.claude/skills/live-tokens-create-component/SKILL.md +4 -3
- package/.claude/skills/live-tokens-create-component/references/contract-tests.md +71 -11
- package/CHANGELOG.md +339 -0
- package/README.md +2 -2
- package/bin/check-component.mjs +8 -0
- package/bin/check-page.mjs +6 -0
- package/bin/cli.mjs +36 -6
- package/bin/contractRunner.mjs +144 -19
- package/bin/lib/findings.mjs +20 -4
- package/bin/rules/a11y.mjs +53 -0
- package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
- package/dist-plugin/index.cjs +204 -3
- package/dist-plugin/index.js +306 -105
- package/dist-plugin/setColors/index.js +1 -1
- package/package.json +47 -2
- package/src/demo/LabeledSelect.svelte +1 -1
- package/src/editor/bootstrap.ts +5 -1
- package/src/editor/component-editor/BadgeEditor.svelte +13 -0
- package/src/editor/component-editor/ButtonEditor.svelte +33 -2
- package/src/editor/component-editor/CalloutEditor.svelte +13 -0
- package/src/editor/component-editor/CardEditor.svelte +12 -2
- package/src/editor/component-editor/CodeSnippetEditor.svelte +11 -1
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +18 -1
- package/src/editor/component-editor/CornerBadgeEditor.svelte +13 -0
- package/src/editor/component-editor/DialogEditor.svelte +15 -0
- package/src/editor/component-editor/IconButtonEditor.svelte +29 -1
- package/src/editor/component-editor/ImageEditor.svelte +20 -9
- package/src/editor/component-editor/ImageLightboxEditor.svelte +44 -0
- package/src/editor/component-editor/InlineEditActionsEditor.svelte +13 -1
- package/src/editor/component-editor/InputEditor.svelte +23 -0
- package/src/editor/component-editor/MenuSelectEditor.svelte +27 -1
- package/src/editor/component-editor/NotificationEditor.svelte +15 -0
- package/src/editor/component-editor/PanelEditor.svelte +1 -1
- package/src/editor/component-editor/ProgressBarEditor.svelte +13 -0
- package/src/editor/component-editor/RadioButtonEditor.svelte +20 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +40 -13
- package/src/editor/component-editor/SegmentedControlEditor.svelte +29 -1
- package/src/editor/component-editor/SideNavigationEditor.svelte +40 -1
- package/src/editor/component-editor/SlidePagerEditor.svelte +20 -0
- package/src/editor/component-editor/SliderEditor.svelte +33 -0
- package/src/editor/component-editor/TabBarEditor.svelte +23 -2
- package/src/editor/component-editor/TableEditor.svelte +14 -0
- package/src/editor/component-editor/ToggleEditor.svelte +17 -0
- package/src/editor/component-editor/TooltipEditor.svelte +11 -2
- package/src/editor/component-editor/VideoFrameEditor.svelte +8 -0
- package/src/editor/component-editor/VideoLightboxEditor.svelte +10 -0
- package/src/editor/component-editor/registration.ts +2 -1
- package/src/editor/component-editor/registry.ts +69 -27
- package/src/editor/component-editor/scaffolding/DemoHeader.svelte +2 -0
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +19 -8
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +3 -0
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +70 -3
- package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
- package/src/editor/core/a11y/a11yReport.ts +164 -0
- package/src/editor/core/a11y/a11yReportTypes.ts +77 -0
- package/src/editor/core/a11y/contrastFailures.ts +22 -0
- package/src/editor/core/a11y/contrastFixes.ts +41 -0
- package/src/editor/core/components/contrastPairRegistry.ts +18 -0
- package/src/editor/core/palettes/contrast.ts +44 -0
- package/src/editor/core/palettes/contrastPairs.ts +82 -0
- package/src/editor/core/palettes/resolveColor.ts +77 -0
- package/src/editor/core/store/editorStore.ts +4 -0
- package/src/editor/core/store/editorViewStore.ts +2 -2
- package/src/editor/core/store/unsavedEdits.ts +10 -0
- package/src/editor/core/themes/liveStateStream.ts +7 -1
- package/src/editor/core/themes/parsers/gradient.ts +5 -2
- package/src/editor/docs/chapters.ts +1 -0
- package/src/editor/docs/content/accessibility.md +411 -0
- package/src/editor/docs/content/editing-tokens.md +32 -1
- package/src/editor/docs/content.generated.ts +2 -1
- package/src/editor/pages/ComponentEditorPage.svelte +11 -1
- package/src/editor/pages/EditorShell.svelte +98 -10
- package/src/editor/skill-atlas/skillSources.generated.ts +3 -3
- package/src/editor/skill-atlas/trees/check-compliance.ts +3 -3
- package/src/editor/skill-atlas/trees/create-component.ts +12 -12
- package/src/editor/styles/ui-editor.css +2 -0
- package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
- package/src/editor/ui/GradientEditor.svelte +9 -1
- package/src/editor/ui/ThemePanel.svelte +4 -4
- package/src/editor/ui/UIContrastReadout.svelte +186 -0
- package/src/editor/ui/UIInfoPopover.svelte +16 -2
- package/src/editor/ui/UIPaletteSelector.svelte +9 -0
- package/src/editor/ui/UITokenSelector.svelte +26 -2
- package/src/editor/ui/a11y/A11yByEye.svelte +264 -0
- package/src/editor/ui/a11y/A11yComponentIssues.svelte +121 -0
- package/src/editor/ui/a11y/A11yFindingRow.svelte +119 -0
- package/src/editor/ui/a11y/A11yIssueRow.svelte +152 -0
- package/src/editor/ui/a11y/A11yOverview.svelte +246 -0
- package/src/editor/ui/a11y/A11yPanel.svelte +153 -0
- package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
- package/src/editor/ui/a11y/A11yRailMark.svelte +41 -0
- package/src/editor/ui/a11y/AccessibilityTab.svelte +321 -0
- package/src/editor/ui/a11y/a11yView.ts +257 -0
- package/src/editor/ui/contrastReadout.ts +45 -0
- package/src/live-tokens/data/colors-and-type/halloween.json +1 -1
- package/src/live-tokens/data/themes/autumn.json +35 -35
- package/src/live-tokens/data/themes/halloween.json +36 -36
- package/src/live-tokens/data/themes/midnight-study.json +35 -35
- package/src/live-tokens/data/themes/ocean.json +35 -35
- package/src/live-tokens/data/themes/royal-velvet.json +35 -35
- package/src/live-tokens/data/themes/sketchy.json +35 -35
- package/src/live-tokens/data/themes/spring-meadow.json +35 -35
- package/src/live-tokens/data/themes/sunset.json +35 -35
- package/src/system/components/Badge.svelte +3 -3
- package/src/system/components/Button.svelte +12 -6
- package/src/system/components/CodeSnippet.svelte +8 -1
- package/src/system/components/CornerBadge.svelte +3 -3
- package/src/system/components/Dialog.svelte +43 -14
- package/src/system/components/IconButton.svelte +1 -1
- package/src/system/components/Image.svelte +7 -0
- package/src/system/components/ImageLightbox.svelte +18 -25
- package/src/system/components/InlineEditActions.svelte +1 -1
- package/src/system/components/Input.svelte +9 -2
- package/src/system/components/MenuSelect.svelte +31 -3
- package/src/system/components/Notification.svelte +1 -1
- package/src/system/components/ProgressBar.svelte +7 -1
- package/src/system/components/RadioButton.svelte +36 -3
- package/src/system/components/SegmentedControl.svelte +36 -2
- package/src/system/components/SlidePager.svelte +3 -3
- package/src/system/components/Slider.svelte +19 -15
- package/src/system/components/TabBar.svelte +33 -3
- package/src/system/components/Toggle.svelte +3 -3
- package/src/system/components/VideoFrame.svelte +1 -1
- package/src/system/components/VideoLightbox.svelte +2 -2
- package/src/system/internal/focusTrap.ts +35 -0
- package/src/testing-js/__aria__/badge.yml +40 -0
- package/src/testing-js/__aria__/button.yml +88 -0
- package/src/testing-js/__aria__/callout.yml +12 -0
- package/src/testing-js/__aria__/card.yml +8 -0
- package/src/testing-js/__aria__/codesnippet.yml +21 -0
- package/src/testing-js/__aria__/collapsiblesection.yml +23 -0
- package/src/testing-js/__aria__/cornerbadge.yml +24 -0
- package/src/testing-js/__aria__/dialog.yml +42 -0
- package/src/testing-js/__aria__/iconbutton.yml +57 -0
- package/src/testing-js/__aria__/image.yml +10 -0
- package/src/testing-js/__aria__/imagelightbox.yml +50 -0
- package/src/testing-js/__aria__/inlineeditactions.yml +16 -0
- package/src/testing-js/__aria__/input.yml +61 -0
- package/src/testing-js/__aria__/menuselect.yml +30 -0
- package/src/testing-js/__aria__/notification.yml +30 -0
- package/src/testing-js/__aria__/panel.yml +2 -0
- package/src/testing-js/__aria__/progressbar.yml +2 -0
- package/src/testing-js/__aria__/radiobutton.yml +20 -0
- package/src/testing-js/__aria__/sectiondivider.yml +9 -0
- package/src/testing-js/__aria__/segmentedcontrol.yml +35 -0
- package/src/testing-js/__aria__/sidenavigation.yml +304 -0
- package/src/testing-js/__aria__/slidepager.yml +72 -0
- package/src/testing-js/__aria__/slider.yml +28 -0
- package/src/testing-js/__aria__/tabbar.yml +35 -0
- package/src/testing-js/__aria__/table.yml +144 -0
- package/src/testing-js/__aria__/toggle.yml +12 -0
- package/src/testing-js/__aria__/tooltip.yml +2 -0
- package/src/testing-js/__aria__/videoframe.yml +6 -0
- package/src/testing-js/__aria__/videolightbox.yml +12 -0
- package/src/testing-js/{chunk-WIZ6W7UT.js → chunk-3WCLMOM7.js} +2 -2
- package/src/testing-js/{chunk-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
- package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
- package/src/testing-js/{chunk-X6KHEVIG.js → chunk-7QXUJRSO.js} +97 -29
- package/src/testing-js/chunk-7QXUJRSO.js.map +1 -0
- package/src/testing-js/chunk-GZKUYLDW.js +193 -0
- package/src/testing-js/chunk-GZKUYLDW.js.map +1 -0
- package/src/testing-js/chunk-RTZQLW3F.js +649 -0
- package/src/testing-js/chunk-RTZQLW3F.js.map +1 -0
- package/src/testing-js/{chunk-SHBYZUPD.js → chunk-RWOT2XDB.js} +212 -15
- package/src/testing-js/chunk-RWOT2XDB.js.map +1 -0
- package/src/testing-js/{chunk-GKO27NGN.js → chunk-THWGMSRH.js} +2 -2
- package/src/testing-js/{chunk-3UKGXCDL.js → chunk-YVVR3GJZ.js} +1 -1
- package/src/testing-js/chunk-YVVR3GJZ.js.map +1 -0
- package/src/testing-js/component-a11y.contract.js +313 -0
- package/src/testing-js/component-a11y.contract.js.map +1 -0
- package/src/testing-js/component-alias.contract.js +2 -2
- package/src/testing-js/component-behavior.contract.js +2 -2
- package/src/testing-js/component-editor.contract.js +6 -5
- package/src/testing-js/component-editor.contract.js.map +1 -1
- package/src/testing-js/component-render.contract.js +10 -6
- package/src/testing-js/component-render.contract.js.map +1 -1
- package/src/testing-js/index.d.ts +44 -16
- package/src/testing-js/index.js +11 -11
- package/src/testing-js/index.js.map +1 -1
- package/src/testing-js/page-compliance.contract.js +132 -13
- package/src/testing-js/page-compliance.contract.js.map +1 -1
- package/src/testing-js/vitest.js +3 -3
- package/template/_gitignore +3 -0
- package/template/package.json +9 -0
- package/src/testing-js/chunk-3UKGXCDL.js.map +0 -1
- package/src/testing-js/chunk-NB3NZRBM.js.map +0 -1
- package/src/testing-js/chunk-SHBYZUPD.js.map +0 -1
- package/src/testing-js/chunk-X6KHEVIG.js.map +0 -1
- /package/src/testing-js/{chunk-WIZ6W7UT.js.map → chunk-3WCLMOM7.js.map} +0 -0
- /package/src/testing-js/{chunk-GKO27NGN.js.map → chunk-THWGMSRH.js.map} +0 -0
package/dist-plugin/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
parseGradientValue,
|
|
14
14
|
reconcilePalettesFromCssVars,
|
|
15
15
|
sanitizeFileName
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-SLICH4IH.js";
|
|
17
17
|
import {
|
|
18
18
|
CURRENT_COMPONENT_SCHEMA_VERSION
|
|
19
19
|
} from "./chunk-QQASYPW7.js";
|
|
@@ -36,9 +36,9 @@ import {
|
|
|
36
36
|
} from "./chunk-6WGFOJXO.js";
|
|
37
37
|
|
|
38
38
|
// vite-plugin/themeFileApi.ts
|
|
39
|
-
import
|
|
39
|
+
import fs3 from "fs";
|
|
40
40
|
import { createHash as createHash2 } from "crypto";
|
|
41
|
-
import
|
|
41
|
+
import path4 from "path";
|
|
42
42
|
|
|
43
43
|
// src/editor/core/themes/migrations/2026-07-21-palette-oklch-basis.ts
|
|
44
44
|
var round = (v, dp) => {
|
|
@@ -748,18 +748,158 @@ function formatReport({ propertiesBefore, propertiesAfter, bytesSaved }) {
|
|
|
748
748
|
return `live-tokens: the production CSS declares ${propertiesAfter.toLocaleString("en-US")} of ${propertiesBefore.toLocaleString("en-US")} custom properties and is ${kB} kB ${bytesSaved >= 0 ? "smaller" : "larger"}.`;
|
|
749
749
|
}
|
|
750
750
|
|
|
751
|
+
// vite-plugin/a11y/a11yRunServer.ts
|
|
752
|
+
import { spawn } from "child_process";
|
|
753
|
+
import fs2 from "fs";
|
|
754
|
+
import path3 from "path";
|
|
755
|
+
var A11Y_RUN_ARGS = ["check-component", "--tests", "--a11y", "--no-fix", "--json", "--open-theme"];
|
|
756
|
+
var A11Y_REPORTS_DIR_NAME = "_a11y-reports";
|
|
757
|
+
var STDERR_TAIL = 2e3;
|
|
758
|
+
var CONTRAST_ROLES = /* @__PURE__ */ new Set(["text", "non-text"]);
|
|
759
|
+
function spawnCli(cliPath) {
|
|
760
|
+
return (args, cwd) => {
|
|
761
|
+
const child = spawn(process.execPath, [cliPath, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
762
|
+
const done = new Promise((resolve) => {
|
|
763
|
+
let stdout = "";
|
|
764
|
+
let stderr = "";
|
|
765
|
+
child.stdout.on("data", (chunk) => stdout += chunk);
|
|
766
|
+
child.stderr.on("data", (chunk) => stderr += chunk);
|
|
767
|
+
child.on("error", (error) => resolve({ code: null, stdout, stderr: `${stderr}${error.message}` }));
|
|
768
|
+
child.on("close", (code) => resolve({ code, stdout, stderr }));
|
|
769
|
+
});
|
|
770
|
+
return { done, stop: () => child.kill("SIGINT") };
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function isContrastFailure(value) {
|
|
774
|
+
const v = value;
|
|
775
|
+
return !!v && typeof v === "object" && typeof v.component === "string" && typeof v.variable === "string" && v.variable.startsWith("--") && Array.isArray(v.backdrop) && v.backdrop.every((layer) => typeof layer === "string") && typeof v.role === "string" && CONTRAST_ROLES.has(v.role) && typeof v.ratio === "number" && typeof v.floor === "number" && (v.context === void 0 || typeof v.context === "string");
|
|
776
|
+
}
|
|
777
|
+
function readContrast(body) {
|
|
778
|
+
const contrast = body?.contrast;
|
|
779
|
+
return Array.isArray(contrast) && contrast.every(isContrastFailure) ? contrast : null;
|
|
780
|
+
}
|
|
781
|
+
function parseCliReport(stdout) {
|
|
782
|
+
try {
|
|
783
|
+
const parsed = JSON.parse(stdout);
|
|
784
|
+
return parsed && Array.isArray(parsed.findings) ? parsed : null;
|
|
785
|
+
} catch {
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function a11yRunServer({ root, reportsDir, openTheme, runCli, broadcast }) {
|
|
790
|
+
let running = null;
|
|
791
|
+
const reportFile = (theme) => path3.join(reportsDir, `${theme}.json`);
|
|
792
|
+
function readReport(theme) {
|
|
793
|
+
const listed = fs2.existsSync(reportsDir) && fs2.readdirSync(reportsDir).includes(`${theme}.json`);
|
|
794
|
+
return listed ? JSON.parse(fs2.readFileSync(reportFile(theme), "utf-8")) : null;
|
|
795
|
+
}
|
|
796
|
+
function writeReport(report) {
|
|
797
|
+
fs2.mkdirSync(reportsDir, { recursive: true });
|
|
798
|
+
fs2.writeFileSync(reportFile(report.theme), `${JSON.stringify(report, null, 2)}
|
|
799
|
+
`);
|
|
800
|
+
}
|
|
801
|
+
function withConfirmed(report, confirmed) {
|
|
802
|
+
const review = new Set(report.findings.filter((f) => f.rule === "a11y-review").map((f) => f.message));
|
|
803
|
+
const kept = [...new Set(confirmed)].filter((message) => review.has(message)).sort();
|
|
804
|
+
const out = { ...report, confirmed: kept };
|
|
805
|
+
if (kept.length === 0) delete out.confirmed;
|
|
806
|
+
return out;
|
|
807
|
+
}
|
|
808
|
+
function reports() {
|
|
809
|
+
if (!fs2.existsSync(reportsDir)) return {};
|
|
810
|
+
return Object.fromEntries(
|
|
811
|
+
fs2.readdirSync(reportsDir).filter((name) => name.endsWith(".json")).map((name) => [name.slice(0, -".json".length), JSON.parse(fs2.readFileSync(path3.join(reportsDir, name), "utf-8"))])
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
function settle(started, outcome) {
|
|
815
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
816
|
+
const cli = parseCliReport(outcome.stdout);
|
|
817
|
+
if (!cli) {
|
|
818
|
+
const stderr = outcome.stderr.trim().slice(-STDERR_TAIL);
|
|
819
|
+
return {
|
|
820
|
+
...started,
|
|
821
|
+
state: "failed",
|
|
822
|
+
finishedAt,
|
|
823
|
+
error: `check-component exited with code ${outcome.code} and printed no report.${stderr ? `
|
|
824
|
+
${stderr}` : ""}`
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
return {
|
|
828
|
+
...started,
|
|
829
|
+
state: "finished",
|
|
830
|
+
finishedAt,
|
|
831
|
+
findings: cli.findings.filter((f) => f.details?.engineRule !== "color-contrast")
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
function start(body) {
|
|
835
|
+
if (running) {
|
|
836
|
+
return { status: 409, body: { error: "An accessibility run is already going.", code: "A11Y_RUN_IN_PROGRESS" } };
|
|
837
|
+
}
|
|
838
|
+
const contrast = readContrast(body);
|
|
839
|
+
if (!contrast) {
|
|
840
|
+
return {
|
|
841
|
+
status: 400,
|
|
842
|
+
body: { error: "Body must be { contrast: [...] } with one entry per failing pair", code: "INVALID_BODY" }
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
const report = { state: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString(), theme: openTheme(), contrast };
|
|
846
|
+
const run = runCli(A11Y_RUN_ARGS, root);
|
|
847
|
+
running = { report, stop: run.stop };
|
|
848
|
+
broadcast(report);
|
|
849
|
+
void run.done.then((outcome) => {
|
|
850
|
+
const settled = settle(report, outcome);
|
|
851
|
+
const final = settled.state === "finished" ? withConfirmed(settled, readReport(settled.theme)?.confirmed ?? []) : settled;
|
|
852
|
+
if (final.state === "finished") writeReport(final);
|
|
853
|
+
running = null;
|
|
854
|
+
broadcast(final);
|
|
855
|
+
});
|
|
856
|
+
return { status: 202, body: report };
|
|
857
|
+
}
|
|
858
|
+
function confirm(body) {
|
|
859
|
+
const { theme, messages, confirmed } = body ?? {};
|
|
860
|
+
if (typeof theme !== "string" || typeof confirmed !== "boolean" || !Array.isArray(messages) || !messages.every((m) => typeof m === "string")) {
|
|
861
|
+
return {
|
|
862
|
+
status: 400,
|
|
863
|
+
body: { error: "Body must be { theme, messages: [...], confirmed: true | false }", code: "INVALID_BODY" }
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
const report = readReport(theme);
|
|
867
|
+
if (!report) return { status: 404, body: { error: `No report for theme "${theme}"`, code: "NO_REPORT" } };
|
|
868
|
+
const marked = new Set(report.confirmed ?? []);
|
|
869
|
+
for (const message of messages) {
|
|
870
|
+
if (confirmed) marked.add(message);
|
|
871
|
+
else marked.delete(message);
|
|
872
|
+
}
|
|
873
|
+
const updated = withConfirmed(report, marked);
|
|
874
|
+
writeReport(updated);
|
|
875
|
+
broadcast(updated);
|
|
876
|
+
return { status: 200, body: updated };
|
|
877
|
+
}
|
|
878
|
+
return {
|
|
879
|
+
start,
|
|
880
|
+
confirm,
|
|
881
|
+
report: () => ({ status: 200, body: { running: running?.report ?? null, reports: reports() } }),
|
|
882
|
+
/** Removes a deleted theme's report. */
|
|
883
|
+
forget: (theme) => fs2.rmSync(reportFile(theme), { force: true }),
|
|
884
|
+
stop: () => running?.stop()
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// src/editor/core/a11y/a11yReportTypes.ts
|
|
889
|
+
var A11Y_EVENT = "a11y";
|
|
890
|
+
|
|
751
891
|
// vite-plugin/themeFileApi.ts
|
|
752
892
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
753
893
|
var PKG_VERSION = (() => {
|
|
754
894
|
try {
|
|
755
|
-
let dir =
|
|
895
|
+
let dir = path4.dirname(fileURLToPath2(import.meta.url));
|
|
756
896
|
for (let i = 0; i < 4; i++) {
|
|
757
|
-
const p =
|
|
758
|
-
if (
|
|
759
|
-
const json = JSON.parse(
|
|
897
|
+
const p = path4.join(dir, "package.json");
|
|
898
|
+
if (fs3.existsSync(p)) {
|
|
899
|
+
const json = JSON.parse(fs3.readFileSync(p, "utf-8"));
|
|
760
900
|
if (json?.name === "@motion-proto/live-tokens") return json.version ?? "";
|
|
761
901
|
}
|
|
762
|
-
const up =
|
|
902
|
+
const up = path4.dirname(dir);
|
|
763
903
|
if (up === dir) break;
|
|
764
904
|
dir = up;
|
|
765
905
|
}
|
|
@@ -803,46 +943,48 @@ function themeFileApi(opts) {
|
|
|
803
943
|
const COMPONENT_CONFIGS_DIR = dataDirs.componentConfigsDir;
|
|
804
944
|
const THEMES_DIR = dataDirs.themesDir;
|
|
805
945
|
const SKETCH_STYLES_DIR = dataDirs.sketchStylesDir;
|
|
806
|
-
const CSS_PATH =
|
|
946
|
+
const CSS_PATH = path4.resolve(opts.tokensCssPath);
|
|
807
947
|
const ISOLATED_DATA_DIR = testDataDir();
|
|
808
|
-
const GENERATED_CSS_PATH = ISOLATED_DATA_DIR ?
|
|
809
|
-
const FONTS_CSS_PATH = ISOLATED_DATA_DIR ?
|
|
948
|
+
const GENERATED_CSS_PATH = ISOLATED_DATA_DIR ? path4.join(ISOLATED_DATA_DIR, "tokens.generated.css") : opts.tokensGeneratedCssPath ? path4.resolve(opts.tokensGeneratedCssPath) : path4.join(dataDirs.dataDir, "tokens.generated.css");
|
|
949
|
+
const FONTS_CSS_PATH = ISOLATED_DATA_DIR ? path4.join(ISOLATED_DATA_DIR, "fonts.css") : opts.fontsCssPath ? path4.resolve(opts.fontsCssPath) : path4.join(path4.dirname(CSS_PATH), "fonts.css");
|
|
950
|
+
const A11Y_REPORTS_DIR = path4.join(dataDirs.dataDir, A11Y_REPORTS_DIR_NAME);
|
|
810
951
|
const API_BASE = opts.apiBase ?? "/api/live-tokens";
|
|
811
|
-
const consumerComponentDirs = opts.componentsSrcDir ? [
|
|
812
|
-
const packageComponentsDir =
|
|
813
|
-
|
|
952
|
+
const consumerComponentDirs = opts.componentsSrcDir ? [path4.resolve(opts.componentsSrcDir)] : [path4.resolve("src/components"), path4.resolve("src/system/components")];
|
|
953
|
+
const packageComponentsDir = path4.resolve(
|
|
954
|
+
path4.dirname(fileURLToPath2(import.meta.url)),
|
|
814
955
|
"..",
|
|
815
956
|
"src",
|
|
816
957
|
"system",
|
|
817
958
|
"components"
|
|
818
959
|
);
|
|
819
|
-
const
|
|
820
|
-
|
|
960
|
+
const PACKAGE_CLI_PATH = path4.resolve(path4.dirname(fileURLToPath2(import.meta.url)), "..", "bin", "cli.mjs");
|
|
961
|
+
const packageDataDir = testPackageDataDir() ?? path4.resolve(
|
|
962
|
+
path4.dirname(fileURLToPath2(import.meta.url)),
|
|
821
963
|
"..",
|
|
822
964
|
"src",
|
|
823
965
|
"live-tokens",
|
|
824
966
|
"data"
|
|
825
967
|
);
|
|
826
|
-
const packageColorsAndTypeDir =
|
|
827
|
-
const packageThemesDir =
|
|
828
|
-
const packageComponentConfigsDir =
|
|
829
|
-
const packageSketchStylesDir =
|
|
968
|
+
const packageColorsAndTypeDir = path4.join(packageDataDir, "colors-and-type");
|
|
969
|
+
const packageThemesDir = path4.join(packageDataDir, "themes");
|
|
970
|
+
const packageComponentConfigsDir = path4.join(packageDataDir, "component-configs");
|
|
971
|
+
const packageSketchStylesDir = path4.join(packageDataDir, "sketch-styles");
|
|
830
972
|
function shippedNames(dataDir, subdir) {
|
|
831
973
|
try {
|
|
832
|
-
const pkgRoot =
|
|
833
|
-
const pkg = JSON.parse(
|
|
974
|
+
const pkgRoot = path4.resolve(dataDir, "..", "..", "..");
|
|
975
|
+
const pkg = JSON.parse(fs3.readFileSync(path4.join(pkgRoot, "package.json"), "utf-8"));
|
|
834
976
|
const files = Array.isArray(pkg.files) ? pkg.files : [];
|
|
835
977
|
const owned = new RegExp(`^src/live-tokens/data/${subdir}/[^/]+\\.json$`);
|
|
836
|
-
return files.filter((f) => owned.test(f)).map((f) =>
|
|
978
|
+
return files.filter((f) => owned.test(f)).map((f) => path4.basename(f, ".json"));
|
|
837
979
|
} catch {
|
|
838
980
|
return [];
|
|
839
981
|
}
|
|
840
982
|
}
|
|
841
983
|
const COMPONENTS_SCAN_DIRS = [...consumerComponentDirs];
|
|
842
|
-
if (!COMPONENTS_SCAN_DIRS.includes(packageComponentsDir) &&
|
|
984
|
+
if (!COMPONENTS_SCAN_DIRS.includes(packageComponentsDir) && fs3.existsSync(packageComponentsDir)) {
|
|
843
985
|
COMPONENTS_SCAN_DIRS.push(packageComponentsDir);
|
|
844
986
|
}
|
|
845
|
-
const isLiveStateFile = (file) => file ===
|
|
987
|
+
const isLiveStateFile = (file) => file === path4.join(THEMES_DIR, "_active.json") || path4.basename(file) === "_working.json" && (file.startsWith(COLORS_AND_TYPE_DIR + path4.sep) || file.startsWith(COMPONENT_CONFIGS_DIR + path4.sep));
|
|
846
988
|
const liveWatch = dataTreeWatch({
|
|
847
989
|
dirs: [COLORS_AND_TYPE_DIR, COMPONENT_CONFIGS_DIR, THEMES_DIR],
|
|
848
990
|
isLive: isLiveStateFile
|
|
@@ -858,8 +1000,8 @@ function themeFileApi(opts) {
|
|
|
858
1000
|
let r = componentResourceCache.get(comp);
|
|
859
1001
|
if (!r) {
|
|
860
1002
|
r = versionedFileResourceServer({
|
|
861
|
-
dir:
|
|
862
|
-
packageDir:
|
|
1003
|
+
dir: path4.join(COMPONENT_CONFIGS_DIR, comp),
|
|
1004
|
+
packageDir: path4.join(packageComponentConfigsDir, comp),
|
|
863
1005
|
onWrite: liveWatch.noteOwnWrite
|
|
864
1006
|
});
|
|
865
1007
|
componentResourceCache.set(comp, r);
|
|
@@ -947,7 +1089,7 @@ function themeFileApi(opts) {
|
|
|
947
1089
|
const productionThemeName = themesResource.getProductionName();
|
|
948
1090
|
const productionTheme = readTheme(productionThemeName)?.theme ?? null;
|
|
949
1091
|
if (themesResource.existingPath(productionThemeName) !== null && !productionTheme?.colorsAndType) {
|
|
950
|
-
const tail = `${
|
|
1092
|
+
const tail = `${path4.basename(GENERATED_CSS_PATH)} was left as it was.`;
|
|
951
1093
|
throw new Error(
|
|
952
1094
|
unreadableThemeReason(productionThemeName) === "colors-and-type" ? `[live-tokens] ${notAThemeError(productionThemeName)} ${tail}` : `[live-tokens] Production theme "${productionThemeName}" is unreadable, so ${tail} Repair the theme file or adopt another theme.`
|
|
953
1095
|
);
|
|
@@ -985,7 +1127,7 @@ function themeFileApi(opts) {
|
|
|
985
1127
|
}
|
|
986
1128
|
}
|
|
987
1129
|
let componentOverrideCount = 0;
|
|
988
|
-
if (
|
|
1130
|
+
if (fs3.existsSync(COMPONENT_CONFIGS_DIR)) {
|
|
989
1131
|
const blocks = [];
|
|
990
1132
|
for (const comp of listComponentNames()) {
|
|
991
1133
|
const prodCfg = productionTheme?.componentConfigs[comp];
|
|
@@ -1030,18 +1172,18 @@ function themeFileApi(opts) {
|
|
|
1030
1172
|
`/* Bake fingerprint: ${bakeFingerprint(body)} */`,
|
|
1031
1173
|
""
|
|
1032
1174
|
];
|
|
1033
|
-
if (!
|
|
1034
|
-
|
|
1175
|
+
if (!fs3.existsSync(path4.dirname(GENERATED_CSS_PATH))) {
|
|
1176
|
+
fs3.mkdirSync(path4.dirname(GENERATED_CSS_PATH), { recursive: true });
|
|
1035
1177
|
}
|
|
1036
|
-
|
|
1178
|
+
fs3.writeFileSync(GENERATED_CSS_PATH, [...header, body].join("\n"));
|
|
1037
1179
|
console.log(
|
|
1038
|
-
`[regenerateTokensCss] Wrote ${
|
|
1180
|
+
`[regenerateTokensCss] Wrote ${path4.basename(GENERATED_CSS_PATH)} (${colorsAndTypeVarCount} theme vars, ${componentOverrideCount} component overrides)`
|
|
1039
1181
|
);
|
|
1040
1182
|
}
|
|
1041
1183
|
function bakeIsCurrent() {
|
|
1042
1184
|
let baked;
|
|
1043
1185
|
try {
|
|
1044
|
-
baked =
|
|
1186
|
+
baked = fs3.readFileSync(GENERATED_CSS_PATH, "utf-8");
|
|
1045
1187
|
} catch {
|
|
1046
1188
|
return false;
|
|
1047
1189
|
}
|
|
@@ -1077,18 +1219,18 @@ function themeFileApi(opts) {
|
|
|
1077
1219
|
lines.push("");
|
|
1078
1220
|
}
|
|
1079
1221
|
const content = lines.join("\n");
|
|
1080
|
-
if (!
|
|
1081
|
-
|
|
1222
|
+
if (!fs3.existsSync(path4.dirname(FONTS_CSS_PATH))) {
|
|
1223
|
+
fs3.mkdirSync(path4.dirname(FONTS_CSS_PATH), { recursive: true });
|
|
1082
1224
|
}
|
|
1083
|
-
|
|
1084
|
-
console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${
|
|
1225
|
+
fs3.writeFileSync(FONTS_CSS_PATH, content);
|
|
1226
|
+
console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${path4.basename(FONTS_CSS_PATH)}`);
|
|
1085
1227
|
}
|
|
1086
1228
|
function componentNameFromFile(filePath) {
|
|
1087
|
-
return
|
|
1229
|
+
return path4.basename(filePath, ".svelte").toLowerCase();
|
|
1088
1230
|
}
|
|
1089
1231
|
function isThemeAwareComponent(filePath) {
|
|
1090
1232
|
try {
|
|
1091
|
-
const source =
|
|
1233
|
+
const source = fs3.readFileSync(filePath, "utf-8");
|
|
1092
1234
|
return /:global\(:root\)\s*\{/.test(source);
|
|
1093
1235
|
} catch {
|
|
1094
1236
|
return false;
|
|
@@ -1097,10 +1239,10 @@ function themeFileApi(opts) {
|
|
|
1097
1239
|
function listComponentSourcePaths() {
|
|
1098
1240
|
const byName = /* @__PURE__ */ new Map();
|
|
1099
1241
|
for (const dir of COMPONENTS_SCAN_DIRS) {
|
|
1100
|
-
if (!
|
|
1101
|
-
for (const f of
|
|
1242
|
+
if (!fs3.existsSync(dir)) continue;
|
|
1243
|
+
for (const f of fs3.readdirSync(dir)) {
|
|
1102
1244
|
if (!f.endsWith(".svelte")) continue;
|
|
1103
|
-
const full =
|
|
1245
|
+
const full = path4.join(dir, f);
|
|
1104
1246
|
if (!isThemeAwareComponent(full)) continue;
|
|
1105
1247
|
const name = componentNameFromFile(f);
|
|
1106
1248
|
if (!byName.has(name)) byName.set(name, full);
|
|
@@ -1114,18 +1256,18 @@ function themeFileApi(opts) {
|
|
|
1114
1256
|
function warnOnTokenDrift(log) {
|
|
1115
1257
|
let tokensCss = "";
|
|
1116
1258
|
try {
|
|
1117
|
-
tokensCss =
|
|
1259
|
+
tokensCss = fs3.readFileSync(CSS_PATH, "utf-8");
|
|
1118
1260
|
} catch {
|
|
1119
1261
|
return;
|
|
1120
1262
|
}
|
|
1121
1263
|
let generatedCss = "";
|
|
1122
1264
|
try {
|
|
1123
|
-
generatedCss =
|
|
1265
|
+
generatedCss = fs3.readFileSync(GENERATED_CSS_PATH, "utf-8");
|
|
1124
1266
|
} catch {
|
|
1125
1267
|
}
|
|
1126
1268
|
const componentSources = listComponentSourcePaths().map((p) => ({
|
|
1127
1269
|
name: componentNameFromFile(p),
|
|
1128
|
-
source:
|
|
1270
|
+
source: fs3.readFileSync(p, "utf-8")
|
|
1129
1271
|
}));
|
|
1130
1272
|
const missing = validateTokensCss({ tokensCss, generatedCss, componentSources });
|
|
1131
1273
|
if (missing.length === 0) return;
|
|
@@ -1133,7 +1275,7 @@ function themeFileApi(opts) {
|
|
|
1133
1275
|
(m) => ` ${m.token} (referenced by ${m.referencedBy.join(", ")})`
|
|
1134
1276
|
);
|
|
1135
1277
|
log(
|
|
1136
|
-
`[live-tokens] ${missing.length} token(s) referenced by components are not defined in ${
|
|
1278
|
+
`[live-tokens] ${missing.length} token(s) referenced by components are not defined in ${path4.relative(process.cwd(), CSS_PATH)}:
|
|
1137
1279
|
${lines.join("\n")}
|
|
1138
1280
|
These render as blank/empty editor slots. Run \`npx live-tokens migrate\` to reconcile.`
|
|
1139
1281
|
);
|
|
@@ -1141,30 +1283,30 @@ ${lines.join("\n")}
|
|
|
1141
1283
|
function autoMigrateAdditive(log) {
|
|
1142
1284
|
let before = "";
|
|
1143
1285
|
try {
|
|
1144
|
-
before =
|
|
1286
|
+
before = fs3.readFileSync(CSS_PATH, "utf-8");
|
|
1145
1287
|
} catch {
|
|
1146
1288
|
return;
|
|
1147
1289
|
}
|
|
1148
1290
|
const { css, applied, changed } = runAdditiveTokensCssMigrations(before);
|
|
1149
1291
|
if (!changed) return;
|
|
1150
|
-
|
|
1292
|
+
fs3.writeFileSync(CSS_PATH, css);
|
|
1151
1293
|
log(
|
|
1152
|
-
`[live-tokens] autoMigrate applied ${applied.length} additive migration(s) to ${
|
|
1294
|
+
`[live-tokens] autoMigrate applied ${applied.length} additive migration(s) to ${path4.relative(process.cwd(), CSS_PATH)}: ${applied.join(", ")}. Review the diff in git.`
|
|
1153
1295
|
);
|
|
1154
1296
|
}
|
|
1155
1297
|
function generateDefaultConfig(comp, sourcePath) {
|
|
1156
|
-
if (!
|
|
1298
|
+
if (!fs3.existsSync(sourcePath)) return;
|
|
1157
1299
|
const r = componentResource(comp);
|
|
1158
1300
|
r.ensureDir();
|
|
1159
1301
|
const defaultPath = r.filePath("default");
|
|
1160
|
-
const sourceStat =
|
|
1302
|
+
const sourceStat = fs3.statSync(sourcePath);
|
|
1161
1303
|
let createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1162
1304
|
let existingUpdatedAt;
|
|
1163
1305
|
let existingAliases;
|
|
1164
1306
|
let existingSchemaVersion;
|
|
1165
|
-
if (
|
|
1307
|
+
if (fs3.existsSync(defaultPath)) {
|
|
1166
1308
|
try {
|
|
1167
|
-
const existing = JSON.parse(
|
|
1309
|
+
const existing = JSON.parse(fs3.readFileSync(defaultPath, "utf-8"));
|
|
1168
1310
|
if (existing.createdAt) createdAt = existing.createdAt;
|
|
1169
1311
|
if (typeof existing.updatedAt === "string") existingUpdatedAt = existing.updatedAt;
|
|
1170
1312
|
if (existing.aliases && typeof existing.aliases === "object") {
|
|
@@ -1173,13 +1315,13 @@ ${lines.join("\n")}
|
|
|
1173
1315
|
if (typeof existing.schemaVersion === "number") existingSchemaVersion = existing.schemaVersion;
|
|
1174
1316
|
} catch {
|
|
1175
1317
|
}
|
|
1176
|
-
const defaultStat =
|
|
1318
|
+
const defaultStat = fs3.statSync(defaultPath);
|
|
1177
1319
|
if (defaultStat.mtimeMs >= sourceStat.mtimeMs && existingSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION) {
|
|
1178
1320
|
return;
|
|
1179
1321
|
}
|
|
1180
1322
|
}
|
|
1181
1323
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1182
|
-
const source =
|
|
1324
|
+
const source = fs3.readFileSync(sourcePath, "utf-8");
|
|
1183
1325
|
const body = extractGlobalRootBody(source);
|
|
1184
1326
|
const aliases = extractAliasDeclarations(body);
|
|
1185
1327
|
const aliasesUnchanged = existingAliases !== void 0 && JSON.stringify(existingAliases) === JSON.stringify(aliases);
|
|
@@ -1194,14 +1336,14 @@ ${lines.join("\n")}
|
|
|
1194
1336
|
};
|
|
1195
1337
|
if (aliasesUnchanged && schemaStampCurrent && existingUpdatedAt) {
|
|
1196
1338
|
const t = /* @__PURE__ */ new Date();
|
|
1197
|
-
|
|
1339
|
+
fs3.utimesSync(defaultPath, t, t);
|
|
1198
1340
|
return;
|
|
1199
1341
|
}
|
|
1200
|
-
|
|
1342
|
+
fs3.writeFileSync(defaultPath, JSON.stringify(defaultConfig, null, 2));
|
|
1201
1343
|
}
|
|
1202
1344
|
function ensureComponentConfigsDir() {
|
|
1203
|
-
if (!
|
|
1204
|
-
|
|
1345
|
+
if (!fs3.existsSync(COMPONENT_CONFIGS_DIR)) {
|
|
1346
|
+
fs3.mkdirSync(COMPONENT_CONFIGS_DIR, { recursive: true });
|
|
1205
1347
|
}
|
|
1206
1348
|
for (const sourcePath of listComponentSourcePaths()) {
|
|
1207
1349
|
const comp = componentNameFromFile(sourcePath);
|
|
@@ -1214,14 +1356,14 @@ ${lines.join("\n")}
|
|
|
1214
1356
|
const legacy = [];
|
|
1215
1357
|
const check = (dir) => {
|
|
1216
1358
|
for (const name of ["_active.json", "_production.json"]) {
|
|
1217
|
-
const p =
|
|
1218
|
-
if (
|
|
1359
|
+
const p = path4.join(dir, name);
|
|
1360
|
+
if (fs3.existsSync(p)) legacy.push(path4.relative(process.cwd(), p));
|
|
1219
1361
|
}
|
|
1220
1362
|
};
|
|
1221
1363
|
check(COLORS_AND_TYPE_DIR);
|
|
1222
|
-
if (
|
|
1223
|
-
for (const entry of
|
|
1224
|
-
if (entry.isDirectory()) check(
|
|
1364
|
+
if (fs3.existsSync(COMPONENT_CONFIGS_DIR)) {
|
|
1365
|
+
for (const entry of fs3.readdirSync(COMPONENT_CONFIGS_DIR, { withFileTypes: true })) {
|
|
1366
|
+
if (entry.isDirectory()) check(path4.join(COMPONENT_CONFIGS_DIR, entry.name));
|
|
1225
1367
|
}
|
|
1226
1368
|
}
|
|
1227
1369
|
return legacy;
|
|
@@ -1229,19 +1371,19 @@ ${lines.join("\n")}
|
|
|
1229
1371
|
function warnOnLegacyPointers(legacy, bakeHeld, warn) {
|
|
1230
1372
|
if (legacy.length === 0) return;
|
|
1231
1373
|
warn(
|
|
1232
|
-
`[live-tokens] ${legacy.length} legacy pointer file(s) left from the pre-working-set data model, starting with ${legacy[0]}. They are no longer read. Run \`npx live-tokens migrate\` to heal the tree.` + (bakeHeld ? ` Until then this tree has named no production theme, so ${
|
|
1374
|
+
`[live-tokens] ${legacy.length} legacy pointer file(s) left from the pre-working-set data model, starting with ${legacy[0]}. They are no longer read. Run \`npx live-tokens migrate\` to heal the tree.` + (bakeHeld ? ` Until then this tree has named no production theme, so ${path4.basename(GENERATED_CSS_PATH)} is left exactly as it is rather than rebuilt from the default.` : "")
|
|
1233
1375
|
);
|
|
1234
1376
|
}
|
|
1235
1377
|
function warnOnLegacyLayout(evidence, warn) {
|
|
1236
1378
|
warn(
|
|
1237
|
-
`[live-tokens] This project still uses the data layout from 0.47 and earlier: ${evidence}. Since 0.48 the whole-theme theme files live in \`themes/\` and the colors and type live in \`colors-and-type/\`. Nothing in the data directory was written, the editor cannot save, and ${
|
|
1379
|
+
`[live-tokens] This project still uses the data layout from 0.47 and earlier: ${evidence}. Since 0.48 the whole-theme theme files live in \`themes/\` and the colors and type live in \`colors-and-type/\`. Nothing in the data directory was written, the editor cannot save, and ${path4.basename(GENERATED_CSS_PATH)} is left exactly as it is. Run \`npx live-tokens migrate\` to move the directories and heal the tree, then restart the dev server.`
|
|
1238
1380
|
);
|
|
1239
1381
|
}
|
|
1240
1382
|
function warnOnUnmigratedSketchStyles(warn) {
|
|
1241
|
-
const legacyDir =
|
|
1242
|
-
if (!
|
|
1383
|
+
const legacyDir = path4.join(dataDirs.dataDir, "sketch-presets");
|
|
1384
|
+
if (!fs3.existsSync(legacyDir)) return;
|
|
1243
1385
|
warn(
|
|
1244
|
-
`[live-tokens] Saved sketchstyles still live under the retired ${
|
|
1386
|
+
`[live-tokens] Saved sketchstyles still live under the retired ${path4.basename(legacyDir)}/ directory name. Run \`npx live-tokens migrate\` to rename it to ${path4.basename(SKETCH_STYLES_DIR)}/; until then they will not show up in the Sketchstyle view.`
|
|
1245
1387
|
);
|
|
1246
1388
|
}
|
|
1247
1389
|
const diskThemeResolvers = {
|
|
@@ -1265,7 +1407,7 @@ ${lines.join("\n")}
|
|
|
1265
1407
|
const resolved = themesResource.existingPath(fileName);
|
|
1266
1408
|
if (resolved === null) return "missing";
|
|
1267
1409
|
try {
|
|
1268
|
-
if (isColorsAndTypeShaped(JSON.parse(
|
|
1410
|
+
if (isColorsAndTypeShaped(JSON.parse(fs3.readFileSync(resolved, "utf-8")))) {
|
|
1269
1411
|
return "colors-and-type";
|
|
1270
1412
|
}
|
|
1271
1413
|
} catch {
|
|
@@ -1273,7 +1415,7 @@ ${lines.join("\n")}
|
|
|
1273
1415
|
return "corrupt";
|
|
1274
1416
|
}
|
|
1275
1417
|
function notAThemeError(fileName) {
|
|
1276
|
-
return `"${fileName}" is a colors-and-type file, not a theme. Before 0.48 those lived in themes/; move it to ${
|
|
1418
|
+
return `"${fileName}" is a colors-and-type file, not a theme. Before 0.48 those lived in themes/; move it to ${path4.basename(COLORS_AND_TYPE_DIR)}/ or delete it. To build a theme, use the theme skill "create-theme".`;
|
|
1277
1419
|
}
|
|
1278
1420
|
function respondUnreadableTheme(res, fileName, missingError) {
|
|
1279
1421
|
switch (unreadableThemeReason(fileName)) {
|
|
@@ -1292,18 +1434,18 @@ ${lines.join("\n")}
|
|
|
1292
1434
|
}
|
|
1293
1435
|
function writeTheme(fileName, theme) {
|
|
1294
1436
|
themesResource.ensureDir();
|
|
1295
|
-
|
|
1437
|
+
fs3.writeFileSync(themesResource.filePath(fileName), JSON.stringify(theme, null, 2));
|
|
1296
1438
|
}
|
|
1297
1439
|
function migrateLocalThemes(warn) {
|
|
1298
|
-
if (
|
|
1299
|
-
if (!
|
|
1300
|
-
for (const file of
|
|
1440
|
+
if (path4.resolve(THEMES_DIR) === path4.resolve(packageThemesDir)) return;
|
|
1441
|
+
if (!fs3.existsSync(THEMES_DIR)) return;
|
|
1442
|
+
for (const file of fs3.readdirSync(THEMES_DIR)) {
|
|
1301
1443
|
if (!file.endsWith(".json") || file.startsWith("_")) continue;
|
|
1302
1444
|
const fileName = file.slice(0, -".json".length);
|
|
1303
1445
|
if (fileName === "default") continue;
|
|
1304
1446
|
let raw;
|
|
1305
1447
|
try {
|
|
1306
|
-
raw = JSON.parse(
|
|
1448
|
+
raw = JSON.parse(fs3.readFileSync(path4.join(THEMES_DIR, file), "utf-8"));
|
|
1307
1449
|
} catch {
|
|
1308
1450
|
continue;
|
|
1309
1451
|
}
|
|
@@ -1324,7 +1466,7 @@ ${lines.join("\n")}
|
|
|
1324
1466
|
function ensureDefaultTheme(warn) {
|
|
1325
1467
|
let existing = null;
|
|
1326
1468
|
try {
|
|
1327
|
-
existing = JSON.parse(
|
|
1469
|
+
existing = JSON.parse(fs3.readFileSync(themesResource.filePath("default"), "utf-8"));
|
|
1328
1470
|
} catch {
|
|
1329
1471
|
}
|
|
1330
1472
|
if (isColorsAndTypeShaped(existing)) {
|
|
@@ -1361,9 +1503,9 @@ ${lines.join("\n")}
|
|
|
1361
1503
|
themesResource.ensureDir();
|
|
1362
1504
|
ensureDefaultTheme(warn);
|
|
1363
1505
|
migrateLocalThemes(warn);
|
|
1364
|
-
const productionPointerHeld = !
|
|
1506
|
+
const productionPointerHeld = !fs3.existsSync(themesResource.productionPath) && listLegacyPointers().length > 0;
|
|
1365
1507
|
if (productionPointerHeld) {
|
|
1366
|
-
if (!
|
|
1508
|
+
if (!fs3.existsSync(themesResource.activePath)) themesResource.setActiveName("default");
|
|
1367
1509
|
} else {
|
|
1368
1510
|
themesResource.ensureMeta();
|
|
1369
1511
|
}
|
|
@@ -1442,8 +1584,8 @@ ${lines.join("\n")}
|
|
|
1442
1584
|
}
|
|
1443
1585
|
function clearAllWorking() {
|
|
1444
1586
|
colorsAndTypeResource.clearWorking();
|
|
1445
|
-
if (!
|
|
1446
|
-
for (const entry of
|
|
1587
|
+
if (!fs3.existsSync(COMPONENT_CONFIGS_DIR)) return;
|
|
1588
|
+
for (const entry of fs3.readdirSync(COMPONENT_CONFIGS_DIR, { withFileTypes: true })) {
|
|
1447
1589
|
if (entry.isDirectory()) componentResource(entry.name).clearWorking();
|
|
1448
1590
|
}
|
|
1449
1591
|
}
|
|
@@ -1487,6 +1629,9 @@ ${lines.join("\n")}
|
|
|
1487
1629
|
const SKETCH_STYLE_BY_NAME_REGEX = new RegExp(`^${escapedBase}/sketch-styles/([a-z0-9\\-_]+)$`);
|
|
1488
1630
|
const PRODUCTION_ROUTE = `${API_BASE}/production`;
|
|
1489
1631
|
const EVENTS_ROUTE = `${API_BASE}/events`;
|
|
1632
|
+
const A11Y_RUN_ROUTE = `${API_BASE}/a11y/run`;
|
|
1633
|
+
const A11Y_REPORT_ROUTE = `${API_BASE}/a11y/report`;
|
|
1634
|
+
const A11Y_CONFIRMED_ROUTE = `${API_BASE}/a11y/confirmed`;
|
|
1490
1635
|
async function handleListColorsAndType(_ctx) {
|
|
1491
1636
|
const files = colorsAndTypeResource.listNames().map((fileName) => {
|
|
1492
1637
|
const data = colorsAndTypeResource.readJson(fileName);
|
|
@@ -1571,15 +1716,15 @@ ${lines.join("\n")}
|
|
|
1571
1716
|
}
|
|
1572
1717
|
const body = JSON.parse(await readBody(req));
|
|
1573
1718
|
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1574
|
-
if (
|
|
1719
|
+
if (fs3.existsSync(filePath)) {
|
|
1575
1720
|
try {
|
|
1576
|
-
const existing = JSON.parse(
|
|
1721
|
+
const existing = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
1577
1722
|
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
1578
1723
|
} catch {
|
|
1579
1724
|
}
|
|
1580
1725
|
}
|
|
1581
1726
|
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
1582
|
-
|
|
1727
|
+
fs3.writeFileSync(filePath, JSON.stringify(body, null, 2));
|
|
1583
1728
|
jsonResponse(res, 200, { ok: true, fileName });
|
|
1584
1729
|
return;
|
|
1585
1730
|
}
|
|
@@ -1588,8 +1733,8 @@ ${lines.join("\n")}
|
|
|
1588
1733
|
jsonResponse(res, 403, { error: "Cannot delete the default colors and type" });
|
|
1589
1734
|
return;
|
|
1590
1735
|
}
|
|
1591
|
-
if (
|
|
1592
|
-
|
|
1736
|
+
if (fs3.existsSync(filePath)) {
|
|
1737
|
+
fs3.unlinkSync(filePath);
|
|
1593
1738
|
} else if (colorsAndTypeResource.existingPath(fileName)) {
|
|
1594
1739
|
jsonResponse(res, 403, {
|
|
1595
1740
|
error: "Cannot delete colors and type shipped with the package. Saving it creates a local copy; deleting that copy restores the shipped version.",
|
|
@@ -1676,16 +1821,16 @@ ${lines.join("\n")}
|
|
|
1676
1821
|
body.component = comp;
|
|
1677
1822
|
body.name = body.name || name;
|
|
1678
1823
|
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1679
|
-
if (
|
|
1824
|
+
if (fs3.existsSync(configPath)) {
|
|
1680
1825
|
try {
|
|
1681
|
-
const existing = JSON.parse(
|
|
1826
|
+
const existing = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
1682
1827
|
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
1683
1828
|
} catch {
|
|
1684
1829
|
}
|
|
1685
1830
|
}
|
|
1686
1831
|
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
1687
1832
|
r.ensureDir();
|
|
1688
|
-
|
|
1833
|
+
fs3.writeFileSync(configPath, JSON.stringify(body, null, 2));
|
|
1689
1834
|
jsonResponse(res, 200, { ok: true, fileName: name });
|
|
1690
1835
|
return;
|
|
1691
1836
|
}
|
|
@@ -1694,7 +1839,7 @@ ${lines.join("\n")}
|
|
|
1694
1839
|
jsonResponse(res, 403, { error: "Cannot delete default config" });
|
|
1695
1840
|
return;
|
|
1696
1841
|
}
|
|
1697
|
-
if (
|
|
1842
|
+
if (fs3.existsSync(configPath)) fs3.unlinkSync(configPath);
|
|
1698
1843
|
jsonResponse(res, 200, { ok: true });
|
|
1699
1844
|
return;
|
|
1700
1845
|
}
|
|
@@ -1702,7 +1847,7 @@ ${lines.join("\n")}
|
|
|
1702
1847
|
async function handleListComponentConfigs({ params, res }) {
|
|
1703
1848
|
const [comp] = params;
|
|
1704
1849
|
const r = componentResource(comp);
|
|
1705
|
-
if (!
|
|
1850
|
+
if (!fs3.existsSync(r.dir)) {
|
|
1706
1851
|
jsonResponse(res, 404, { error: "Component not found" });
|
|
1707
1852
|
return;
|
|
1708
1853
|
}
|
|
@@ -1790,9 +1935,9 @@ ${lines.join("\n")}
|
|
|
1790
1935
|
const body = JSON.parse(await readBody(req));
|
|
1791
1936
|
const { theme } = normalizeTheme(body, diskThemeResolvers);
|
|
1792
1937
|
theme.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1793
|
-
if (
|
|
1938
|
+
if (fs3.existsSync(filePath)) {
|
|
1794
1939
|
try {
|
|
1795
|
-
const existing = JSON.parse(
|
|
1940
|
+
const existing = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
1796
1941
|
if (existing.createdAt) theme.createdAt = existing.createdAt;
|
|
1797
1942
|
} catch {
|
|
1798
1943
|
}
|
|
@@ -1814,7 +1959,7 @@ ${lines.join("\n")}
|
|
|
1814
1959
|
});
|
|
1815
1960
|
return;
|
|
1816
1961
|
}
|
|
1817
|
-
if (
|
|
1962
|
+
if (fs3.existsSync(filePath)) {
|
|
1818
1963
|
const deletingActive = themesResource.getActiveName() === fileName;
|
|
1819
1964
|
const liveColors = deletingActive ? resolveLiveColorsAndType(activeTheme()) : null;
|
|
1820
1965
|
const liveComponents = {};
|
|
@@ -1824,7 +1969,8 @@ ${lines.join("\n")}
|
|
|
1824
1969
|
if (resolved) liveComponents[comp] = resolved.data;
|
|
1825
1970
|
}
|
|
1826
1971
|
}
|
|
1827
|
-
|
|
1972
|
+
fs3.unlinkSync(filePath);
|
|
1973
|
+
a11yRuns.forget(fileName);
|
|
1828
1974
|
if (themesResource.existingPath(fileName) === null && deletingActive) {
|
|
1829
1975
|
themesResource.setActiveName("default");
|
|
1830
1976
|
}
|
|
@@ -1913,7 +2059,49 @@ data: ${JSON.stringify(state)}
|
|
|
1913
2059
|
|
|
1914
2060
|
`);
|
|
1915
2061
|
});
|
|
1916
|
-
|
|
2062
|
+
eventClients.add(res);
|
|
2063
|
+
req.on("close", () => {
|
|
2064
|
+
unsubscribe();
|
|
2065
|
+
eventClients.delete(res);
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
const eventClients = /* @__PURE__ */ new Set();
|
|
2069
|
+
const a11yRuns = a11yRunServer({
|
|
2070
|
+
root: process.cwd(),
|
|
2071
|
+
reportsDir: A11Y_REPORTS_DIR,
|
|
2072
|
+
openTheme: () => themesResource.getActiveName(),
|
|
2073
|
+
runCli: spawnCli(PACKAGE_CLI_PATH),
|
|
2074
|
+
broadcast: (report) => {
|
|
2075
|
+
const frame = `event: ${A11Y_EVENT}
|
|
2076
|
+
data: ${JSON.stringify(report)}
|
|
2077
|
+
|
|
2078
|
+
`;
|
|
2079
|
+
for (const client of eventClients) client.write(frame);
|
|
2080
|
+
}
|
|
2081
|
+
});
|
|
2082
|
+
async function handleStartA11yRun({ req, res }) {
|
|
2083
|
+
let body;
|
|
2084
|
+
try {
|
|
2085
|
+
body = JSON.parse(await readBody(req));
|
|
2086
|
+
} catch {
|
|
2087
|
+
body = void 0;
|
|
2088
|
+
}
|
|
2089
|
+
const answer = a11yRuns.start(body);
|
|
2090
|
+
jsonResponse(res, answer.status, answer.body);
|
|
2091
|
+
}
|
|
2092
|
+
async function handleConfirmA11yReview({ req, res }) {
|
|
2093
|
+
let body;
|
|
2094
|
+
try {
|
|
2095
|
+
body = JSON.parse(await readBody(req));
|
|
2096
|
+
} catch {
|
|
2097
|
+
body = void 0;
|
|
2098
|
+
}
|
|
2099
|
+
const answer = a11yRuns.confirm(body);
|
|
2100
|
+
jsonResponse(res, answer.status, answer.body);
|
|
2101
|
+
}
|
|
2102
|
+
async function handleGetA11yReport({ res }) {
|
|
2103
|
+
const answer = a11yRuns.report();
|
|
2104
|
+
jsonResponse(res, answer.status, answer.body);
|
|
1917
2105
|
}
|
|
1918
2106
|
async function handleExportTheme({ params, res }) {
|
|
1919
2107
|
const [fileName] = params;
|
|
@@ -2074,22 +2262,22 @@ data: ${JSON.stringify(state)}
|
|
|
2074
2262
|
}
|
|
2075
2263
|
body.name = String(body.name || fileName);
|
|
2076
2264
|
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2077
|
-
if (
|
|
2265
|
+
if (fs3.existsSync(filePath)) {
|
|
2078
2266
|
try {
|
|
2079
|
-
const existing = JSON.parse(
|
|
2267
|
+
const existing = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
2080
2268
|
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
2081
2269
|
} catch {
|
|
2082
2270
|
}
|
|
2083
2271
|
}
|
|
2084
2272
|
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
2085
2273
|
sketchStylesResource.ensureDir();
|
|
2086
|
-
|
|
2274
|
+
fs3.writeFileSync(filePath, JSON.stringify(body, null, 2));
|
|
2087
2275
|
jsonResponse(res, 200, { ok: true, fileName });
|
|
2088
2276
|
return;
|
|
2089
2277
|
}
|
|
2090
2278
|
if (req.method === "DELETE") {
|
|
2091
|
-
if (
|
|
2092
|
-
|
|
2279
|
+
if (fs3.existsSync(filePath)) {
|
|
2280
|
+
fs3.unlinkSync(filePath);
|
|
2093
2281
|
} else if (sketchStylesResource.existingPath(fileName)) {
|
|
2094
2282
|
jsonResponse(res, 403, {
|
|
2095
2283
|
error: "Cannot delete a sketchstyle shipped with the package. Saving it creates a local copy; deleting that copy restores the shipped version.",
|
|
@@ -2186,12 +2374,24 @@ data: ${JSON.stringify(state)}
|
|
|
2186
2374
|
{ method: "DELETE", pattern: SKETCH_STYLE_BY_NAME_REGEX, handler: handleSketchStyleByName },
|
|
2187
2375
|
{ method: "POST", pattern: SKETCH_STYLE_BY_NAME_REGEX, handler: methodNotAllowed },
|
|
2188
2376
|
{ method: "GET", pattern: EVENTS_ROUTE, handler: handleEvents },
|
|
2377
|
+
{ method: "POST", pattern: A11Y_RUN_ROUTE, handler: handleStartA11yRun },
|
|
2378
|
+
{ method: "GET", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
|
|
2379
|
+
{ method: "PUT", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
|
|
2380
|
+
{ method: "DELETE", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
|
|
2381
|
+
{ method: "GET", pattern: A11Y_REPORT_ROUTE, handler: handleGetA11yReport },
|
|
2382
|
+
{ method: "PUT", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
|
|
2383
|
+
{ method: "POST", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
|
|
2384
|
+
{ method: "DELETE", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
|
|
2385
|
+
{ method: "PUT", pattern: A11Y_CONFIRMED_ROUTE, handler: handleConfirmA11yReview },
|
|
2386
|
+
{ method: "GET", pattern: A11Y_CONFIRMED_ROUTE, handler: methodNotAllowed },
|
|
2387
|
+
{ method: "POST", pattern: A11Y_CONFIRMED_ROUTE, handler: methodNotAllowed },
|
|
2388
|
+
{ method: "DELETE", pattern: A11Y_CONFIRMED_ROUTE, handler: methodNotAllowed },
|
|
2189
2389
|
{ method: "PUT", pattern: PRODUCTION_ROUTE, handler: handleAdoptTheme },
|
|
2190
2390
|
{ method: "GET", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
|
|
2191
2391
|
{ method: "POST", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
|
|
2192
2392
|
{ method: "DELETE", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed }
|
|
2193
2393
|
];
|
|
2194
|
-
const isEditorOwnedJson = (file) => file.endsWith(".json") &&
|
|
2394
|
+
const isEditorOwnedJson = (file) => file.endsWith(".json") && path4.resolve(file).startsWith(dataDirs.dataDir + path4.sep);
|
|
2195
2395
|
let buildLogger = null;
|
|
2196
2396
|
let buildChecked = false;
|
|
2197
2397
|
let productionCssLogger = null;
|
|
@@ -2241,7 +2441,7 @@ data: ${JSON.stringify(state)}
|
|
|
2241
2441
|
const { productionPointerHeld } = ensureThemesDir((msg) => server.config.logger.warn(msg));
|
|
2242
2442
|
const openTheme = activeTheme();
|
|
2243
2443
|
if (openTheme) pruneMatchingWorking(openTheme);
|
|
2244
|
-
if (!productionPointerHeld || !
|
|
2444
|
+
if (!productionPointerHeld || !fs3.existsSync(GENERATED_CSS_PATH)) regenerateTokensCss();
|
|
2245
2445
|
if (opts.autoMigrate && !ISOLATED_DATA_DIR) {
|
|
2246
2446
|
autoMigrateAdditive((msg) => server.config.logger.info(msg));
|
|
2247
2447
|
}
|
|
@@ -2264,10 +2464,11 @@ data: ${JSON.stringify(state)}
|
|
|
2264
2464
|
const handled = await dispatch(req, res, routes);
|
|
2265
2465
|
if (!handled) next();
|
|
2266
2466
|
});
|
|
2467
|
+
server.httpServer?.once("close", () => a11yRuns.stop());
|
|
2267
2468
|
},
|
|
2268
2469
|
handleHotUpdate(ctx) {
|
|
2269
2470
|
if (legacyLayout) return;
|
|
2270
|
-
const normalized =
|
|
2471
|
+
const normalized = path4.resolve(ctx.file);
|
|
2271
2472
|
if (!COMPONENTS_SCAN_DIRS.some((d) => normalized.startsWith(d))) return;
|
|
2272
2473
|
if (!normalized.endsWith(".svelte")) return;
|
|
2273
2474
|
if (!isThemeAwareComponent(normalized)) return;
|