@motion-proto/live-tokens 0.96.0 → 0.98.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +309 -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 +142 -17
- package/bin/lib/findings.mjs +20 -4
- package/bin/rules/a11y.mjs +53 -0
- package/bin/set-colors.mjs +1 -0
- package/bin/setup-claude.mjs +2 -2
- package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
- package/dist-plugin/index.cjs +148 -3
- package/dist-plugin/index.js +250 -105
- package/dist-plugin/setColors/index.cjs +7 -1
- package/dist-plugin/setColors/index.d.cts +9 -4
- package/dist-plugin/setColors/index.d.ts +9 -4
- package/dist-plugin/setColors/index.js +8 -2
- package/dist-plugin/setGeometry/index.d.cts +1 -1
- package/dist-plugin/setGeometry/index.d.ts +1 -1
- package/dist-plugin/setType/index.d.cts +1 -1
- package/dist-plugin/setType/index.d.ts +1 -1
- package/dist-plugin/{themeTypes-B8_Idrp4.d.cts → themeTypes-BaTQUyac.d.cts} +10 -1
- package/dist-plugin/{themeTypes-B8_Idrp4.d.ts → themeTypes-BaTQUyac.d.ts} +10 -1
- package/package.json +49 -2
- package/src/demo/LabeledSelect.svelte +1 -1
- package/src/editor/bootstrap.ts +3 -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 +41 -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 -10
- package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
- package/src/editor/core/a11y/a11yReport.ts +101 -0
- package/src/editor/core/a11y/a11yReportTypes.ts +69 -0
- package/src/editor/core/a11y/contrastFailures.ts +22 -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 +79 -0
- package/src/editor/core/palettes/resolveColor.ts +77 -0
- package/src/editor/core/store/editorPersistence.ts +9 -1
- package/src/editor/core/store/editorRenderer.ts +2 -0
- package/src/editor/core/store/editorStore.ts +29 -0
- package/src/editor/core/store/editorTypes.ts +9 -0
- package/src/editor/core/store/editorViewStore.ts +2 -2
- package/src/editor/core/store/unsavedEdits.ts +10 -0
- package/src/editor/core/themes/buildColors.ts +16 -5
- package/src/editor/core/themes/liveStateStream.ts +7 -1
- package/src/editor/core/themes/parsers/gradient.ts +5 -2
- package/src/editor/core/themes/slices/customColors.ts +226 -0
- package/src/editor/core/themes/themeTypes.ts +10 -0
- package/src/editor/docs/chapters.ts +1 -0
- package/src/editor/docs/content/accessibility.md +362 -0
- package/src/editor/docs/content/editing-tokens.md +56 -1
- package/src/editor/docs/content.generated.ts +2 -1
- package/src/editor/pages/ComponentEditorPage.svelte +10 -1
- package/src/editor/pages/EditorShell.svelte +97 -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/ui/CustomColorReplaceStep.svelte +118 -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 +347 -15
- package/src/editor/ui/UITokenSelector.svelte +33 -14
- package/src/editor/ui/VariablesTab.svelte +3 -0
- package/src/editor/ui/a11y/A11yComponentIssues.svelte +103 -0
- package/src/editor/ui/a11y/A11yFindingRow.svelte +57 -0
- package/src/editor/ui/a11y/A11yIssueRow.svelte +121 -0
- package/src/editor/ui/a11y/A11yOverview.svelte +242 -0
- package/src/editor/ui/a11y/A11yPanel.svelte +128 -0
- package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
- package/src/editor/ui/a11y/A11yRailMark.svelte +34 -0
- package/src/editor/ui/a11y/AccessibilityTab.svelte +334 -0
- package/src/editor/ui/a11y/a11yView.ts +218 -0
- package/src/editor/ui/anchorToTrigger.ts +63 -0
- package/src/editor/ui/contrastReadout.ts +45 -0
- package/src/editor/ui/sections/CustomColorsSection.svelte +254 -0
- package/src/live-tokens/data/themes/autumn.json +20 -20
- package/src/live-tokens/data/themes/halloween.json +20 -20
- package/src/live-tokens/data/themes/midnight-study.json +20 -20
- package/src/live-tokens/data/themes/ocean.json +20 -20
- package/src/live-tokens/data/themes/royal-velvet.json +20 -20
- package/src/live-tokens/data/themes/sketchy.json +20 -20
- package/src/live-tokens/data/themes/spring-meadow.json +20 -20
- package/src/live-tokens/data/themes/sunset.json +20 -20
- package/src/system/components/Badge.svelte +3 -3
- package/src/system/components/Button.svelte +9 -3
- 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 +26 -26
- package/src/system/components/Input.svelte +8 -1
- package/src/system/components/MenuSelect.svelte +30 -2
- package/src/system/components/Notification.svelte +1 -1
- package/src/system/components/ProgressBar.svelte +6 -0
- package/src/system/components/RadioButton.svelte +36 -3
- package/src/system/components/SegmentedControl.svelte +36 -2
- package/src/system/components/SlidePager.svelte +2 -2
- package/src/system/components/Slider.svelte +13 -9
- package/src/system/components/TabBar.svelte +33 -3
- package/src/system/components/Toggle.svelte +2 -2
- 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-SHBYZUPD.js → chunk-5HNZHIHG.js} +211 -14
- package/src/testing-js/chunk-5HNZHIHG.js.map +1 -0
- 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-EWSGE27V.js +168 -0
- package/src/testing-js/chunk-EWSGE27V.js.map +1 -0
- package/src/testing-js/{chunk-GKO27NGN.js → chunk-R54I7YL6.js} +2 -2
- package/src/testing-js/{chunk-X6KHEVIG.js → chunk-RWH46IU5.js} +97 -29
- package/src/testing-js/chunk-RWH46IU5.js.map +1 -0
- package/src/testing-js/chunk-SHLPDNAQ.js +658 -0
- package/src/testing-js/chunk-SHLPDNAQ.js.map +1 -0
- 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 +283 -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 +9 -9
- package/src/testing-js/page-compliance.contract.js +163 -18
- package/src/testing-js/page-compliance.contract.js.map +1 -1
- package/src/testing-js/vitest.js +3 -3
- package/template/package.json +9 -0
- package/src/editor/ui/keepInViewport.ts +0 -84
- 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-R54I7YL6.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,119 @@ 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_REPORT_PATH = path3.join("test-results", "live-tokens", "a11y-report.json");
|
|
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, reportFile, runCli, broadcast }) {
|
|
790
|
+
let running = null;
|
|
791
|
+
function current() {
|
|
792
|
+
if (running) return running.report;
|
|
793
|
+
if (!fs2.existsSync(reportFile)) return null;
|
|
794
|
+
return JSON.parse(fs2.readFileSync(reportFile, "utf-8"));
|
|
795
|
+
}
|
|
796
|
+
function settle(started, outcome) {
|
|
797
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
798
|
+
const cli = parseCliReport(outcome.stdout);
|
|
799
|
+
if (!cli) {
|
|
800
|
+
const stderr = outcome.stderr.trim().slice(-STDERR_TAIL);
|
|
801
|
+
return {
|
|
802
|
+
...started,
|
|
803
|
+
state: "failed",
|
|
804
|
+
finishedAt,
|
|
805
|
+
error: `check-component exited with code ${outcome.code} and printed no report.${stderr ? `
|
|
806
|
+
${stderr}` : ""}`
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
return {
|
|
810
|
+
...started,
|
|
811
|
+
state: "finished",
|
|
812
|
+
finishedAt,
|
|
813
|
+
...cli.theme ? { theme: cli.theme } : {},
|
|
814
|
+
findings: cli.findings.filter((f) => f.details?.engineRule !== "color-contrast")
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function start(body) {
|
|
818
|
+
if (running) {
|
|
819
|
+
return { status: 409, body: { error: "An accessibility run is already going.", code: "A11Y_RUN_IN_PROGRESS" } };
|
|
820
|
+
}
|
|
821
|
+
const contrast = readContrast(body);
|
|
822
|
+
if (!contrast) {
|
|
823
|
+
return {
|
|
824
|
+
status: 400,
|
|
825
|
+
body: { error: "Body must be { contrast: [...] } with one entry per failing pair", code: "INVALID_BODY" }
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
const report = { state: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString(), contrast };
|
|
829
|
+
const run = runCli(A11Y_RUN_ARGS, root);
|
|
830
|
+
running = { report, stop: run.stop };
|
|
831
|
+
broadcast(report);
|
|
832
|
+
void run.done.then((outcome) => {
|
|
833
|
+
const settled = settle(report, outcome);
|
|
834
|
+
fs2.mkdirSync(path3.dirname(reportFile), { recursive: true });
|
|
835
|
+
fs2.writeFileSync(reportFile, `${JSON.stringify(settled, null, 2)}
|
|
836
|
+
`);
|
|
837
|
+
running = null;
|
|
838
|
+
broadcast(settled);
|
|
839
|
+
});
|
|
840
|
+
return { status: 202, body: report };
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
start,
|
|
844
|
+
report: () => ({ status: 200, body: { report: current() } }),
|
|
845
|
+
stop: () => running?.stop()
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// src/editor/core/a11y/a11yReportTypes.ts
|
|
850
|
+
var A11Y_EVENT = "a11y";
|
|
851
|
+
|
|
751
852
|
// vite-plugin/themeFileApi.ts
|
|
752
853
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
753
854
|
var PKG_VERSION = (() => {
|
|
754
855
|
try {
|
|
755
|
-
let dir =
|
|
856
|
+
let dir = path4.dirname(fileURLToPath2(import.meta.url));
|
|
756
857
|
for (let i = 0; i < 4; i++) {
|
|
757
|
-
const p =
|
|
758
|
-
if (
|
|
759
|
-
const json = JSON.parse(
|
|
858
|
+
const p = path4.join(dir, "package.json");
|
|
859
|
+
if (fs3.existsSync(p)) {
|
|
860
|
+
const json = JSON.parse(fs3.readFileSync(p, "utf-8"));
|
|
760
861
|
if (json?.name === "@motion-proto/live-tokens") return json.version ?? "";
|
|
761
862
|
}
|
|
762
|
-
const up =
|
|
863
|
+
const up = path4.dirname(dir);
|
|
763
864
|
if (up === dir) break;
|
|
764
865
|
dir = up;
|
|
765
866
|
}
|
|
@@ -803,46 +904,48 @@ function themeFileApi(opts) {
|
|
|
803
904
|
const COMPONENT_CONFIGS_DIR = dataDirs.componentConfigsDir;
|
|
804
905
|
const THEMES_DIR = dataDirs.themesDir;
|
|
805
906
|
const SKETCH_STYLES_DIR = dataDirs.sketchStylesDir;
|
|
806
|
-
const CSS_PATH =
|
|
907
|
+
const CSS_PATH = path4.resolve(opts.tokensCssPath);
|
|
807
908
|
const ISOLATED_DATA_DIR = testDataDir();
|
|
808
|
-
const GENERATED_CSS_PATH = ISOLATED_DATA_DIR ?
|
|
809
|
-
const FONTS_CSS_PATH = ISOLATED_DATA_DIR ?
|
|
909
|
+
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");
|
|
910
|
+
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");
|
|
911
|
+
const A11Y_REPORT_FILE = ISOLATED_DATA_DIR ? path4.join(ISOLATED_DATA_DIR, path4.basename(A11Y_REPORT_PATH)) : path4.join(process.cwd(), A11Y_REPORT_PATH);
|
|
810
912
|
const API_BASE = opts.apiBase ?? "/api/live-tokens";
|
|
811
|
-
const consumerComponentDirs = opts.componentsSrcDir ? [
|
|
812
|
-
const packageComponentsDir =
|
|
813
|
-
|
|
913
|
+
const consumerComponentDirs = opts.componentsSrcDir ? [path4.resolve(opts.componentsSrcDir)] : [path4.resolve("src/components"), path4.resolve("src/system/components")];
|
|
914
|
+
const packageComponentsDir = path4.resolve(
|
|
915
|
+
path4.dirname(fileURLToPath2(import.meta.url)),
|
|
814
916
|
"..",
|
|
815
917
|
"src",
|
|
816
918
|
"system",
|
|
817
919
|
"components"
|
|
818
920
|
);
|
|
819
|
-
const
|
|
820
|
-
|
|
921
|
+
const PACKAGE_CLI_PATH = path4.resolve(path4.dirname(fileURLToPath2(import.meta.url)), "..", "bin", "cli.mjs");
|
|
922
|
+
const packageDataDir = testPackageDataDir() ?? path4.resolve(
|
|
923
|
+
path4.dirname(fileURLToPath2(import.meta.url)),
|
|
821
924
|
"..",
|
|
822
925
|
"src",
|
|
823
926
|
"live-tokens",
|
|
824
927
|
"data"
|
|
825
928
|
);
|
|
826
|
-
const packageColorsAndTypeDir =
|
|
827
|
-
const packageThemesDir =
|
|
828
|
-
const packageComponentConfigsDir =
|
|
829
|
-
const packageSketchStylesDir =
|
|
929
|
+
const packageColorsAndTypeDir = path4.join(packageDataDir, "colors-and-type");
|
|
930
|
+
const packageThemesDir = path4.join(packageDataDir, "themes");
|
|
931
|
+
const packageComponentConfigsDir = path4.join(packageDataDir, "component-configs");
|
|
932
|
+
const packageSketchStylesDir = path4.join(packageDataDir, "sketch-styles");
|
|
830
933
|
function shippedNames(dataDir, subdir) {
|
|
831
934
|
try {
|
|
832
|
-
const pkgRoot =
|
|
833
|
-
const pkg = JSON.parse(
|
|
935
|
+
const pkgRoot = path4.resolve(dataDir, "..", "..", "..");
|
|
936
|
+
const pkg = JSON.parse(fs3.readFileSync(path4.join(pkgRoot, "package.json"), "utf-8"));
|
|
834
937
|
const files = Array.isArray(pkg.files) ? pkg.files : [];
|
|
835
938
|
const owned = new RegExp(`^src/live-tokens/data/${subdir}/[^/]+\\.json$`);
|
|
836
|
-
return files.filter((f) => owned.test(f)).map((f) =>
|
|
939
|
+
return files.filter((f) => owned.test(f)).map((f) => path4.basename(f, ".json"));
|
|
837
940
|
} catch {
|
|
838
941
|
return [];
|
|
839
942
|
}
|
|
840
943
|
}
|
|
841
944
|
const COMPONENTS_SCAN_DIRS = [...consumerComponentDirs];
|
|
842
|
-
if (!COMPONENTS_SCAN_DIRS.includes(packageComponentsDir) &&
|
|
945
|
+
if (!COMPONENTS_SCAN_DIRS.includes(packageComponentsDir) && fs3.existsSync(packageComponentsDir)) {
|
|
843
946
|
COMPONENTS_SCAN_DIRS.push(packageComponentsDir);
|
|
844
947
|
}
|
|
845
|
-
const isLiveStateFile = (file) => file ===
|
|
948
|
+
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
949
|
const liveWatch = dataTreeWatch({
|
|
847
950
|
dirs: [COLORS_AND_TYPE_DIR, COMPONENT_CONFIGS_DIR, THEMES_DIR],
|
|
848
951
|
isLive: isLiveStateFile
|
|
@@ -858,8 +961,8 @@ function themeFileApi(opts) {
|
|
|
858
961
|
let r = componentResourceCache.get(comp);
|
|
859
962
|
if (!r) {
|
|
860
963
|
r = versionedFileResourceServer({
|
|
861
|
-
dir:
|
|
862
|
-
packageDir:
|
|
964
|
+
dir: path4.join(COMPONENT_CONFIGS_DIR, comp),
|
|
965
|
+
packageDir: path4.join(packageComponentConfigsDir, comp),
|
|
863
966
|
onWrite: liveWatch.noteOwnWrite
|
|
864
967
|
});
|
|
865
968
|
componentResourceCache.set(comp, r);
|
|
@@ -947,7 +1050,7 @@ function themeFileApi(opts) {
|
|
|
947
1050
|
const productionThemeName = themesResource.getProductionName();
|
|
948
1051
|
const productionTheme = readTheme(productionThemeName)?.theme ?? null;
|
|
949
1052
|
if (themesResource.existingPath(productionThemeName) !== null && !productionTheme?.colorsAndType) {
|
|
950
|
-
const tail = `${
|
|
1053
|
+
const tail = `${path4.basename(GENERATED_CSS_PATH)} was left as it was.`;
|
|
951
1054
|
throw new Error(
|
|
952
1055
|
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
1056
|
);
|
|
@@ -985,7 +1088,7 @@ function themeFileApi(opts) {
|
|
|
985
1088
|
}
|
|
986
1089
|
}
|
|
987
1090
|
let componentOverrideCount = 0;
|
|
988
|
-
if (
|
|
1091
|
+
if (fs3.existsSync(COMPONENT_CONFIGS_DIR)) {
|
|
989
1092
|
const blocks = [];
|
|
990
1093
|
for (const comp of listComponentNames()) {
|
|
991
1094
|
const prodCfg = productionTheme?.componentConfigs[comp];
|
|
@@ -1030,18 +1133,18 @@ function themeFileApi(opts) {
|
|
|
1030
1133
|
`/* Bake fingerprint: ${bakeFingerprint(body)} */`,
|
|
1031
1134
|
""
|
|
1032
1135
|
];
|
|
1033
|
-
if (!
|
|
1034
|
-
|
|
1136
|
+
if (!fs3.existsSync(path4.dirname(GENERATED_CSS_PATH))) {
|
|
1137
|
+
fs3.mkdirSync(path4.dirname(GENERATED_CSS_PATH), { recursive: true });
|
|
1035
1138
|
}
|
|
1036
|
-
|
|
1139
|
+
fs3.writeFileSync(GENERATED_CSS_PATH, [...header, body].join("\n"));
|
|
1037
1140
|
console.log(
|
|
1038
|
-
`[regenerateTokensCss] Wrote ${
|
|
1141
|
+
`[regenerateTokensCss] Wrote ${path4.basename(GENERATED_CSS_PATH)} (${colorsAndTypeVarCount} theme vars, ${componentOverrideCount} component overrides)`
|
|
1039
1142
|
);
|
|
1040
1143
|
}
|
|
1041
1144
|
function bakeIsCurrent() {
|
|
1042
1145
|
let baked;
|
|
1043
1146
|
try {
|
|
1044
|
-
baked =
|
|
1147
|
+
baked = fs3.readFileSync(GENERATED_CSS_PATH, "utf-8");
|
|
1045
1148
|
} catch {
|
|
1046
1149
|
return false;
|
|
1047
1150
|
}
|
|
@@ -1077,18 +1180,18 @@ function themeFileApi(opts) {
|
|
|
1077
1180
|
lines.push("");
|
|
1078
1181
|
}
|
|
1079
1182
|
const content = lines.join("\n");
|
|
1080
|
-
if (!
|
|
1081
|
-
|
|
1183
|
+
if (!fs3.existsSync(path4.dirname(FONTS_CSS_PATH))) {
|
|
1184
|
+
fs3.mkdirSync(path4.dirname(FONTS_CSS_PATH), { recursive: true });
|
|
1082
1185
|
}
|
|
1083
|
-
|
|
1084
|
-
console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${
|
|
1186
|
+
fs3.writeFileSync(FONTS_CSS_PATH, content);
|
|
1187
|
+
console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${path4.basename(FONTS_CSS_PATH)}`);
|
|
1085
1188
|
}
|
|
1086
1189
|
function componentNameFromFile(filePath) {
|
|
1087
|
-
return
|
|
1190
|
+
return path4.basename(filePath, ".svelte").toLowerCase();
|
|
1088
1191
|
}
|
|
1089
1192
|
function isThemeAwareComponent(filePath) {
|
|
1090
1193
|
try {
|
|
1091
|
-
const source =
|
|
1194
|
+
const source = fs3.readFileSync(filePath, "utf-8");
|
|
1092
1195
|
return /:global\(:root\)\s*\{/.test(source);
|
|
1093
1196
|
} catch {
|
|
1094
1197
|
return false;
|
|
@@ -1097,10 +1200,10 @@ function themeFileApi(opts) {
|
|
|
1097
1200
|
function listComponentSourcePaths() {
|
|
1098
1201
|
const byName = /* @__PURE__ */ new Map();
|
|
1099
1202
|
for (const dir of COMPONENTS_SCAN_DIRS) {
|
|
1100
|
-
if (!
|
|
1101
|
-
for (const f of
|
|
1203
|
+
if (!fs3.existsSync(dir)) continue;
|
|
1204
|
+
for (const f of fs3.readdirSync(dir)) {
|
|
1102
1205
|
if (!f.endsWith(".svelte")) continue;
|
|
1103
|
-
const full =
|
|
1206
|
+
const full = path4.join(dir, f);
|
|
1104
1207
|
if (!isThemeAwareComponent(full)) continue;
|
|
1105
1208
|
const name = componentNameFromFile(f);
|
|
1106
1209
|
if (!byName.has(name)) byName.set(name, full);
|
|
@@ -1114,18 +1217,18 @@ function themeFileApi(opts) {
|
|
|
1114
1217
|
function warnOnTokenDrift(log) {
|
|
1115
1218
|
let tokensCss = "";
|
|
1116
1219
|
try {
|
|
1117
|
-
tokensCss =
|
|
1220
|
+
tokensCss = fs3.readFileSync(CSS_PATH, "utf-8");
|
|
1118
1221
|
} catch {
|
|
1119
1222
|
return;
|
|
1120
1223
|
}
|
|
1121
1224
|
let generatedCss = "";
|
|
1122
1225
|
try {
|
|
1123
|
-
generatedCss =
|
|
1226
|
+
generatedCss = fs3.readFileSync(GENERATED_CSS_PATH, "utf-8");
|
|
1124
1227
|
} catch {
|
|
1125
1228
|
}
|
|
1126
1229
|
const componentSources = listComponentSourcePaths().map((p) => ({
|
|
1127
1230
|
name: componentNameFromFile(p),
|
|
1128
|
-
source:
|
|
1231
|
+
source: fs3.readFileSync(p, "utf-8")
|
|
1129
1232
|
}));
|
|
1130
1233
|
const missing = validateTokensCss({ tokensCss, generatedCss, componentSources });
|
|
1131
1234
|
if (missing.length === 0) return;
|
|
@@ -1133,7 +1236,7 @@ function themeFileApi(opts) {
|
|
|
1133
1236
|
(m) => ` ${m.token} (referenced by ${m.referencedBy.join(", ")})`
|
|
1134
1237
|
);
|
|
1135
1238
|
log(
|
|
1136
|
-
`[live-tokens] ${missing.length} token(s) referenced by components are not defined in ${
|
|
1239
|
+
`[live-tokens] ${missing.length} token(s) referenced by components are not defined in ${path4.relative(process.cwd(), CSS_PATH)}:
|
|
1137
1240
|
${lines.join("\n")}
|
|
1138
1241
|
These render as blank/empty editor slots. Run \`npx live-tokens migrate\` to reconcile.`
|
|
1139
1242
|
);
|
|
@@ -1141,30 +1244,30 @@ ${lines.join("\n")}
|
|
|
1141
1244
|
function autoMigrateAdditive(log) {
|
|
1142
1245
|
let before = "";
|
|
1143
1246
|
try {
|
|
1144
|
-
before =
|
|
1247
|
+
before = fs3.readFileSync(CSS_PATH, "utf-8");
|
|
1145
1248
|
} catch {
|
|
1146
1249
|
return;
|
|
1147
1250
|
}
|
|
1148
1251
|
const { css, applied, changed } = runAdditiveTokensCssMigrations(before);
|
|
1149
1252
|
if (!changed) return;
|
|
1150
|
-
|
|
1253
|
+
fs3.writeFileSync(CSS_PATH, css);
|
|
1151
1254
|
log(
|
|
1152
|
-
`[live-tokens] autoMigrate applied ${applied.length} additive migration(s) to ${
|
|
1255
|
+
`[live-tokens] autoMigrate applied ${applied.length} additive migration(s) to ${path4.relative(process.cwd(), CSS_PATH)}: ${applied.join(", ")}. Review the diff in git.`
|
|
1153
1256
|
);
|
|
1154
1257
|
}
|
|
1155
1258
|
function generateDefaultConfig(comp, sourcePath) {
|
|
1156
|
-
if (!
|
|
1259
|
+
if (!fs3.existsSync(sourcePath)) return;
|
|
1157
1260
|
const r = componentResource(comp);
|
|
1158
1261
|
r.ensureDir();
|
|
1159
1262
|
const defaultPath = r.filePath("default");
|
|
1160
|
-
const sourceStat =
|
|
1263
|
+
const sourceStat = fs3.statSync(sourcePath);
|
|
1161
1264
|
let createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1162
1265
|
let existingUpdatedAt;
|
|
1163
1266
|
let existingAliases;
|
|
1164
1267
|
let existingSchemaVersion;
|
|
1165
|
-
if (
|
|
1268
|
+
if (fs3.existsSync(defaultPath)) {
|
|
1166
1269
|
try {
|
|
1167
|
-
const existing = JSON.parse(
|
|
1270
|
+
const existing = JSON.parse(fs3.readFileSync(defaultPath, "utf-8"));
|
|
1168
1271
|
if (existing.createdAt) createdAt = existing.createdAt;
|
|
1169
1272
|
if (typeof existing.updatedAt === "string") existingUpdatedAt = existing.updatedAt;
|
|
1170
1273
|
if (existing.aliases && typeof existing.aliases === "object") {
|
|
@@ -1173,13 +1276,13 @@ ${lines.join("\n")}
|
|
|
1173
1276
|
if (typeof existing.schemaVersion === "number") existingSchemaVersion = existing.schemaVersion;
|
|
1174
1277
|
} catch {
|
|
1175
1278
|
}
|
|
1176
|
-
const defaultStat =
|
|
1279
|
+
const defaultStat = fs3.statSync(defaultPath);
|
|
1177
1280
|
if (defaultStat.mtimeMs >= sourceStat.mtimeMs && existingSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION) {
|
|
1178
1281
|
return;
|
|
1179
1282
|
}
|
|
1180
1283
|
}
|
|
1181
1284
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1182
|
-
const source =
|
|
1285
|
+
const source = fs3.readFileSync(sourcePath, "utf-8");
|
|
1183
1286
|
const body = extractGlobalRootBody(source);
|
|
1184
1287
|
const aliases = extractAliasDeclarations(body);
|
|
1185
1288
|
const aliasesUnchanged = existingAliases !== void 0 && JSON.stringify(existingAliases) === JSON.stringify(aliases);
|
|
@@ -1194,14 +1297,14 @@ ${lines.join("\n")}
|
|
|
1194
1297
|
};
|
|
1195
1298
|
if (aliasesUnchanged && schemaStampCurrent && existingUpdatedAt) {
|
|
1196
1299
|
const t = /* @__PURE__ */ new Date();
|
|
1197
|
-
|
|
1300
|
+
fs3.utimesSync(defaultPath, t, t);
|
|
1198
1301
|
return;
|
|
1199
1302
|
}
|
|
1200
|
-
|
|
1303
|
+
fs3.writeFileSync(defaultPath, JSON.stringify(defaultConfig, null, 2));
|
|
1201
1304
|
}
|
|
1202
1305
|
function ensureComponentConfigsDir() {
|
|
1203
|
-
if (!
|
|
1204
|
-
|
|
1306
|
+
if (!fs3.existsSync(COMPONENT_CONFIGS_DIR)) {
|
|
1307
|
+
fs3.mkdirSync(COMPONENT_CONFIGS_DIR, { recursive: true });
|
|
1205
1308
|
}
|
|
1206
1309
|
for (const sourcePath of listComponentSourcePaths()) {
|
|
1207
1310
|
const comp = componentNameFromFile(sourcePath);
|
|
@@ -1214,14 +1317,14 @@ ${lines.join("\n")}
|
|
|
1214
1317
|
const legacy = [];
|
|
1215
1318
|
const check = (dir) => {
|
|
1216
1319
|
for (const name of ["_active.json", "_production.json"]) {
|
|
1217
|
-
const p =
|
|
1218
|
-
if (
|
|
1320
|
+
const p = path4.join(dir, name);
|
|
1321
|
+
if (fs3.existsSync(p)) legacy.push(path4.relative(process.cwd(), p));
|
|
1219
1322
|
}
|
|
1220
1323
|
};
|
|
1221
1324
|
check(COLORS_AND_TYPE_DIR);
|
|
1222
|
-
if (
|
|
1223
|
-
for (const entry of
|
|
1224
|
-
if (entry.isDirectory()) check(
|
|
1325
|
+
if (fs3.existsSync(COMPONENT_CONFIGS_DIR)) {
|
|
1326
|
+
for (const entry of fs3.readdirSync(COMPONENT_CONFIGS_DIR, { withFileTypes: true })) {
|
|
1327
|
+
if (entry.isDirectory()) check(path4.join(COMPONENT_CONFIGS_DIR, entry.name));
|
|
1225
1328
|
}
|
|
1226
1329
|
}
|
|
1227
1330
|
return legacy;
|
|
@@ -1229,19 +1332,19 @@ ${lines.join("\n")}
|
|
|
1229
1332
|
function warnOnLegacyPointers(legacy, bakeHeld, warn) {
|
|
1230
1333
|
if (legacy.length === 0) return;
|
|
1231
1334
|
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 ${
|
|
1335
|
+
`[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
1336
|
);
|
|
1234
1337
|
}
|
|
1235
1338
|
function warnOnLegacyLayout(evidence, warn) {
|
|
1236
1339
|
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 ${
|
|
1340
|
+
`[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
1341
|
);
|
|
1239
1342
|
}
|
|
1240
1343
|
function warnOnUnmigratedSketchStyles(warn) {
|
|
1241
|
-
const legacyDir =
|
|
1242
|
-
if (!
|
|
1344
|
+
const legacyDir = path4.join(dataDirs.dataDir, "sketch-presets");
|
|
1345
|
+
if (!fs3.existsSync(legacyDir)) return;
|
|
1243
1346
|
warn(
|
|
1244
|
-
`[live-tokens] Saved sketchstyles still live under the retired ${
|
|
1347
|
+
`[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
1348
|
);
|
|
1246
1349
|
}
|
|
1247
1350
|
const diskThemeResolvers = {
|
|
@@ -1265,7 +1368,7 @@ ${lines.join("\n")}
|
|
|
1265
1368
|
const resolved = themesResource.existingPath(fileName);
|
|
1266
1369
|
if (resolved === null) return "missing";
|
|
1267
1370
|
try {
|
|
1268
|
-
if (isColorsAndTypeShaped(JSON.parse(
|
|
1371
|
+
if (isColorsAndTypeShaped(JSON.parse(fs3.readFileSync(resolved, "utf-8")))) {
|
|
1269
1372
|
return "colors-and-type";
|
|
1270
1373
|
}
|
|
1271
1374
|
} catch {
|
|
@@ -1273,7 +1376,7 @@ ${lines.join("\n")}
|
|
|
1273
1376
|
return "corrupt";
|
|
1274
1377
|
}
|
|
1275
1378
|
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 ${
|
|
1379
|
+
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
1380
|
}
|
|
1278
1381
|
function respondUnreadableTheme(res, fileName, missingError) {
|
|
1279
1382
|
switch (unreadableThemeReason(fileName)) {
|
|
@@ -1292,18 +1395,18 @@ ${lines.join("\n")}
|
|
|
1292
1395
|
}
|
|
1293
1396
|
function writeTheme(fileName, theme) {
|
|
1294
1397
|
themesResource.ensureDir();
|
|
1295
|
-
|
|
1398
|
+
fs3.writeFileSync(themesResource.filePath(fileName), JSON.stringify(theme, null, 2));
|
|
1296
1399
|
}
|
|
1297
1400
|
function migrateLocalThemes(warn) {
|
|
1298
|
-
if (
|
|
1299
|
-
if (!
|
|
1300
|
-
for (const file of
|
|
1401
|
+
if (path4.resolve(THEMES_DIR) === path4.resolve(packageThemesDir)) return;
|
|
1402
|
+
if (!fs3.existsSync(THEMES_DIR)) return;
|
|
1403
|
+
for (const file of fs3.readdirSync(THEMES_DIR)) {
|
|
1301
1404
|
if (!file.endsWith(".json") || file.startsWith("_")) continue;
|
|
1302
1405
|
const fileName = file.slice(0, -".json".length);
|
|
1303
1406
|
if (fileName === "default") continue;
|
|
1304
1407
|
let raw;
|
|
1305
1408
|
try {
|
|
1306
|
-
raw = JSON.parse(
|
|
1409
|
+
raw = JSON.parse(fs3.readFileSync(path4.join(THEMES_DIR, file), "utf-8"));
|
|
1307
1410
|
} catch {
|
|
1308
1411
|
continue;
|
|
1309
1412
|
}
|
|
@@ -1324,7 +1427,7 @@ ${lines.join("\n")}
|
|
|
1324
1427
|
function ensureDefaultTheme(warn) {
|
|
1325
1428
|
let existing = null;
|
|
1326
1429
|
try {
|
|
1327
|
-
existing = JSON.parse(
|
|
1430
|
+
existing = JSON.parse(fs3.readFileSync(themesResource.filePath("default"), "utf-8"));
|
|
1328
1431
|
} catch {
|
|
1329
1432
|
}
|
|
1330
1433
|
if (isColorsAndTypeShaped(existing)) {
|
|
@@ -1361,9 +1464,9 @@ ${lines.join("\n")}
|
|
|
1361
1464
|
themesResource.ensureDir();
|
|
1362
1465
|
ensureDefaultTheme(warn);
|
|
1363
1466
|
migrateLocalThemes(warn);
|
|
1364
|
-
const productionPointerHeld = !
|
|
1467
|
+
const productionPointerHeld = !fs3.existsSync(themesResource.productionPath) && listLegacyPointers().length > 0;
|
|
1365
1468
|
if (productionPointerHeld) {
|
|
1366
|
-
if (!
|
|
1469
|
+
if (!fs3.existsSync(themesResource.activePath)) themesResource.setActiveName("default");
|
|
1367
1470
|
} else {
|
|
1368
1471
|
themesResource.ensureMeta();
|
|
1369
1472
|
}
|
|
@@ -1442,8 +1545,8 @@ ${lines.join("\n")}
|
|
|
1442
1545
|
}
|
|
1443
1546
|
function clearAllWorking() {
|
|
1444
1547
|
colorsAndTypeResource.clearWorking();
|
|
1445
|
-
if (!
|
|
1446
|
-
for (const entry of
|
|
1548
|
+
if (!fs3.existsSync(COMPONENT_CONFIGS_DIR)) return;
|
|
1549
|
+
for (const entry of fs3.readdirSync(COMPONENT_CONFIGS_DIR, { withFileTypes: true })) {
|
|
1447
1550
|
if (entry.isDirectory()) componentResource(entry.name).clearWorking();
|
|
1448
1551
|
}
|
|
1449
1552
|
}
|
|
@@ -1487,6 +1590,8 @@ ${lines.join("\n")}
|
|
|
1487
1590
|
const SKETCH_STYLE_BY_NAME_REGEX = new RegExp(`^${escapedBase}/sketch-styles/([a-z0-9\\-_]+)$`);
|
|
1488
1591
|
const PRODUCTION_ROUTE = `${API_BASE}/production`;
|
|
1489
1592
|
const EVENTS_ROUTE = `${API_BASE}/events`;
|
|
1593
|
+
const A11Y_RUN_ROUTE = `${API_BASE}/a11y/run`;
|
|
1594
|
+
const A11Y_REPORT_ROUTE = `${API_BASE}/a11y/report`;
|
|
1490
1595
|
async function handleListColorsAndType(_ctx) {
|
|
1491
1596
|
const files = colorsAndTypeResource.listNames().map((fileName) => {
|
|
1492
1597
|
const data = colorsAndTypeResource.readJson(fileName);
|
|
@@ -1571,15 +1676,15 @@ ${lines.join("\n")}
|
|
|
1571
1676
|
}
|
|
1572
1677
|
const body = JSON.parse(await readBody(req));
|
|
1573
1678
|
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1574
|
-
if (
|
|
1679
|
+
if (fs3.existsSync(filePath)) {
|
|
1575
1680
|
try {
|
|
1576
|
-
const existing = JSON.parse(
|
|
1681
|
+
const existing = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
1577
1682
|
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
1578
1683
|
} catch {
|
|
1579
1684
|
}
|
|
1580
1685
|
}
|
|
1581
1686
|
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
1582
|
-
|
|
1687
|
+
fs3.writeFileSync(filePath, JSON.stringify(body, null, 2));
|
|
1583
1688
|
jsonResponse(res, 200, { ok: true, fileName });
|
|
1584
1689
|
return;
|
|
1585
1690
|
}
|
|
@@ -1588,8 +1693,8 @@ ${lines.join("\n")}
|
|
|
1588
1693
|
jsonResponse(res, 403, { error: "Cannot delete the default colors and type" });
|
|
1589
1694
|
return;
|
|
1590
1695
|
}
|
|
1591
|
-
if (
|
|
1592
|
-
|
|
1696
|
+
if (fs3.existsSync(filePath)) {
|
|
1697
|
+
fs3.unlinkSync(filePath);
|
|
1593
1698
|
} else if (colorsAndTypeResource.existingPath(fileName)) {
|
|
1594
1699
|
jsonResponse(res, 403, {
|
|
1595
1700
|
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 +1781,16 @@ ${lines.join("\n")}
|
|
|
1676
1781
|
body.component = comp;
|
|
1677
1782
|
body.name = body.name || name;
|
|
1678
1783
|
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1679
|
-
if (
|
|
1784
|
+
if (fs3.existsSync(configPath)) {
|
|
1680
1785
|
try {
|
|
1681
|
-
const existing = JSON.parse(
|
|
1786
|
+
const existing = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
1682
1787
|
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
1683
1788
|
} catch {
|
|
1684
1789
|
}
|
|
1685
1790
|
}
|
|
1686
1791
|
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
1687
1792
|
r.ensureDir();
|
|
1688
|
-
|
|
1793
|
+
fs3.writeFileSync(configPath, JSON.stringify(body, null, 2));
|
|
1689
1794
|
jsonResponse(res, 200, { ok: true, fileName: name });
|
|
1690
1795
|
return;
|
|
1691
1796
|
}
|
|
@@ -1694,7 +1799,7 @@ ${lines.join("\n")}
|
|
|
1694
1799
|
jsonResponse(res, 403, { error: "Cannot delete default config" });
|
|
1695
1800
|
return;
|
|
1696
1801
|
}
|
|
1697
|
-
if (
|
|
1802
|
+
if (fs3.existsSync(configPath)) fs3.unlinkSync(configPath);
|
|
1698
1803
|
jsonResponse(res, 200, { ok: true });
|
|
1699
1804
|
return;
|
|
1700
1805
|
}
|
|
@@ -1702,7 +1807,7 @@ ${lines.join("\n")}
|
|
|
1702
1807
|
async function handleListComponentConfigs({ params, res }) {
|
|
1703
1808
|
const [comp] = params;
|
|
1704
1809
|
const r = componentResource(comp);
|
|
1705
|
-
if (!
|
|
1810
|
+
if (!fs3.existsSync(r.dir)) {
|
|
1706
1811
|
jsonResponse(res, 404, { error: "Component not found" });
|
|
1707
1812
|
return;
|
|
1708
1813
|
}
|
|
@@ -1790,9 +1895,9 @@ ${lines.join("\n")}
|
|
|
1790
1895
|
const body = JSON.parse(await readBody(req));
|
|
1791
1896
|
const { theme } = normalizeTheme(body, diskThemeResolvers);
|
|
1792
1897
|
theme.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1793
|
-
if (
|
|
1898
|
+
if (fs3.existsSync(filePath)) {
|
|
1794
1899
|
try {
|
|
1795
|
-
const existing = JSON.parse(
|
|
1900
|
+
const existing = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
1796
1901
|
if (existing.createdAt) theme.createdAt = existing.createdAt;
|
|
1797
1902
|
} catch {
|
|
1798
1903
|
}
|
|
@@ -1814,7 +1919,7 @@ ${lines.join("\n")}
|
|
|
1814
1919
|
});
|
|
1815
1920
|
return;
|
|
1816
1921
|
}
|
|
1817
|
-
if (
|
|
1922
|
+
if (fs3.existsSync(filePath)) {
|
|
1818
1923
|
const deletingActive = themesResource.getActiveName() === fileName;
|
|
1819
1924
|
const liveColors = deletingActive ? resolveLiveColorsAndType(activeTheme()) : null;
|
|
1820
1925
|
const liveComponents = {};
|
|
@@ -1824,7 +1929,7 @@ ${lines.join("\n")}
|
|
|
1824
1929
|
if (resolved) liveComponents[comp] = resolved.data;
|
|
1825
1930
|
}
|
|
1826
1931
|
}
|
|
1827
|
-
|
|
1932
|
+
fs3.unlinkSync(filePath);
|
|
1828
1933
|
if (themesResource.existingPath(fileName) === null && deletingActive) {
|
|
1829
1934
|
themesResource.setActiveName("default");
|
|
1830
1935
|
}
|
|
@@ -1913,7 +2018,38 @@ data: ${JSON.stringify(state)}
|
|
|
1913
2018
|
|
|
1914
2019
|
`);
|
|
1915
2020
|
});
|
|
1916
|
-
|
|
2021
|
+
eventClients.add(res);
|
|
2022
|
+
req.on("close", () => {
|
|
2023
|
+
unsubscribe();
|
|
2024
|
+
eventClients.delete(res);
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2027
|
+
const eventClients = /* @__PURE__ */ new Set();
|
|
2028
|
+
const a11yRuns = a11yRunServer({
|
|
2029
|
+
root: process.cwd(),
|
|
2030
|
+
reportFile: A11Y_REPORT_FILE,
|
|
2031
|
+
runCli: spawnCli(PACKAGE_CLI_PATH),
|
|
2032
|
+
broadcast: (report) => {
|
|
2033
|
+
const frame = `event: ${A11Y_EVENT}
|
|
2034
|
+
data: ${JSON.stringify(report)}
|
|
2035
|
+
|
|
2036
|
+
`;
|
|
2037
|
+
for (const client of eventClients) client.write(frame);
|
|
2038
|
+
}
|
|
2039
|
+
});
|
|
2040
|
+
async function handleStartA11yRun({ req, res }) {
|
|
2041
|
+
let body;
|
|
2042
|
+
try {
|
|
2043
|
+
body = JSON.parse(await readBody(req));
|
|
2044
|
+
} catch {
|
|
2045
|
+
body = void 0;
|
|
2046
|
+
}
|
|
2047
|
+
const answer = a11yRuns.start(body);
|
|
2048
|
+
jsonResponse(res, answer.status, answer.body);
|
|
2049
|
+
}
|
|
2050
|
+
async function handleGetA11yReport({ res }) {
|
|
2051
|
+
const answer = a11yRuns.report();
|
|
2052
|
+
jsonResponse(res, answer.status, answer.body);
|
|
1917
2053
|
}
|
|
1918
2054
|
async function handleExportTheme({ params, res }) {
|
|
1919
2055
|
const [fileName] = params;
|
|
@@ -2074,22 +2210,22 @@ data: ${JSON.stringify(state)}
|
|
|
2074
2210
|
}
|
|
2075
2211
|
body.name = String(body.name || fileName);
|
|
2076
2212
|
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2077
|
-
if (
|
|
2213
|
+
if (fs3.existsSync(filePath)) {
|
|
2078
2214
|
try {
|
|
2079
|
-
const existing = JSON.parse(
|
|
2215
|
+
const existing = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
2080
2216
|
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
2081
2217
|
} catch {
|
|
2082
2218
|
}
|
|
2083
2219
|
}
|
|
2084
2220
|
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
2085
2221
|
sketchStylesResource.ensureDir();
|
|
2086
|
-
|
|
2222
|
+
fs3.writeFileSync(filePath, JSON.stringify(body, null, 2));
|
|
2087
2223
|
jsonResponse(res, 200, { ok: true, fileName });
|
|
2088
2224
|
return;
|
|
2089
2225
|
}
|
|
2090
2226
|
if (req.method === "DELETE") {
|
|
2091
|
-
if (
|
|
2092
|
-
|
|
2227
|
+
if (fs3.existsSync(filePath)) {
|
|
2228
|
+
fs3.unlinkSync(filePath);
|
|
2093
2229
|
} else if (sketchStylesResource.existingPath(fileName)) {
|
|
2094
2230
|
jsonResponse(res, 403, {
|
|
2095
2231
|
error: "Cannot delete a sketchstyle shipped with the package. Saving it creates a local copy; deleting that copy restores the shipped version.",
|
|
@@ -2186,12 +2322,20 @@ data: ${JSON.stringify(state)}
|
|
|
2186
2322
|
{ method: "DELETE", pattern: SKETCH_STYLE_BY_NAME_REGEX, handler: handleSketchStyleByName },
|
|
2187
2323
|
{ method: "POST", pattern: SKETCH_STYLE_BY_NAME_REGEX, handler: methodNotAllowed },
|
|
2188
2324
|
{ method: "GET", pattern: EVENTS_ROUTE, handler: handleEvents },
|
|
2325
|
+
{ method: "POST", pattern: A11Y_RUN_ROUTE, handler: handleStartA11yRun },
|
|
2326
|
+
{ method: "GET", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
|
|
2327
|
+
{ method: "PUT", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
|
|
2328
|
+
{ method: "DELETE", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
|
|
2329
|
+
{ method: "GET", pattern: A11Y_REPORT_ROUTE, handler: handleGetA11yReport },
|
|
2330
|
+
{ method: "PUT", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
|
|
2331
|
+
{ method: "POST", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
|
|
2332
|
+
{ method: "DELETE", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
|
|
2189
2333
|
{ method: "PUT", pattern: PRODUCTION_ROUTE, handler: handleAdoptTheme },
|
|
2190
2334
|
{ method: "GET", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
|
|
2191
2335
|
{ method: "POST", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
|
|
2192
2336
|
{ method: "DELETE", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed }
|
|
2193
2337
|
];
|
|
2194
|
-
const isEditorOwnedJson = (file) => file.endsWith(".json") &&
|
|
2338
|
+
const isEditorOwnedJson = (file) => file.endsWith(".json") && path4.resolve(file).startsWith(dataDirs.dataDir + path4.sep);
|
|
2195
2339
|
let buildLogger = null;
|
|
2196
2340
|
let buildChecked = false;
|
|
2197
2341
|
let productionCssLogger = null;
|
|
@@ -2241,7 +2385,7 @@ data: ${JSON.stringify(state)}
|
|
|
2241
2385
|
const { productionPointerHeld } = ensureThemesDir((msg) => server.config.logger.warn(msg));
|
|
2242
2386
|
const openTheme = activeTheme();
|
|
2243
2387
|
if (openTheme) pruneMatchingWorking(openTheme);
|
|
2244
|
-
if (!productionPointerHeld || !
|
|
2388
|
+
if (!productionPointerHeld || !fs3.existsSync(GENERATED_CSS_PATH)) regenerateTokensCss();
|
|
2245
2389
|
if (opts.autoMigrate && !ISOLATED_DATA_DIR) {
|
|
2246
2390
|
autoMigrateAdditive((msg) => server.config.logger.info(msg));
|
|
2247
2391
|
}
|
|
@@ -2264,10 +2408,11 @@ data: ${JSON.stringify(state)}
|
|
|
2264
2408
|
const handled = await dispatch(req, res, routes);
|
|
2265
2409
|
if (!handled) next();
|
|
2266
2410
|
});
|
|
2411
|
+
server.httpServer?.once("close", () => a11yRuns.stop());
|
|
2267
2412
|
},
|
|
2268
2413
|
handleHotUpdate(ctx) {
|
|
2269
2414
|
if (legacyLayout) return;
|
|
2270
|
-
const normalized =
|
|
2415
|
+
const normalized = path4.resolve(ctx.file);
|
|
2271
2416
|
if (!COMPONENTS_SCAN_DIRS.some((d) => normalized.startsWith(d))) return;
|
|
2272
2417
|
if (!normalized.endsWith(".svelte")) return;
|
|
2273
2418
|
if (!isThemeAwareComponent(normalized)) return;
|