meno-core 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{chunk-7ZLF4NE5.js → chunk-3JXK2QFU.js} +2 -2
- package/dist/chunks/{chunk-QWTQZHG3.js → chunk-FQBIC2OB.js} +1 -1
- package/dist/chunks/chunk-FQBIC2OB.js.map +7 -0
- package/dist/chunks/{chunk-J4IPTP5X.js → chunk-LOZL5HOF.js} +29 -3
- package/dist/chunks/chunk-LOZL5HOF.js.map +7 -0
- package/dist/lib/client/index.js +2 -2
- package/dist/lib/server/index.js +812 -218
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +4 -2
- package/dist/lib/shared/index.js.map +1 -1
- package/lib/client/scripts/formHandler.ts +17 -0
- package/lib/server/index.ts +19 -0
- package/lib/server/middleware/cors.test.ts +1 -1
- package/lib/server/middleware/cors.ts +1 -1
- package/lib/server/routes/api/functions.ts +2 -2
- package/lib/server/services/ColorService.ts +38 -7
- package/lib/server/services/VariableService.ts +34 -5
- package/lib/server/themeCssCodec.test.ts +154 -0
- package/lib/server/themeCssCodec.ts +488 -0
- package/lib/server/themeCssStore.test.ts +139 -0
- package/lib/server/themeCssStore.ts +210 -0
- package/lib/shared/nodeUtils.ts +18 -0
- package/lib/shared/types/colors.ts +6 -2
- package/lib/shared/types/variables.ts +14 -7
- package/lib/shared/utilityClassMapper.test.ts +10 -0
- package/lib/shared/utilityClassMapper.ts +9 -1
- package/lib/shared/validation/schemas.test.ts +78 -0
- package/lib/shared/validation/schemas.ts +53 -5
- package/package.json +1 -1
- package/dist/chunks/chunk-J4IPTP5X.js.map +0 -7
- package/dist/chunks/chunk-QWTQZHG3.js.map +0 -7
- /package/dist/chunks/{chunk-7ZLF4NE5.js.map → chunk-3JXK2QFU.js.map} +0 -0
package/dist/lib/server/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CMS_DRAFT_SUFFIX,
|
|
3
|
+
VARIABLE_GROUPS,
|
|
3
4
|
buildSlugIndex,
|
|
4
5
|
collectComponentLibraries,
|
|
5
6
|
createLogger,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
fontFaceCss,
|
|
9
10
|
fontPreloadLinks,
|
|
10
11
|
generateLibraryTags,
|
|
12
|
+
getDefaultScalingType,
|
|
11
13
|
getLocaleLinks,
|
|
12
14
|
isPathWithinRoot,
|
|
13
15
|
isReservedDraftFilename,
|
|
@@ -18,7 +20,7 @@ import {
|
|
|
18
20
|
resolveSlugToPageId,
|
|
19
21
|
rewriteComponentRefs,
|
|
20
22
|
translatePath
|
|
21
|
-
} from "../../chunks/chunk-
|
|
23
|
+
} from "../../chunks/chunk-FQBIC2OB.js";
|
|
22
24
|
import {
|
|
23
25
|
syncNetlifyLocale404Block
|
|
24
26
|
} from "../../chunks/chunk-2AR55GYH.js";
|
|
@@ -31,7 +33,7 @@ import {
|
|
|
31
33
|
processStructure,
|
|
32
34
|
resolveHtmlMapping,
|
|
33
35
|
skipEmptyTemplateAttributes
|
|
34
|
-
} from "../../chunks/chunk-
|
|
36
|
+
} from "../../chunks/chunk-3JXK2QFU.js";
|
|
35
37
|
import {
|
|
36
38
|
DEFAULT_BREAKPOINTS,
|
|
37
39
|
DEFAULT_FLUID_RANGE,
|
|
@@ -90,7 +92,7 @@ import {
|
|
|
90
92
|
validateCMSDraftItem,
|
|
91
93
|
validateCMSItem,
|
|
92
94
|
validateComponentDefinition
|
|
93
|
-
} from "../../chunks/chunk-
|
|
95
|
+
} from "../../chunks/chunk-LOZL5HOF.js";
|
|
94
96
|
import {
|
|
95
97
|
DEFAULT_I18N_CONFIG,
|
|
96
98
|
buildLocalizedPath,
|
|
@@ -282,7 +284,7 @@ init_constants();
|
|
|
282
284
|
// lib/server/middleware/cors.ts
|
|
283
285
|
var DEFAULT_OPTIONS = {
|
|
284
286
|
origin: "*",
|
|
285
|
-
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
287
|
+
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
|
286
288
|
allowedHeaders: ["Content-Type", "Authorization"],
|
|
287
289
|
credentials: false
|
|
288
290
|
};
|
|
@@ -457,13 +459,13 @@ async function bundleWithBun(entrypoint, opts) {
|
|
|
457
459
|
const code = await result.outputs[0].text();
|
|
458
460
|
return { code, success: true, errors: [] };
|
|
459
461
|
}
|
|
460
|
-
const errors = (result.logs || []).map((
|
|
462
|
+
const errors = (result.logs || []).map((log7) => {
|
|
461
463
|
const parts = [];
|
|
462
|
-
if (
|
|
463
|
-
if (
|
|
464
|
-
if (
|
|
465
|
-
if (
|
|
466
|
-
return parts.length > 0 ? parts.join("\n") : String(
|
|
464
|
+
if (log7.position?.file) parts.push(`File: ${log7.position.file}`);
|
|
465
|
+
if (log7.position?.line) parts.push(`Line ${log7.position.line}:${log7.position.column || 0}`);
|
|
466
|
+
if (log7.position?.lineText) parts.push(log7.position.lineText);
|
|
467
|
+
if (log7.message) parts.push(log7.message);
|
|
468
|
+
return parts.length > 0 ? parts.join("\n") : String(log7);
|
|
467
469
|
});
|
|
468
470
|
return { code: "", success: false, errors };
|
|
469
471
|
} catch (err) {
|
|
@@ -471,12 +473,12 @@ async function bundleWithBun(entrypoint, opts) {
|
|
|
471
473
|
if (opts.throw) throw err;
|
|
472
474
|
const errors = [];
|
|
473
475
|
if (be.logs && Array.isArray(be.logs)) {
|
|
474
|
-
for (const
|
|
476
|
+
for (const log7 of be.logs) {
|
|
475
477
|
const parts = [];
|
|
476
|
-
if (
|
|
477
|
-
if (
|
|
478
|
-
if (
|
|
479
|
-
errors.push(parts.length > 0 ? parts.join("\n") : String(
|
|
478
|
+
if (log7.position?.line) parts.push(`Line ${log7.position.line}:${log7.position.column || 0}`);
|
|
479
|
+
if (log7.position?.lineText) parts.push(log7.position.lineText);
|
|
480
|
+
if (log7.message) parts.push(log7.message);
|
|
481
|
+
errors.push(parts.length > 0 ? parts.join("\n") : String(log7));
|
|
480
482
|
}
|
|
481
483
|
} else {
|
|
482
484
|
errors.push(be.message || String(err));
|
|
@@ -486,8 +488,8 @@ async function bundleWithBun(entrypoint, opts) {
|
|
|
486
488
|
}
|
|
487
489
|
async function minifyWithBun(code) {
|
|
488
490
|
const { writeFile: writeFile3, rm: _rm } = await import("node:fs/promises");
|
|
489
|
-
const { join:
|
|
490
|
-
const tempFile =
|
|
491
|
+
const { join: join10 } = await import("node:path");
|
|
492
|
+
const tempFile = join10("/tmp", `meno-minify-${Date.now()}.js`);
|
|
491
493
|
try {
|
|
492
494
|
await writeFile3(tempFile, code, "utf-8");
|
|
493
495
|
const result = await Bun.build({
|
|
@@ -506,12 +508,12 @@ async function minifyWithBun(code) {
|
|
|
506
508
|
const { inspect: inspect2 } = await import("../../chunks/fs-G3ANWEFP.js");
|
|
507
509
|
let details = "";
|
|
508
510
|
if (be.logs && Array.isArray(be.logs)) {
|
|
509
|
-
details = be.logs.map((
|
|
511
|
+
details = be.logs.map((log7) => {
|
|
510
512
|
const parts = [];
|
|
511
|
-
if (
|
|
512
|
-
if (
|
|
513
|
-
if (
|
|
514
|
-
return parts.length > 0 ? parts.join("\n") : String(
|
|
513
|
+
if (log7.position?.line) parts.push(`Line ${log7.position.line}:${log7.position.column || 0}`);
|
|
514
|
+
if (log7.position?.lineText) parts.push(log7.position.lineText);
|
|
515
|
+
if (log7.message) parts.push(log7.message);
|
|
516
|
+
return parts.length > 0 ? parts.join("\n") : String(log7);
|
|
515
517
|
}).join("\n\n");
|
|
516
518
|
}
|
|
517
519
|
if (!details) {
|
|
@@ -524,16 +526,16 @@ async function minifyWithBun(code) {
|
|
|
524
526
|
return { code: "", success: false, errors: [details] };
|
|
525
527
|
} finally {
|
|
526
528
|
try {
|
|
527
|
-
const { rm } = await import("node:fs/promises");
|
|
528
|
-
await
|
|
529
|
+
const { rm: rm2 } = await import("node:fs/promises");
|
|
530
|
+
await rm2(tempFile, { force: true });
|
|
529
531
|
} catch {
|
|
530
532
|
}
|
|
531
533
|
}
|
|
532
534
|
}
|
|
533
535
|
async function validateWithBun(code) {
|
|
534
|
-
const { writeFile: writeFile3, rm } = await import("node:fs/promises");
|
|
535
|
-
const { join:
|
|
536
|
-
const tempFile =
|
|
536
|
+
const { writeFile: writeFile3, rm: rm2 } = await import("node:fs/promises");
|
|
537
|
+
const { join: join10 } = await import("node:path");
|
|
538
|
+
const tempFile = join10("/tmp", `meno-validate-${Date.now()}-${Math.random().toString(36).slice(2)}.js`);
|
|
537
539
|
try {
|
|
538
540
|
await writeFile3(tempFile, code, "utf-8");
|
|
539
541
|
await Bun.build({
|
|
@@ -547,12 +549,12 @@ async function validateWithBun(code) {
|
|
|
547
549
|
const { inspect: inspect2 } = await import("../../chunks/fs-G3ANWEFP.js");
|
|
548
550
|
let details = "";
|
|
549
551
|
if (be.logs && Array.isArray(be.logs)) {
|
|
550
|
-
details = be.logs.map((
|
|
552
|
+
details = be.logs.map((log7) => {
|
|
551
553
|
const parts = [];
|
|
552
|
-
if (
|
|
553
|
-
if (
|
|
554
|
-
if (
|
|
555
|
-
return parts.length > 0 ? parts.join("\n") : String(
|
|
554
|
+
if (log7.position?.line) parts.push(`Line ${log7.position.line}:${log7.position.column || 0}`);
|
|
555
|
+
if (log7.position?.lineText) parts.push(log7.position.lineText);
|
|
556
|
+
if (log7.message) parts.push(log7.message);
|
|
557
|
+
return parts.length > 0 ? parts.join("\n") : String(log7);
|
|
556
558
|
}).join("\n\n");
|
|
557
559
|
}
|
|
558
560
|
if (!details) {
|
|
@@ -565,7 +567,7 @@ async function validateWithBun(code) {
|
|
|
565
567
|
return details || "Unknown JavaScript error";
|
|
566
568
|
} finally {
|
|
567
569
|
try {
|
|
568
|
-
await
|
|
570
|
+
await rm2(tempFile, { force: true });
|
|
569
571
|
} catch {
|
|
570
572
|
}
|
|
571
573
|
}
|
|
@@ -1690,6 +1692,9 @@ async function handleComponentsConfigRoute() {
|
|
|
1690
1692
|
}
|
|
1691
1693
|
}
|
|
1692
1694
|
|
|
1695
|
+
// lib/server/services/ColorService.ts
|
|
1696
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
1697
|
+
|
|
1693
1698
|
// lib/server/services/CachedConfigLoader.ts
|
|
1694
1699
|
var CachedConfigLoader = class {
|
|
1695
1700
|
config = null;
|
|
@@ -1730,8 +1735,511 @@ var CachedConfigLoader = class {
|
|
|
1730
1735
|
}
|
|
1731
1736
|
};
|
|
1732
1737
|
|
|
1738
|
+
// lib/server/themeCssStore.ts
|
|
1739
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
1740
|
+
import { rm } from "node:fs/promises";
|
|
1741
|
+
import { join as join2 } from "node:path";
|
|
1742
|
+
|
|
1743
|
+
// lib/server/themeCssCodec.ts
|
|
1744
|
+
var COLORS_LABEL = "Colors";
|
|
1745
|
+
var OTHER_LABEL = "Other";
|
|
1746
|
+
var HEADER_BANNER = `/*
|
|
1747
|
+
* Meno design tokens \u2014 theme.css is the source of truth for this project's colors and
|
|
1748
|
+
* variables. Edit values here, or use the Studio color/variable panels. Variables are
|
|
1749
|
+
* grouped by the comment sections below; the @media regions are regenerated on save
|
|
1750
|
+
* (responsive scaling is configured in project.config.json).
|
|
1751
|
+
*/`;
|
|
1752
|
+
var AUTO_BANNER = "/* === Responsive scaling \u2014 auto-generated from project.config.json; regenerated on save === */";
|
|
1753
|
+
var OVERRIDES_BANNER = "/* === Responsive overrides \u2014 per-variable; preserved across regen === */";
|
|
1754
|
+
function sectionLabelForGroup(group) {
|
|
1755
|
+
if (!group || group === "other") return OTHER_LABEL;
|
|
1756
|
+
return VARIABLE_GROUPS.find((g) => g.value === group)?.label ?? OTHER_LABEL;
|
|
1757
|
+
}
|
|
1758
|
+
var LABEL_TO_GROUP = new Map(VARIABLE_GROUPS.map((g) => [g.label.toLowerCase(), g.value]));
|
|
1759
|
+
function groupForSectionLabel(label) {
|
|
1760
|
+
return LABEL_TO_GROUP.get(label.trim().toLowerCase()) ?? null;
|
|
1761
|
+
}
|
|
1762
|
+
function isColorValue(value) {
|
|
1763
|
+
const v = value.trim().toLowerCase();
|
|
1764
|
+
if (/^#[0-9a-f]{3,8}$/.test(v)) return true;
|
|
1765
|
+
if (/^(rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color)\(/.test(v)) return true;
|
|
1766
|
+
if (v === "transparent" || v === "currentcolor") return true;
|
|
1767
|
+
return false;
|
|
1768
|
+
}
|
|
1769
|
+
function inferGroup(cssVar, value) {
|
|
1770
|
+
const v = value.trim().toLowerCase();
|
|
1771
|
+
if (/^\d+(\.\d+)?m?s$/.test(v)) return "duration";
|
|
1772
|
+
if (/^(normal|bold|lighter|bolder)$/.test(v)) return "font-weight";
|
|
1773
|
+
if (/^\d+$/.test(v)) {
|
|
1774
|
+
const n = Number(v);
|
|
1775
|
+
if (n >= 100 && n <= 900 && n % 100 === 0) return "font-weight";
|
|
1776
|
+
}
|
|
1777
|
+
if (/^\d*\.\d+$/.test(v) || /^[1-9](\.\d+)?$/.test(v)) return "line-height";
|
|
1778
|
+
const name = cssVar.replace(/^--/, "").toLowerCase();
|
|
1779
|
+
for (const g of VARIABLE_GROUPS) {
|
|
1780
|
+
if (g.value !== "other" && name.includes(g.value)) return g.value;
|
|
1781
|
+
}
|
|
1782
|
+
return "other";
|
|
1783
|
+
}
|
|
1784
|
+
function groupVariables(variables) {
|
|
1785
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1786
|
+
for (const v of variables) {
|
|
1787
|
+
const label = sectionLabelForGroup(v.group);
|
|
1788
|
+
(buckets.get(label) ?? buckets.set(label, []).get(label)).push(v);
|
|
1789
|
+
}
|
|
1790
|
+
const ordered = [];
|
|
1791
|
+
for (const g of VARIABLE_GROUPS) {
|
|
1792
|
+
if (g.value === "other") continue;
|
|
1793
|
+
const vars = buckets.get(g.label);
|
|
1794
|
+
if (vars?.length) {
|
|
1795
|
+
ordered.push({ label: g.label, vars });
|
|
1796
|
+
buckets.delete(g.label);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
for (const [label, vars] of buckets) if (vars.length) ordered.push({ label, vars });
|
|
1800
|
+
return ordered;
|
|
1801
|
+
}
|
|
1802
|
+
function buildAutoMediaBlocks(scalable, cfg) {
|
|
1803
|
+
const { breakpoints, responsiveScales } = cfg;
|
|
1804
|
+
const baseRef = responsiveScales.baseReference || 16;
|
|
1805
|
+
const sorted = Object.entries(breakpoints).sort((a, b) => b[1].breakpoint - a[1].breakpoint);
|
|
1806
|
+
const blocks = [];
|
|
1807
|
+
for (const [bpName, bp] of sorted) {
|
|
1808
|
+
const lines = [];
|
|
1809
|
+
for (const v of scalable) {
|
|
1810
|
+
if (v.scales?.[bpName] !== void 0) continue;
|
|
1811
|
+
const categoryScales = responsiveScales[v.type];
|
|
1812
|
+
const scale = categoryScales?.[bpName];
|
|
1813
|
+
if (scale == null) continue;
|
|
1814
|
+
const scaled = scalePropertyValue(v.value, baseRef, scale);
|
|
1815
|
+
if (scaled !== null && scaled !== v.value) lines.push(` ${v.cssVar}: ${scaled};`);
|
|
1816
|
+
}
|
|
1817
|
+
if (lines.length) blocks.push(`@media (max-width: ${bp.breakpoint}px) {
|
|
1818
|
+
:root {
|
|
1819
|
+
${lines.join("\n")}
|
|
1820
|
+
}
|
|
1821
|
+
}`);
|
|
1822
|
+
}
|
|
1823
|
+
return blocks.join("\n\n");
|
|
1824
|
+
}
|
|
1825
|
+
function buildOverrideMediaBlocks(variables, breakpoints) {
|
|
1826
|
+
const sorted = Object.entries(breakpoints).sort((a, b) => b[1].breakpoint - a[1].breakpoint);
|
|
1827
|
+
const blocks = [];
|
|
1828
|
+
for (const [bpName, bp] of sorted) {
|
|
1829
|
+
const lines = [];
|
|
1830
|
+
for (const v of variables) {
|
|
1831
|
+
const override = v.scales?.[bpName];
|
|
1832
|
+
if (override !== void 0 && override !== v.value) lines.push(` ${v.cssVar}: ${override};`);
|
|
1833
|
+
}
|
|
1834
|
+
if (lines.length) blocks.push(`@media (max-width: ${bp.breakpoint}px) {
|
|
1835
|
+
:root {
|
|
1836
|
+
${lines.join("\n")}
|
|
1837
|
+
}
|
|
1838
|
+
}`);
|
|
1839
|
+
}
|
|
1840
|
+
return blocks.join("\n\n");
|
|
1841
|
+
}
|
|
1842
|
+
function serializeThemeCss(model, scaleConfig2) {
|
|
1843
|
+
const { themes, variables, raw = [] } = model;
|
|
1844
|
+
const palette = themes?.palette;
|
|
1845
|
+
const defaultName = themes?.default ?? "default";
|
|
1846
|
+
const defaultTheme = themes?.themes?.[defaultName];
|
|
1847
|
+
const sections = [];
|
|
1848
|
+
const colorEntries = Object.entries(defaultTheme?.colors ?? {});
|
|
1849
|
+
if (colorEntries.length) {
|
|
1850
|
+
sections.push(
|
|
1851
|
+
[
|
|
1852
|
+
` /* ${COLORS_LABEL}: ${defaultName} */`,
|
|
1853
|
+
...colorEntries.map(([k, val]) => ` --${k}: ${resolvePaletteColor(val, palette)};`)
|
|
1854
|
+
].join("\n")
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
for (const { label, vars } of groupVariables(variables)) {
|
|
1858
|
+
sections.push([` /* ${label} */`, ...vars.map((v) => ` ${v.cssVar}: ${v.value};`)].join("\n"));
|
|
1859
|
+
}
|
|
1860
|
+
const blocks = [HEADER_BANNER];
|
|
1861
|
+
blocks.push(sections.length ? `:root {
|
|
1862
|
+
${sections.join("\n\n")}
|
|
1863
|
+
}` : ":root {}");
|
|
1864
|
+
for (const [name, theme] of Object.entries(themes?.themes ?? {})) {
|
|
1865
|
+
if (name === defaultName) continue;
|
|
1866
|
+
const lines = Object.entries(theme.colors ?? {}).map(
|
|
1867
|
+
([k, val]) => ` --${k}: ${resolvePaletteColor(val, palette)};`
|
|
1868
|
+
);
|
|
1869
|
+
if (lines.length) blocks.push(`[theme="${name}"] {
|
|
1870
|
+
${lines.join("\n")}
|
|
1871
|
+
}`);
|
|
1872
|
+
}
|
|
1873
|
+
if (scaleConfig2.responsiveScales?.enabled) {
|
|
1874
|
+
const auto = buildAutoMediaBlocks(
|
|
1875
|
+
variables.filter((v) => v.type && v.type !== "none"),
|
|
1876
|
+
scaleConfig2
|
|
1877
|
+
);
|
|
1878
|
+
if (auto) blocks.push(AUTO_BANNER, auto);
|
|
1879
|
+
}
|
|
1880
|
+
const overrides = buildOverrideMediaBlocks(variables, scaleConfig2.breakpoints);
|
|
1881
|
+
if (overrides) blocks.push(OVERRIDES_BANNER, overrides);
|
|
1882
|
+
for (const r of raw) {
|
|
1883
|
+
const t = r.trim();
|
|
1884
|
+
if (t) blocks.push(t);
|
|
1885
|
+
}
|
|
1886
|
+
return `${blocks.join("\n\n")}
|
|
1887
|
+
`;
|
|
1888
|
+
}
|
|
1889
|
+
function skipString(s, i) {
|
|
1890
|
+
const q = s[i];
|
|
1891
|
+
i++;
|
|
1892
|
+
while (i < s.length) {
|
|
1893
|
+
if (s[i] === "\\") {
|
|
1894
|
+
i += 2;
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
if (s[i] === q) return i + 1;
|
|
1898
|
+
i++;
|
|
1899
|
+
}
|
|
1900
|
+
return i;
|
|
1901
|
+
}
|
|
1902
|
+
function readBlock(s, open) {
|
|
1903
|
+
let i = open + 1;
|
|
1904
|
+
let depth = 1;
|
|
1905
|
+
const bodyStart = i;
|
|
1906
|
+
while (i < s.length && depth > 0) {
|
|
1907
|
+
if (s[i] === "/" && s[i + 1] === "*") {
|
|
1908
|
+
const e = s.indexOf("*/", i + 2);
|
|
1909
|
+
i = e === -1 ? s.length : e + 2;
|
|
1910
|
+
continue;
|
|
1911
|
+
}
|
|
1912
|
+
if (s[i] === '"' || s[i] === "'") {
|
|
1913
|
+
i = skipString(s, i);
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
if (s[i] === "{") depth++;
|
|
1917
|
+
else if (s[i] === "}") {
|
|
1918
|
+
depth--;
|
|
1919
|
+
if (depth === 0) break;
|
|
1920
|
+
}
|
|
1921
|
+
i++;
|
|
1922
|
+
}
|
|
1923
|
+
return { body: s.slice(bodyStart, i), end: i < s.length ? i + 1 : s.length };
|
|
1924
|
+
}
|
|
1925
|
+
function scanTopLevel(css) {
|
|
1926
|
+
const nodes = [];
|
|
1927
|
+
let i = 0;
|
|
1928
|
+
while (i < css.length) {
|
|
1929
|
+
while (i < css.length && /\s/.test(css[i])) i++;
|
|
1930
|
+
if (i >= css.length) break;
|
|
1931
|
+
if (css[i] === "/" && css[i + 1] === "*") {
|
|
1932
|
+
const e = css.indexOf("*/", i + 2);
|
|
1933
|
+
const stop = e === -1 ? css.length : e + 2;
|
|
1934
|
+
const text = css.slice(i, stop);
|
|
1935
|
+
nodes.push({ kind: "comment", text, raw: text });
|
|
1936
|
+
i = stop;
|
|
1937
|
+
continue;
|
|
1938
|
+
}
|
|
1939
|
+
const start = i;
|
|
1940
|
+
while (i < css.length) {
|
|
1941
|
+
const c = css[i];
|
|
1942
|
+
if (c === "/" && css[i + 1] === "*") {
|
|
1943
|
+
const e = css.indexOf("*/", i + 2);
|
|
1944
|
+
i = e === -1 ? css.length : e + 2;
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
if (c === '"' || c === "'") {
|
|
1948
|
+
i = skipString(css, i);
|
|
1949
|
+
continue;
|
|
1950
|
+
}
|
|
1951
|
+
if (c === "{" || c === ";") break;
|
|
1952
|
+
i++;
|
|
1953
|
+
}
|
|
1954
|
+
if (i < css.length && css[i] === "{") {
|
|
1955
|
+
const prelude = css.slice(start, i).trim();
|
|
1956
|
+
const { body, end } = readBlock(css, i);
|
|
1957
|
+
nodes.push({ kind: "block", prelude, body, raw: css.slice(start, end) });
|
|
1958
|
+
i = end;
|
|
1959
|
+
} else {
|
|
1960
|
+
const stop = i < css.length ? i + 1 : css.length;
|
|
1961
|
+
const raw = css.slice(start, stop).trim();
|
|
1962
|
+
if (raw) nodes.push({ kind: "statement", raw });
|
|
1963
|
+
i = stop;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
return nodes;
|
|
1967
|
+
}
|
|
1968
|
+
function scanDeclarations(body) {
|
|
1969
|
+
const items = [];
|
|
1970
|
+
let i = 0;
|
|
1971
|
+
while (i < body.length) {
|
|
1972
|
+
while (i < body.length && /\s/.test(body[i])) i++;
|
|
1973
|
+
if (i >= body.length) break;
|
|
1974
|
+
if (body[i] === "/" && body[i + 1] === "*") {
|
|
1975
|
+
const e = body.indexOf("*/", i + 2);
|
|
1976
|
+
const stop = e === -1 ? body.length : e + 2;
|
|
1977
|
+
items.push({ kind: "comment", text: body.slice(i, stop) });
|
|
1978
|
+
i = stop;
|
|
1979
|
+
continue;
|
|
1980
|
+
}
|
|
1981
|
+
const start = i;
|
|
1982
|
+
while (i < body.length) {
|
|
1983
|
+
const c = body[i];
|
|
1984
|
+
if (c === "/" && body[i + 1] === "*") {
|
|
1985
|
+
const e = body.indexOf("*/", i + 2);
|
|
1986
|
+
i = e === -1 ? body.length : e + 2;
|
|
1987
|
+
continue;
|
|
1988
|
+
}
|
|
1989
|
+
if (c === '"' || c === "'") {
|
|
1990
|
+
i = skipString(body, i);
|
|
1991
|
+
continue;
|
|
1992
|
+
}
|
|
1993
|
+
if (c === ";") break;
|
|
1994
|
+
i++;
|
|
1995
|
+
}
|
|
1996
|
+
const text = body.slice(start, i).trim();
|
|
1997
|
+
i = i < body.length ? i + 1 : body.length;
|
|
1998
|
+
const colon = text.indexOf(":");
|
|
1999
|
+
if (colon > 0) {
|
|
2000
|
+
items.push({ kind: "decl", prop: text.slice(0, colon).trim(), value: text.slice(colon + 1).trim() });
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
return items;
|
|
2004
|
+
}
|
|
2005
|
+
function commentInner(text) {
|
|
2006
|
+
return text.replace(/^\/\*+/, "").replace(/\*+\/$/, "").trim();
|
|
2007
|
+
}
|
|
2008
|
+
function isHeaderBanner(text) {
|
|
2009
|
+
const t = commentInner(text);
|
|
2010
|
+
return /meno design tokens/i.test(t) || /AUTO-GENERATED by meno-astro/i.test(t);
|
|
2011
|
+
}
|
|
2012
|
+
function prettifyName(name) {
|
|
2013
|
+
return name.replace(/[-_]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2014
|
+
}
|
|
2015
|
+
function maxWidthPx(prelude) {
|
|
2016
|
+
const m = prelude.match(/max-width:\s*(\d+)px/i);
|
|
2017
|
+
return m ? Number(m[1]) : null;
|
|
2018
|
+
}
|
|
2019
|
+
function bpNameForPx(px, breakpoints) {
|
|
2020
|
+
for (const [name, entry] of Object.entries(breakpoints)) if (entry.breakpoint === px) return name;
|
|
2021
|
+
return null;
|
|
2022
|
+
}
|
|
2023
|
+
function parseThemeCss(css, breakpoints) {
|
|
2024
|
+
const themesMap = {};
|
|
2025
|
+
const variables = [];
|
|
2026
|
+
const varByCssVar = /* @__PURE__ */ new Map();
|
|
2027
|
+
const raw = [];
|
|
2028
|
+
let defaultName = null;
|
|
2029
|
+
let region = null;
|
|
2030
|
+
for (const node of scanTopLevel(css)) {
|
|
2031
|
+
if (node.kind === "comment") {
|
|
2032
|
+
if (isHeaderBanner(node.text)) continue;
|
|
2033
|
+
const inner = commentInner(node.text);
|
|
2034
|
+
if (/responsive overrides/i.test(inner)) {
|
|
2035
|
+
region = "overrides";
|
|
2036
|
+
continue;
|
|
2037
|
+
}
|
|
2038
|
+
if (/responsive scaling|auto-generated from project\.config/i.test(inner)) {
|
|
2039
|
+
region = "auto";
|
|
2040
|
+
continue;
|
|
2041
|
+
}
|
|
2042
|
+
raw.push(node.raw);
|
|
2043
|
+
continue;
|
|
2044
|
+
}
|
|
2045
|
+
if (node.kind === "statement") {
|
|
2046
|
+
raw.push(node.raw);
|
|
2047
|
+
continue;
|
|
2048
|
+
}
|
|
2049
|
+
const sel = node.prelude;
|
|
2050
|
+
if (sel === ":root") {
|
|
2051
|
+
let section = null;
|
|
2052
|
+
for (const it of scanDeclarations(node.body)) {
|
|
2053
|
+
if (it.kind === "comment") {
|
|
2054
|
+
const inner = commentInner(it.text);
|
|
2055
|
+
if (/^colors\b/i.test(inner)) {
|
|
2056
|
+
section = "colors";
|
|
2057
|
+
const dn = inner.match(/^colors\s*:\s*([\w-]+)/i);
|
|
2058
|
+
if (dn) defaultName = dn[1];
|
|
2059
|
+
} else {
|
|
2060
|
+
const g = groupForSectionLabel(inner);
|
|
2061
|
+
if (g) section = g;
|
|
2062
|
+
}
|
|
2063
|
+
continue;
|
|
2064
|
+
}
|
|
2065
|
+
if (!it.prop.startsWith("--")) continue;
|
|
2066
|
+
const name = it.prop.slice(2);
|
|
2067
|
+
if (section === "colors" || section === null && isColorValue(it.value)) {
|
|
2068
|
+
const dn = defaultName ??= "default";
|
|
2069
|
+
(themesMap[dn] ??= { colors: {} }).colors[name] = it.value;
|
|
2070
|
+
} else {
|
|
2071
|
+
const group = section ?? inferGroup(it.prop, it.value);
|
|
2072
|
+
const v = { cssVar: it.prop, value: it.value, type: getDefaultScalingType(group), group };
|
|
2073
|
+
variables.push(v);
|
|
2074
|
+
varByCssVar.set(it.prop, v);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
continue;
|
|
2078
|
+
}
|
|
2079
|
+
const themeMatch = sel.match(/^\[theme=["']?([^"'\]]+)["']?\]$/);
|
|
2080
|
+
if (themeMatch) {
|
|
2081
|
+
const theme = themesMap[themeMatch[1]] ??= { colors: {} };
|
|
2082
|
+
for (const it of scanDeclarations(node.body)) {
|
|
2083
|
+
if (it.kind === "decl" && it.prop.startsWith("--")) theme.colors[it.prop.slice(2)] = it.value;
|
|
2084
|
+
}
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
if (/^@media\b/i.test(sel)) {
|
|
2088
|
+
const px = maxWidthPx(sel);
|
|
2089
|
+
if (region === "auto") continue;
|
|
2090
|
+
const bpName = px != null && breakpoints ? bpNameForPx(px, breakpoints) : null;
|
|
2091
|
+
if (!bpName) {
|
|
2092
|
+
raw.push(node.raw);
|
|
2093
|
+
continue;
|
|
2094
|
+
}
|
|
2095
|
+
for (const inner of scanTopLevel(node.body)) {
|
|
2096
|
+
if (inner.kind !== "block" || inner.prelude !== ":root") continue;
|
|
2097
|
+
for (const it of scanDeclarations(inner.body)) {
|
|
2098
|
+
if (it.kind !== "decl" || !it.prop.startsWith("--")) continue;
|
|
2099
|
+
const v = varByCssVar.get(it.prop);
|
|
2100
|
+
if (v) (v.scales ??= {})[bpName] = it.value;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
continue;
|
|
2104
|
+
}
|
|
2105
|
+
raw.push(node.raw);
|
|
2106
|
+
}
|
|
2107
|
+
if (!defaultName) defaultName = Object.keys(themesMap)[0] ?? "default";
|
|
2108
|
+
(themesMap[defaultName] ??= { colors: {} }).label ??= prettifyName(defaultName);
|
|
2109
|
+
for (const [name, t] of Object.entries(themesMap)) t.label ??= prettifyName(name);
|
|
2110
|
+
return { themes: { default: defaultName, themes: themesMap }, variables, raw };
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
// lib/server/themeCssStore.ts
|
|
2114
|
+
var log2 = createLogger("themeCssStore");
|
|
2115
|
+
function themeCssPath(root = getProjectRoot()) {
|
|
2116
|
+
return join2(root, "src", "styles", "theme.css");
|
|
2117
|
+
}
|
|
2118
|
+
function isThemeCssProject(root = getProjectRoot()) {
|
|
2119
|
+
if (existsSync2(themeCssPath(root))) return true;
|
|
2120
|
+
if (existsSync2(join2(root, "src", "pages"))) return true;
|
|
2121
|
+
try {
|
|
2122
|
+
const cfg = JSON.parse(readFileSync(join2(root, "project.config.json"), "utf8"));
|
|
2123
|
+
if (cfg.format === "astro") return true;
|
|
2124
|
+
} catch {
|
|
2125
|
+
}
|
|
2126
|
+
return false;
|
|
2127
|
+
}
|
|
2128
|
+
var writeChain = Promise.resolve();
|
|
2129
|
+
function enqueue(fn) {
|
|
2130
|
+
const run = writeChain.then(fn, fn);
|
|
2131
|
+
writeChain = run.then(
|
|
2132
|
+
() => void 0,
|
|
2133
|
+
() => void 0
|
|
2134
|
+
);
|
|
2135
|
+
return run;
|
|
2136
|
+
}
|
|
2137
|
+
async function readJsonSafe(path3) {
|
|
2138
|
+
try {
|
|
2139
|
+
return JSON.parse(await readTextFile(path3));
|
|
2140
|
+
} catch {
|
|
2141
|
+
return null;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
function scaleConfig() {
|
|
2145
|
+
return { breakpoints: configService.getBreakpoints(), responsiveScales: configService.getResponsiveScales() };
|
|
2146
|
+
}
|
|
2147
|
+
function emptyModel() {
|
|
2148
|
+
return { themes: { default: "default", themes: { default: { colors: {} } } }, variables: [], raw: [] };
|
|
2149
|
+
}
|
|
2150
|
+
async function readModel(root) {
|
|
2151
|
+
const path3 = themeCssPath(root);
|
|
2152
|
+
if (!await fileExists(path3)) return emptyModel();
|
|
2153
|
+
return parseThemeCss(await readTextFile(path3), configService.getBreakpoints());
|
|
2154
|
+
}
|
|
2155
|
+
async function writeModel(root, model) {
|
|
2156
|
+
const css = serializeThemeCss(model, scaleConfig());
|
|
2157
|
+
await ensureDir(join2(root, "src", "styles"));
|
|
2158
|
+
await writeFile(themeCssPath(root), css);
|
|
2159
|
+
}
|
|
2160
|
+
function hasColors(themes) {
|
|
2161
|
+
return Object.values(themes.themes ?? {}).some((t) => Object.keys(t.colors ?? {}).length > 0);
|
|
2162
|
+
}
|
|
2163
|
+
function mergeThemes(current, incoming) {
|
|
2164
|
+
if (!incoming?.themes) return current;
|
|
2165
|
+
if (!hasColors(current)) return incoming;
|
|
2166
|
+
const out = { default: current.default, palette: current.palette, themes: { ...current.themes } };
|
|
2167
|
+
for (const [name, theme] of Object.entries(incoming.themes)) {
|
|
2168
|
+
const existing = out.themes[name];
|
|
2169
|
+
out.themes[name] = existing ? { ...existing, colors: { ...theme.colors, ...existing.colors } } : theme;
|
|
2170
|
+
}
|
|
2171
|
+
return out;
|
|
2172
|
+
}
|
|
2173
|
+
function mergeVariables(current, incoming) {
|
|
2174
|
+
if (!incoming?.length) return current;
|
|
2175
|
+
const seen = new Set(current.map((v) => v.cssVar));
|
|
2176
|
+
return [...current, ...incoming.filter((v) => !seen.has(v.cssVar))];
|
|
2177
|
+
}
|
|
2178
|
+
var AUTHORED_MARKER = "Meno design tokens";
|
|
2179
|
+
async function migrateIfNeeded(root) {
|
|
2180
|
+
const colorsJson = join2(root, "colors.json");
|
|
2181
|
+
const variablesJson = join2(root, "variables.json");
|
|
2182
|
+
const hasColorsJson = existsSync2(colorsJson);
|
|
2183
|
+
const hasVarsJson = existsSync2(variablesJson);
|
|
2184
|
+
if (!hasColorsJson && !hasVarsJson) return;
|
|
2185
|
+
try {
|
|
2186
|
+
const jsonColors = hasColorsJson ? await readJsonSafe(colorsJson) : null;
|
|
2187
|
+
const jsonVars = hasVarsJson ? await readJsonSafe(variablesJson) : null;
|
|
2188
|
+
let current = emptyModel();
|
|
2189
|
+
let authored = false;
|
|
2190
|
+
if (await fileExists(themeCssPath(root))) {
|
|
2191
|
+
const text = await readTextFile(themeCssPath(root));
|
|
2192
|
+
authored = text.includes(AUTHORED_MARKER);
|
|
2193
|
+
current = parseThemeCss(text, configService.getBreakpoints());
|
|
2194
|
+
}
|
|
2195
|
+
const base = authored ? current : emptyModel();
|
|
2196
|
+
const merged = {
|
|
2197
|
+
themes: mergeThemes(base.themes, jsonColors?.themes ? jsonColors : null),
|
|
2198
|
+
variables: mergeVariables(base.variables, jsonVars?.variables),
|
|
2199
|
+
raw: current.raw
|
|
2200
|
+
};
|
|
2201
|
+
await writeModel(root, merged);
|
|
2202
|
+
await rm(colorsJson, { force: true });
|
|
2203
|
+
await rm(variablesJson, { force: true });
|
|
2204
|
+
log2.info("Folded colors.json / variables.json into src/styles/theme.css");
|
|
2205
|
+
} catch (err) {
|
|
2206
|
+
log2.warn("theme.css migration failed; keeping JSON files:", err);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
async function loadThemeModel(root = getProjectRoot()) {
|
|
2210
|
+
await configService.load();
|
|
2211
|
+
return enqueue(async () => {
|
|
2212
|
+
await migrateIfNeeded(root);
|
|
2213
|
+
return readModel(root);
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
async function saveColors(themes, root = getProjectRoot()) {
|
|
2217
|
+
await configService.load();
|
|
2218
|
+
return enqueue(async () => {
|
|
2219
|
+
await migrateIfNeeded(root);
|
|
2220
|
+
const current = await readModel(root);
|
|
2221
|
+
await writeModel(root, { themes, variables: current.variables, raw: current.raw });
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
async function saveVariables(variables, root = getProjectRoot()) {
|
|
2225
|
+
await configService.load();
|
|
2226
|
+
return enqueue(async () => {
|
|
2227
|
+
await migrateIfNeeded(root);
|
|
2228
|
+
const current = await readModel(root);
|
|
2229
|
+
await writeModel(root, { themes: current.themes, variables, raw: current.raw });
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
async function regenerateThemeCss(root = getProjectRoot()) {
|
|
2233
|
+
await configService.load();
|
|
2234
|
+
return enqueue(async () => {
|
|
2235
|
+
await migrateIfNeeded(root);
|
|
2236
|
+
if (!await fileExists(themeCssPath(root))) return;
|
|
2237
|
+
await writeModel(root, await readModel(root));
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
|
|
1733
2241
|
// lib/server/services/ColorService.ts
|
|
1734
|
-
var
|
|
2242
|
+
var log3 = createLogger("ColorService");
|
|
1735
2243
|
var ColorService = class extends CachedConfigLoader {
|
|
1736
2244
|
/**
|
|
1737
2245
|
* Load theme configuration from colors.json
|
|
@@ -1740,9 +2248,19 @@ var ColorService = class extends CachedConfigLoader {
|
|
|
1740
2248
|
return this.load();
|
|
1741
2249
|
}
|
|
1742
2250
|
/**
|
|
1743
|
-
* Perform the actual loading from file
|
|
2251
|
+
* Perform the actual loading from file.
|
|
2252
|
+
* Astro projects read colors from `src/styles/theme.css` (the token source of truth);
|
|
2253
|
+
* legacy JSON projects read `colors.json`.
|
|
1744
2254
|
*/
|
|
1745
2255
|
async performLoad() {
|
|
2256
|
+
if (isThemeCssProject()) {
|
|
2257
|
+
try {
|
|
2258
|
+
return (await loadThemeModel()).themes;
|
|
2259
|
+
} catch (error) {
|
|
2260
|
+
log3.warn("Failed to load colors from theme.css:", error);
|
|
2261
|
+
return this.getDefaultThemeConfig();
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
1746
2264
|
try {
|
|
1747
2265
|
const content = await loadJSONFile(projectPaths.colors());
|
|
1748
2266
|
if (content) {
|
|
@@ -1752,7 +2270,7 @@ var ColorService = class extends CachedConfigLoader {
|
|
|
1752
2270
|
}
|
|
1753
2271
|
}
|
|
1754
2272
|
} catch (error) {
|
|
1755
|
-
|
|
2273
|
+
log3.warn("Failed to load colors.json:", error);
|
|
1756
2274
|
}
|
|
1757
2275
|
return this.getDefaultThemeConfig();
|
|
1758
2276
|
}
|
|
@@ -1832,6 +2350,23 @@ var ColorService = class extends CachedConfigLoader {
|
|
|
1832
2350
|
* Distinguishes between missing file, invalid JSON, and valid config
|
|
1833
2351
|
*/
|
|
1834
2352
|
async getConfigWithStatus() {
|
|
2353
|
+
if (isThemeCssProject()) {
|
|
2354
|
+
const filePath2 = themeCssPath();
|
|
2355
|
+
if (!existsSync3(filePath2)) {
|
|
2356
|
+
return { status: "missing", config: this.getMinimalConfig(), filePath: filePath2 };
|
|
2357
|
+
}
|
|
2358
|
+
try {
|
|
2359
|
+
const model = await loadThemeModel();
|
|
2360
|
+
return { status: "valid", config: model.themes, filePath: filePath2 };
|
|
2361
|
+
} catch (error) {
|
|
2362
|
+
return {
|
|
2363
|
+
status: "invalid",
|
|
2364
|
+
config: this.getMinimalConfig(),
|
|
2365
|
+
error: error instanceof Error ? error.message : "Parse error",
|
|
2366
|
+
filePath: filePath2
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
1835
2370
|
const filePath = projectPaths.colors();
|
|
1836
2371
|
try {
|
|
1837
2372
|
const content = await loadJSONFile(filePath);
|
|
@@ -1882,7 +2417,7 @@ var ColorService = class extends CachedConfigLoader {
|
|
|
1882
2417
|
const config = await this.loadThemeConfig();
|
|
1883
2418
|
return Object.entries(config.themes).map(([name, theme]) => ({
|
|
1884
2419
|
name,
|
|
1885
|
-
label: theme.label
|
|
2420
|
+
label: theme.label ?? name
|
|
1886
2421
|
}));
|
|
1887
2422
|
}
|
|
1888
2423
|
/**
|
|
@@ -1916,12 +2451,15 @@ var ColorService = class extends CachedConfigLoader {
|
|
|
1916
2451
|
return this.loadThemeConfig();
|
|
1917
2452
|
}
|
|
1918
2453
|
/**
|
|
1919
|
-
* Save theme configuration
|
|
2454
|
+
* Save theme configuration. Astro projects write the colors half of
|
|
2455
|
+
* `src/styles/theme.css` (preserving variables); legacy JSON projects write `colors.json`.
|
|
1920
2456
|
*/
|
|
1921
2457
|
async saveThemeConfig(config) {
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2458
|
+
if (isThemeCssProject()) {
|
|
2459
|
+
await saveColors(config);
|
|
2460
|
+
} else {
|
|
2461
|
+
await writeFile(projectPaths.colors(), JSON.stringify(config, null, 2));
|
|
2462
|
+
}
|
|
1925
2463
|
this.setCache(config);
|
|
1926
2464
|
}
|
|
1927
2465
|
};
|
|
@@ -1948,7 +2486,8 @@ async function handleColorsStatusRoute() {
|
|
|
1948
2486
|
}
|
|
1949
2487
|
|
|
1950
2488
|
// lib/server/services/VariableService.ts
|
|
1951
|
-
|
|
2489
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
2490
|
+
var log4 = createLogger("VariableService");
|
|
1952
2491
|
var VariableService = class extends CachedConfigLoader {
|
|
1953
2492
|
/**
|
|
1954
2493
|
* Load variables configuration from variables.json
|
|
@@ -1957,6 +2496,14 @@ var VariableService = class extends CachedConfigLoader {
|
|
|
1957
2496
|
return this.load();
|
|
1958
2497
|
}
|
|
1959
2498
|
async performLoad() {
|
|
2499
|
+
if (isThemeCssProject()) {
|
|
2500
|
+
try {
|
|
2501
|
+
return { variables: (await loadThemeModel()).variables };
|
|
2502
|
+
} catch (error) {
|
|
2503
|
+
log4.warn("Failed to load variables from theme.css:", error);
|
|
2504
|
+
return this.getDefaultConfig();
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
1960
2507
|
try {
|
|
1961
2508
|
const content = await loadJSONFile(projectPaths.variables());
|
|
1962
2509
|
if (content) {
|
|
@@ -1966,7 +2513,7 @@ var VariableService = class extends CachedConfigLoader {
|
|
|
1966
2513
|
}
|
|
1967
2514
|
}
|
|
1968
2515
|
} catch (error) {
|
|
1969
|
-
|
|
2516
|
+
log4.warn("Failed to load variables.json:", error);
|
|
1970
2517
|
}
|
|
1971
2518
|
return this.getDefaultConfig();
|
|
1972
2519
|
}
|
|
@@ -1979,7 +2526,7 @@ var VariableService = class extends CachedConfigLoader {
|
|
|
1979
2526
|
migrateConfig(config) {
|
|
1980
2527
|
for (const variable of config.variables) {
|
|
1981
2528
|
if (variable.group === "spacing") {
|
|
1982
|
-
const lower = `${variable.cssVar} ${variable.name}`.toLowerCase();
|
|
2529
|
+
const lower = `${variable.cssVar} ${variable.name ?? ""}`.toLowerCase();
|
|
1983
2530
|
if (lower.includes("margin")) {
|
|
1984
2531
|
variable.group = "margin";
|
|
1985
2532
|
} else if (lower.includes("gap")) {
|
|
@@ -1995,6 +2542,22 @@ var VariableService = class extends CachedConfigLoader {
|
|
|
1995
2542
|
* Get config with status info (for editor UI)
|
|
1996
2543
|
*/
|
|
1997
2544
|
async getConfigWithStatus() {
|
|
2545
|
+
if (isThemeCssProject()) {
|
|
2546
|
+
const filePath2 = themeCssPath();
|
|
2547
|
+
if (!existsSync4(filePath2)) {
|
|
2548
|
+
return { status: "missing", config: this.getDefaultConfig(), filePath: filePath2 };
|
|
2549
|
+
}
|
|
2550
|
+
try {
|
|
2551
|
+
return { status: "valid", config: { variables: (await loadThemeModel()).variables }, filePath: filePath2 };
|
|
2552
|
+
} catch (error) {
|
|
2553
|
+
return {
|
|
2554
|
+
status: "invalid",
|
|
2555
|
+
config: this.getDefaultConfig(),
|
|
2556
|
+
error: error instanceof Error ? error.message : "Parse error",
|
|
2557
|
+
filePath: filePath2
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
1998
2561
|
const filePath = projectPaths.variables();
|
|
1999
2562
|
try {
|
|
2000
2563
|
const content = await loadJSONFile(filePath);
|
|
@@ -2021,12 +2584,15 @@ var VariableService = class extends CachedConfigLoader {
|
|
|
2021
2584
|
}
|
|
2022
2585
|
}
|
|
2023
2586
|
/**
|
|
2024
|
-
* Save variables configuration
|
|
2587
|
+
* Save variables configuration. Astro projects write the variables half of
|
|
2588
|
+
* `src/styles/theme.css` (preserving colors); legacy JSON projects write `variables.json`.
|
|
2025
2589
|
*/
|
|
2026
2590
|
async saveConfig(config) {
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2591
|
+
if (isThemeCssProject()) {
|
|
2592
|
+
await saveVariables(config.variables);
|
|
2593
|
+
} else {
|
|
2594
|
+
await writeFile(projectPaths.variables(), JSON.stringify(config, null, 2));
|
|
2595
|
+
}
|
|
2030
2596
|
this.setCache(config);
|
|
2031
2597
|
}
|
|
2032
2598
|
};
|
|
@@ -2142,7 +2708,7 @@ async function handleVariablesCSSRoute() {
|
|
|
2142
2708
|
}
|
|
2143
2709
|
|
|
2144
2710
|
// lib/server/services/EnumService.ts
|
|
2145
|
-
var
|
|
2711
|
+
var log5 = createLogger("EnumService");
|
|
2146
2712
|
var EnumService = class extends CachedConfigLoader {
|
|
2147
2713
|
/**
|
|
2148
2714
|
* Load enums configuration from enums.json
|
|
@@ -2162,7 +2728,7 @@ var EnumService = class extends CachedConfigLoader {
|
|
|
2162
2728
|
return this.validateEnums(data);
|
|
2163
2729
|
}
|
|
2164
2730
|
} catch (error) {
|
|
2165
|
-
|
|
2731
|
+
log5.warn("Failed to parse enums.json:", error);
|
|
2166
2732
|
}
|
|
2167
2733
|
}
|
|
2168
2734
|
if (configContent) {
|
|
@@ -2384,7 +2950,7 @@ async function handleFunctionsRoute(req, url) {
|
|
|
2384
2950
|
const response = await handler(context);
|
|
2385
2951
|
const headers = new Headers(response.headers);
|
|
2386
2952
|
headers.set("Access-Control-Allow-Origin", "*");
|
|
2387
|
-
headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
2953
|
+
headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
|
2388
2954
|
headers.set("Access-Control-Allow-Headers", "Content-Type");
|
|
2389
2955
|
return new Response(response.body, {
|
|
2390
2956
|
status: response.status,
|
|
@@ -2410,7 +2976,7 @@ function handleFunctionsPreflight(req, url) {
|
|
|
2410
2976
|
status: 204,
|
|
2411
2977
|
headers: {
|
|
2412
2978
|
"Access-Control-Allow-Origin": "*",
|
|
2413
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
2979
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
2414
2980
|
"Access-Control-Allow-Headers": "Content-Type",
|
|
2415
2981
|
"Access-Control-Max-Age": "86400"
|
|
2416
2982
|
}
|
|
@@ -4713,7 +5279,23 @@ var formHandlerScript = `
|
|
|
4713
5279
|
// Find all forms with fetch handler
|
|
4714
5280
|
const forms = document.querySelectorAll('form[data-submit-handler="fetch"]');
|
|
4715
5281
|
|
|
5282
|
+
// Spam protection (server-enforced at /api/send-email): inject a hidden honeypot
|
|
5283
|
+
// field bots fill but humans never see, and stamp a clock-skew-free dwell time on
|
|
5284
|
+
// submit. Fields named with a leading "_" are control/meta and are not emailed.
|
|
5285
|
+
const loadedAt = Date.now();
|
|
5286
|
+
|
|
4716
5287
|
forms.forEach(function(form) {
|
|
5288
|
+
if (!form.querySelector('input[name="_honey"]')) {
|
|
5289
|
+
const hp = document.createElement('input');
|
|
5290
|
+
hp.type = 'text';
|
|
5291
|
+
hp.name = '_honey';
|
|
5292
|
+
hp.tabIndex = -1;
|
|
5293
|
+
hp.setAttribute('autocomplete', 'off');
|
|
5294
|
+
hp.setAttribute('aria-hidden', 'true');
|
|
5295
|
+
hp.style.cssText = 'position:absolute;left:-9999px;width:1px;height:1px;opacity:0';
|
|
5296
|
+
form.appendChild(hp);
|
|
5297
|
+
}
|
|
5298
|
+
|
|
4717
5299
|
form.addEventListener('submit', async function(e) {
|
|
4718
5300
|
e.preventDefault();
|
|
4719
5301
|
|
|
@@ -4748,6 +5330,7 @@ var formHandlerScript = `
|
|
|
4748
5330
|
|
|
4749
5331
|
try {
|
|
4750
5332
|
const formData = new FormData(form);
|
|
5333
|
+
formData.append('_elapsed', String(Date.now() - loadedAt));
|
|
4751
5334
|
|
|
4752
5335
|
const response = await fetch(action, {
|
|
4753
5336
|
method: method.toUpperCase(),
|
|
@@ -8440,8 +9023,8 @@ ${plainTextErrors}`;
|
|
|
8440
9023
|
}
|
|
8441
9024
|
|
|
8442
9025
|
// lib/server/services/pageService.ts
|
|
8443
|
-
import { existsSync as
|
|
8444
|
-
import { join as
|
|
9026
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, mkdirSync, rmdirSync } from "node:fs";
|
|
9027
|
+
import { join as join4 } from "node:path";
|
|
8445
9028
|
|
|
8446
9029
|
// lib/server/utils/jsonLineMapper.ts
|
|
8447
9030
|
function buildLineMap(jsonText) {
|
|
@@ -8853,7 +9436,7 @@ var PageService = class {
|
|
|
8853
9436
|
*/
|
|
8854
9437
|
getAllFolders() {
|
|
8855
9438
|
const pagesDir = this.pagesBaseDir();
|
|
8856
|
-
if (!
|
|
9439
|
+
if (!existsSync5(pagesDir)) {
|
|
8857
9440
|
return [];
|
|
8858
9441
|
}
|
|
8859
9442
|
const collectFolders = (dir, prefix) => {
|
|
@@ -8864,7 +9447,7 @@ var PageService = class {
|
|
|
8864
9447
|
if (!entry.isDirectory()) continue;
|
|
8865
9448
|
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
8866
9449
|
result.push(relativePath);
|
|
8867
|
-
result.push(...collectFolders(
|
|
9450
|
+
result.push(...collectFolders(join4(dir, entry.name), relativePath));
|
|
8868
9451
|
}
|
|
8869
9452
|
return result;
|
|
8870
9453
|
} catch {
|
|
@@ -8895,8 +9478,8 @@ var PageService = class {
|
|
|
8895
9478
|
}
|
|
8896
9479
|
}
|
|
8897
9480
|
const pagesDir = this.pagesBaseDir();
|
|
8898
|
-
const folderPath =
|
|
8899
|
-
if (
|
|
9481
|
+
const folderPath = join4(pagesDir, trimmed);
|
|
9482
|
+
if (existsSync5(folderPath)) {
|
|
8900
9483
|
throw new Error("Folder already exists");
|
|
8901
9484
|
}
|
|
8902
9485
|
mkdirSync(folderPath, { recursive: true });
|
|
@@ -8917,18 +9500,18 @@ var PageService = class {
|
|
|
8917
9500
|
const pageName = pagePath === "/" ? "index" : pagePath.substring(1);
|
|
8918
9501
|
const slashIndex = pageName.lastIndexOf("/");
|
|
8919
9502
|
const baseName = slashIndex >= 0 ? pageName.substring(slashIndex + 1) : pageName;
|
|
8920
|
-
const sourceFile =
|
|
8921
|
-
const targetFile = newFolder ?
|
|
9503
|
+
const sourceFile = join4(pagesDir, `${pageName}${ext}`);
|
|
9504
|
+
const targetFile = newFolder ? join4(pagesDir, newFolder, `${baseName}${ext}`) : join4(pagesDir, `${baseName}${ext}`);
|
|
8922
9505
|
if (!isPathWithinRoot(sourceFile, pagesDir) || !isPathWithinRoot(targetFile, pagesDir)) {
|
|
8923
9506
|
throw new Error(`movePage path escapes pages dir: ${pagePath} -> ${newFolder}`);
|
|
8924
9507
|
}
|
|
8925
9508
|
if (newFolder) {
|
|
8926
|
-
const targetDir =
|
|
8927
|
-
if (!
|
|
9509
|
+
const targetDir = join4(pagesDir, newFolder);
|
|
9510
|
+
if (!existsSync5(targetDir)) {
|
|
8928
9511
|
mkdirSync(targetDir, { recursive: true });
|
|
8929
9512
|
}
|
|
8930
9513
|
}
|
|
8931
|
-
if (!
|
|
9514
|
+
if (!existsSync5(sourceFile)) {
|
|
8932
9515
|
throw new Error(`Page not found: ${pagePath}`);
|
|
8933
9516
|
}
|
|
8934
9517
|
await rename2(sourceFile, targetFile);
|
|
@@ -8940,7 +9523,7 @@ var PageService = class {
|
|
|
8940
9523
|
const lineMap = buildLineMap(content);
|
|
8941
9524
|
this.pageCache.set(newPath, content, lineMap);
|
|
8942
9525
|
}
|
|
8943
|
-
const sourceDir = slashIndex >= 0 ?
|
|
9526
|
+
const sourceDir = slashIndex >= 0 ? join4(pagesDir, pageName.substring(0, slashIndex)) : null;
|
|
8944
9527
|
if (sourceDir && sourceDir !== pagesDir) {
|
|
8945
9528
|
try {
|
|
8946
9529
|
const remaining = readdirSync2(sourceDir);
|
|
@@ -8989,12 +9572,12 @@ var PageService = class {
|
|
|
8989
9572
|
const { rename: rename2 } = await import("node:fs/promises");
|
|
8990
9573
|
const pagesDir = this.pagesBaseDir();
|
|
8991
9574
|
const ext = this.pageExt();
|
|
8992
|
-
const sourceFile =
|
|
8993
|
-
const targetFile =
|
|
8994
|
-
if (!
|
|
9575
|
+
const sourceFile = join4(pagesDir, `${oldName}${ext}`);
|
|
9576
|
+
const targetFile = join4(pagesDir, `${newName}${ext}`);
|
|
9577
|
+
if (!existsSync5(sourceFile)) {
|
|
8995
9578
|
throw new Error(`Page not found: ${oldPath}`);
|
|
8996
9579
|
}
|
|
8997
|
-
if (
|
|
9580
|
+
if (existsSync5(targetFile)) {
|
|
8998
9581
|
throw new Error(`Page already exists: ${newPath}`);
|
|
8999
9582
|
}
|
|
9000
9583
|
await rename2(sourceFile, targetFile);
|
|
@@ -9044,8 +9627,8 @@ var PageService = class {
|
|
|
9044
9627
|
};
|
|
9045
9628
|
|
|
9046
9629
|
// lib/server/services/componentService.ts
|
|
9047
|
-
import { join as
|
|
9048
|
-
import { existsSync as
|
|
9630
|
+
import { join as join5 } from "node:path";
|
|
9631
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync as readdirSync3, rmdirSync as rmdirSync2 } from "node:fs";
|
|
9049
9632
|
var ComponentService = class {
|
|
9050
9633
|
components = /* @__PURE__ */ new Map();
|
|
9051
9634
|
componentCategories = /* @__PURE__ */ new Map();
|
|
@@ -9277,7 +9860,7 @@ var ComponentService = class {
|
|
|
9277
9860
|
const componentsDir = this.componentsBaseDir();
|
|
9278
9861
|
if (!isSafePathSegment(name)) throw new Error(`unsafe component name: ${name}`);
|
|
9279
9862
|
const cat = this.resolveComponentCategory(name, category);
|
|
9280
|
-
const dir = cat ?
|
|
9863
|
+
const dir = cat ? join5(componentsDir, cat) : componentsDir;
|
|
9281
9864
|
if (!isPathWithinRoot(dir, componentsDir)) throw new Error(`component category escapes components dir: ${cat}`);
|
|
9282
9865
|
return dir;
|
|
9283
9866
|
}
|
|
@@ -9308,10 +9891,10 @@ var ComponentService = class {
|
|
|
9308
9891
|
*/
|
|
9309
9892
|
findComponentCategoryOnDisk(name) {
|
|
9310
9893
|
const componentsDir = this.componentsBaseDir();
|
|
9311
|
-
if (!
|
|
9894
|
+
if (!existsSync6(componentsDir)) return void 0;
|
|
9312
9895
|
const ext = this.writer ? null : ".json";
|
|
9313
9896
|
const existsAt = (dir) => {
|
|
9314
|
-
if (ext) return
|
|
9897
|
+
if (ext) return existsSync6(join5(dir, `${name}${ext}`));
|
|
9315
9898
|
try {
|
|
9316
9899
|
return readdirSync3(dir, { withFileTypes: true }).some(
|
|
9317
9900
|
(e) => e.isFile() && (e.name === `${name}.json` || e.name === `${name}.astro`)
|
|
@@ -9323,7 +9906,7 @@ var ComponentService = class {
|
|
|
9323
9906
|
if (existsAt(componentsDir)) return void 0;
|
|
9324
9907
|
try {
|
|
9325
9908
|
for (const entry of readdirSync3(componentsDir, { withFileTypes: true })) {
|
|
9326
|
-
if (entry.isDirectory() && existsAt(
|
|
9909
|
+
if (entry.isDirectory() && existsAt(join5(componentsDir, entry.name))) {
|
|
9327
9910
|
return entry.name;
|
|
9328
9911
|
}
|
|
9329
9912
|
}
|
|
@@ -9350,7 +9933,7 @@ var ComponentService = class {
|
|
|
9350
9933
|
*/
|
|
9351
9934
|
async getComponentJavaScript(name) {
|
|
9352
9935
|
const componentDir = this.getComponentDir(name);
|
|
9353
|
-
const jsFilePath =
|
|
9936
|
+
const jsFilePath = join5(componentDir, `${name}.js`);
|
|
9354
9937
|
try {
|
|
9355
9938
|
if (await fileExists(jsFilePath)) {
|
|
9356
9939
|
return await readTextFile(jsFilePath);
|
|
@@ -9398,15 +9981,15 @@ var ComponentService = class {
|
|
|
9398
9981
|
const dataWithJS = javascript !== void 0 ? { ...dataWithoutJS, component: { ...dataWithoutJS.component, javascript } } : dataWithoutJS;
|
|
9399
9982
|
const targetCategory = this.resolveComponentCategory(name, category);
|
|
9400
9983
|
const componentsDir = this.componentsBaseDir();
|
|
9401
|
-
const componentDir = targetCategory ?
|
|
9984
|
+
const componentDir = targetCategory ? join5(componentsDir, targetCategory) : componentsDir;
|
|
9402
9985
|
if (this.writer) {
|
|
9403
9986
|
await this.writer.writeComponent(componentDir, name, dataWithJS);
|
|
9404
9987
|
} else {
|
|
9405
9988
|
const writeFile3 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("node:fs/promises")).writeFile;
|
|
9406
|
-
if (targetCategory && !
|
|
9989
|
+
if (targetCategory && !existsSync6(componentDir)) {
|
|
9407
9990
|
mkdirSync2(componentDir, { recursive: true });
|
|
9408
9991
|
}
|
|
9409
|
-
const filePath =
|
|
9992
|
+
const filePath = join5(componentDir, `${name}.json`);
|
|
9410
9993
|
await writeFile3(filePath, JSON.stringify(dataWithoutJS, null, 2), "utf-8");
|
|
9411
9994
|
}
|
|
9412
9995
|
this.components.set(name, dataWithJS);
|
|
@@ -9439,9 +10022,9 @@ var ComponentService = class {
|
|
|
9439
10022
|
return;
|
|
9440
10023
|
}
|
|
9441
10024
|
const writeFile3 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("node:fs/promises")).writeFile;
|
|
9442
|
-
const jsFilePath =
|
|
10025
|
+
const jsFilePath = join5(componentDir, `${name}.js`);
|
|
9443
10026
|
await writeFile3(jsFilePath, javascript || "", "utf-8");
|
|
9444
|
-
const componentPath =
|
|
10027
|
+
const componentPath = join5(componentDir, `${name}.json`);
|
|
9445
10028
|
const componentData = await loadJSONFile(componentPath);
|
|
9446
10029
|
if (componentData) {
|
|
9447
10030
|
const parsed = parseJSON(componentData);
|
|
@@ -9485,9 +10068,9 @@ var ComponentService = class {
|
|
|
9485
10068
|
return;
|
|
9486
10069
|
}
|
|
9487
10070
|
const writeFile3 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("node:fs/promises")).writeFile;
|
|
9488
|
-
const cssFilePath =
|
|
10071
|
+
const cssFilePath = join5(componentDir, `${name}.css`);
|
|
9489
10072
|
await writeFile3(cssFilePath, css || "", "utf-8");
|
|
9490
|
-
const componentPath =
|
|
10073
|
+
const componentPath = join5(componentDir, `${name}.json`);
|
|
9491
10074
|
const componentData = await loadJSONFile(componentPath);
|
|
9492
10075
|
if (componentData) {
|
|
9493
10076
|
const parsed = parseJSON(componentData);
|
|
@@ -9520,7 +10103,7 @@ var ComponentService = class {
|
|
|
9520
10103
|
*/
|
|
9521
10104
|
getAllFolders() {
|
|
9522
10105
|
const componentsDir = this.componentsBaseDir();
|
|
9523
|
-
if (!
|
|
10106
|
+
if (!existsSync6(componentsDir)) {
|
|
9524
10107
|
return [];
|
|
9525
10108
|
}
|
|
9526
10109
|
try {
|
|
@@ -9555,8 +10138,8 @@ var ComponentService = class {
|
|
|
9555
10138
|
throw new Error("Folder name can only contain lowercase letters, numbers, dashes, and underscores");
|
|
9556
10139
|
}
|
|
9557
10140
|
const componentsDir = this.componentsBaseDir();
|
|
9558
|
-
const folderPath =
|
|
9559
|
-
if (
|
|
10141
|
+
const folderPath = join5(componentsDir, folderName);
|
|
10142
|
+
if (existsSync6(folderPath)) {
|
|
9560
10143
|
throw new Error("Folder already exists");
|
|
9561
10144
|
}
|
|
9562
10145
|
mkdirSync2(folderPath, { recursive: true });
|
|
@@ -9589,20 +10172,20 @@ var ComponentService = class {
|
|
|
9589
10172
|
return;
|
|
9590
10173
|
}
|
|
9591
10174
|
const componentsDir = this.componentsBaseDir();
|
|
9592
|
-
const sourceDir = currentCategory ?
|
|
9593
|
-
const targetDir = targetCategory ?
|
|
10175
|
+
const sourceDir = currentCategory ? join5(componentsDir, currentCategory) : componentsDir;
|
|
10176
|
+
const targetDir = targetCategory ? join5(componentsDir, targetCategory) : componentsDir;
|
|
9594
10177
|
if (this.writer) {
|
|
9595
10178
|
await this.writer.moveComponent(sourceDir, targetDir, name);
|
|
9596
10179
|
} else {
|
|
9597
|
-
if (targetCategory && !
|
|
10180
|
+
if (targetCategory && !existsSync6(targetDir)) {
|
|
9598
10181
|
mkdirSync2(targetDir, { recursive: true });
|
|
9599
10182
|
}
|
|
9600
10183
|
const extensions = [".json", ".js", ".css"];
|
|
9601
10184
|
const { rename: rename2 } = await import("node:fs/promises");
|
|
9602
10185
|
for (const ext of extensions) {
|
|
9603
|
-
const sourcePath =
|
|
9604
|
-
const targetPath =
|
|
9605
|
-
if (
|
|
10186
|
+
const sourcePath = join5(sourceDir, `${name}${ext}`);
|
|
10187
|
+
const targetPath = join5(targetDir, `${name}${ext}`);
|
|
10188
|
+
if (existsSync6(sourcePath)) {
|
|
9606
10189
|
await rename2(sourcePath, targetPath);
|
|
9607
10190
|
}
|
|
9608
10191
|
}
|
|
@@ -9657,7 +10240,7 @@ var ComponentService = class {
|
|
|
9657
10240
|
}
|
|
9658
10241
|
const category = this.componentCategories.get(oldName);
|
|
9659
10242
|
const componentsDir = this.componentsBaseDir();
|
|
9660
|
-
const dir = category ?
|
|
10243
|
+
const dir = category ? join5(componentsDir, category) : componentsDir;
|
|
9661
10244
|
if (this.writer) {
|
|
9662
10245
|
if (await this.writer.componentExists(dir, trimmedNew)) {
|
|
9663
10246
|
throw new Error(`Component "${trimmedNew}" already exists in components/${category ?? ""}`);
|
|
@@ -9665,15 +10248,15 @@ var ComponentService = class {
|
|
|
9665
10248
|
await this.writer.moveComponent(dir, dir, oldName, trimmedNew);
|
|
9666
10249
|
} else {
|
|
9667
10250
|
for (const ext of [".json", ".js", ".css"]) {
|
|
9668
|
-
if (
|
|
10251
|
+
if (existsSync6(join5(dir, `${trimmedNew}${ext}`))) {
|
|
9669
10252
|
throw new Error(`File "${trimmedNew}${ext}" already exists in components/${category ?? ""}`);
|
|
9670
10253
|
}
|
|
9671
10254
|
}
|
|
9672
10255
|
const { rename: rename2 } = await import("node:fs/promises");
|
|
9673
10256
|
for (const ext of [".json", ".js", ".css"]) {
|
|
9674
|
-
const src =
|
|
9675
|
-
const dst =
|
|
9676
|
-
if (
|
|
10257
|
+
const src = join5(dir, `${oldName}${ext}`);
|
|
10258
|
+
const dst = join5(dir, `${trimmedNew}${ext}`);
|
|
10259
|
+
if (existsSync6(src)) {
|
|
9677
10260
|
await rename2(src, dst);
|
|
9678
10261
|
}
|
|
9679
10262
|
}
|
|
@@ -9691,7 +10274,7 @@ var ComponentService = class {
|
|
|
9691
10274
|
if (!structure) continue;
|
|
9692
10275
|
if (rewriteComponentRefs(structure, oldName, trimmedNew)) {
|
|
9693
10276
|
const otherCategory = this.componentCategories.get(name);
|
|
9694
|
-
const otherDir = otherCategory ?
|
|
10277
|
+
const otherDir = otherCategory ? join5(componentsDir, otherCategory) : componentsDir;
|
|
9695
10278
|
const persisted = JSON.parse(JSON.stringify(otherDef));
|
|
9696
10279
|
if (persisted?.component?.javascript !== void 0) {
|
|
9697
10280
|
delete persisted.component.javascript;
|
|
@@ -9699,7 +10282,7 @@ var ComponentService = class {
|
|
|
9699
10282
|
if (this.writer) {
|
|
9700
10283
|
await this.writer.writeComponent(otherDir, name, persisted);
|
|
9701
10284
|
} else {
|
|
9702
|
-
await writeFile3(
|
|
10285
|
+
await writeFile3(join5(otherDir, `${name}.json`), JSON.stringify(persisted, null, 2), "utf-8");
|
|
9703
10286
|
}
|
|
9704
10287
|
componentRefs++;
|
|
9705
10288
|
}
|
|
@@ -10239,9 +10822,9 @@ var CMSService = class {
|
|
|
10239
10822
|
};
|
|
10240
10823
|
|
|
10241
10824
|
// lib/server/providers/fileSystemCMSProvider.ts
|
|
10242
|
-
import { existsSync as
|
|
10243
|
-
import { join as
|
|
10244
|
-
var
|
|
10825
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, mkdirSync as mkdirSync3 } from "node:fs";
|
|
10826
|
+
import { join as join6 } from "node:path";
|
|
10827
|
+
var log6 = createLogger("FileSystemCMSProvider");
|
|
10245
10828
|
var DRAFT_FILE_SUFFIX = `${CMS_DRAFT_SUFFIX}.json`;
|
|
10246
10829
|
async function loadJSONFile2(filePath) {
|
|
10247
10830
|
try {
|
|
@@ -10250,7 +10833,7 @@ async function loadJSONFile2(filePath) {
|
|
|
10250
10833
|
}
|
|
10251
10834
|
return null;
|
|
10252
10835
|
} catch (error) {
|
|
10253
|
-
|
|
10836
|
+
log6.warn(`Failed to parse JSON at ${filePath}:`, error instanceof Error ? error.message : error);
|
|
10254
10837
|
return null;
|
|
10255
10838
|
}
|
|
10256
10839
|
}
|
|
@@ -10312,14 +10895,14 @@ var FileSystemCMSProvider = class {
|
|
|
10312
10895
|
return this.schemaCache;
|
|
10313
10896
|
}
|
|
10314
10897
|
const schemas = /* @__PURE__ */ new Map();
|
|
10315
|
-
if (!
|
|
10898
|
+
if (!existsSync7(this.templatesDir)) {
|
|
10316
10899
|
return schemas;
|
|
10317
10900
|
}
|
|
10318
10901
|
const files = readdirSync4(this.templatesDir);
|
|
10319
10902
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
10320
10903
|
const results = await Promise.all(
|
|
10321
10904
|
jsonFiles.map(async (file) => {
|
|
10322
|
-
const filePath =
|
|
10905
|
+
const filePath = join6(this.templatesDir, file);
|
|
10323
10906
|
const content = await loadJSONFile2(filePath);
|
|
10324
10907
|
return { filePath, content };
|
|
10325
10908
|
})
|
|
@@ -10344,15 +10927,15 @@ var FileSystemCMSProvider = class {
|
|
|
10344
10927
|
*/
|
|
10345
10928
|
async getItems(collection) {
|
|
10346
10929
|
this.validateCollection(collection);
|
|
10347
|
-
const collectionDir =
|
|
10348
|
-
if (!
|
|
10930
|
+
const collectionDir = join6(this.cmsDir, collection);
|
|
10931
|
+
if (!existsSync7(collectionDir)) {
|
|
10349
10932
|
return [];
|
|
10350
10933
|
}
|
|
10351
10934
|
const files = readdirSync4(collectionDir);
|
|
10352
10935
|
const jsonFiles = files.filter((f) => f.endsWith(".json") && !f.endsWith(DRAFT_FILE_SUFFIX));
|
|
10353
10936
|
const results = await Promise.all(
|
|
10354
10937
|
jsonFiles.map(async (file) => {
|
|
10355
|
-
const filePath =
|
|
10938
|
+
const filePath = join6(collectionDir, file);
|
|
10356
10939
|
const content = await loadJSONFile2(filePath);
|
|
10357
10940
|
return { file, filePath, content };
|
|
10358
10941
|
})
|
|
@@ -10366,7 +10949,7 @@ var FileSystemCMSProvider = class {
|
|
|
10366
10949
|
if (validation.valid) {
|
|
10367
10950
|
items.push(validation.data);
|
|
10368
10951
|
} else {
|
|
10369
|
-
|
|
10952
|
+
log6.warn(`Invalid CMS item at ${filePath}:`, validation.errors);
|
|
10370
10953
|
}
|
|
10371
10954
|
}
|
|
10372
10955
|
}
|
|
@@ -10385,7 +10968,7 @@ var FileSystemCMSProvider = class {
|
|
|
10385
10968
|
async getItemByFilename(collection, filename) {
|
|
10386
10969
|
this.validateCollection(collection);
|
|
10387
10970
|
this.validateFilename(filename);
|
|
10388
|
-
const filePath =
|
|
10971
|
+
const filePath = join6(this.cmsDir, collection, `${filename}.json`);
|
|
10389
10972
|
const content = await loadJSONFile2(filePath);
|
|
10390
10973
|
if (!content || typeof content !== "object") {
|
|
10391
10974
|
return null;
|
|
@@ -10395,7 +10978,7 @@ var FileSystemCMSProvider = class {
|
|
|
10395
10978
|
if (validation.valid) {
|
|
10396
10979
|
return validation.data;
|
|
10397
10980
|
}
|
|
10398
|
-
|
|
10981
|
+
log6.warn(`Invalid CMS item at ${filePath}:`, validation.errors);
|
|
10399
10982
|
return null;
|
|
10400
10983
|
}
|
|
10401
10984
|
/**
|
|
@@ -10437,12 +11020,12 @@ var FileSystemCMSProvider = class {
|
|
|
10437
11020
|
throw new Error("Cannot derive filename: item is missing _id, _filename, and a usable slug-field value.");
|
|
10438
11021
|
}
|
|
10439
11022
|
this.validateFilename(filename);
|
|
10440
|
-
const collectionDir =
|
|
10441
|
-
if (!
|
|
11023
|
+
const collectionDir = join6(this.cmsDir, collection);
|
|
11024
|
+
if (!existsSync7(collectionDir)) {
|
|
10442
11025
|
mkdirSync3(collectionDir, { recursive: true });
|
|
10443
11026
|
}
|
|
10444
11027
|
const itemData = stripTransient(item);
|
|
10445
|
-
const filePath =
|
|
11028
|
+
const filePath = join6(collectionDir, `${filename}.json`);
|
|
10446
11029
|
await writeFile3(filePath, JSON.stringify(itemData, null, 2), "utf-8");
|
|
10447
11030
|
}
|
|
10448
11031
|
/**
|
|
@@ -10452,18 +11035,18 @@ var FileSystemCMSProvider = class {
|
|
|
10452
11035
|
this.validateCollection(collection);
|
|
10453
11036
|
this.validateFilename(filename);
|
|
10454
11037
|
const { unlink } = await import("node:fs/promises");
|
|
10455
|
-
const publishedPath =
|
|
10456
|
-
if (
|
|
11038
|
+
const publishedPath = join6(this.cmsDir, collection, `${filename}.json`);
|
|
11039
|
+
if (existsSync7(publishedPath)) {
|
|
10457
11040
|
await unlink(publishedPath);
|
|
10458
11041
|
}
|
|
10459
11042
|
const draftPath = this.draftPath(collection, filename);
|
|
10460
|
-
if (
|
|
11043
|
+
if (existsSync7(draftPath)) {
|
|
10461
11044
|
await unlink(draftPath);
|
|
10462
11045
|
}
|
|
10463
11046
|
}
|
|
10464
11047
|
// ---- Draft helpers ----------------------------------------------------
|
|
10465
11048
|
draftPath(collection, filename) {
|
|
10466
|
-
return
|
|
11049
|
+
return join6(this.cmsDir, collection, `${filename}${DRAFT_FILE_SUFFIX}`);
|
|
10467
11050
|
}
|
|
10468
11051
|
/**
|
|
10469
11052
|
* Get the draft version of an item, or null if no draft file exists.
|
|
@@ -10490,12 +11073,12 @@ var FileSystemCMSProvider = class {
|
|
|
10490
11073
|
*/
|
|
10491
11074
|
async getAllDrafts(collection) {
|
|
10492
11075
|
this.validateCollection(collection);
|
|
10493
|
-
const collectionDir =
|
|
10494
|
-
if (!
|
|
11076
|
+
const collectionDir = join6(this.cmsDir, collection);
|
|
11077
|
+
if (!existsSync7(collectionDir)) return [];
|
|
10495
11078
|
const files = readdirSync4(collectionDir).filter((f) => f.endsWith(DRAFT_FILE_SUFFIX));
|
|
10496
11079
|
const results = await Promise.all(
|
|
10497
11080
|
files.map(async (file) => {
|
|
10498
|
-
const filePath =
|
|
11081
|
+
const filePath = join6(collectionDir, file);
|
|
10499
11082
|
const content = await loadJSONFile2(filePath);
|
|
10500
11083
|
return { file, content };
|
|
10501
11084
|
})
|
|
@@ -10517,7 +11100,7 @@ var FileSystemCMSProvider = class {
|
|
|
10517
11100
|
async hasDraft(collection, filename) {
|
|
10518
11101
|
this.validateCollection(collection);
|
|
10519
11102
|
this.validateFilename(filename);
|
|
10520
|
-
return
|
|
11103
|
+
return existsSync7(this.draftPath(collection, filename));
|
|
10521
11104
|
}
|
|
10522
11105
|
/**
|
|
10523
11106
|
* Save the draft version of an item. Loose validation — drafts may have
|
|
@@ -10551,8 +11134,8 @@ var FileSystemCMSProvider = class {
|
|
|
10551
11134
|
throw new Error("Cannot derive draft filename: item is missing _id, _filename, and a usable slug-field value.");
|
|
10552
11135
|
}
|
|
10553
11136
|
this.validateFilename(filename);
|
|
10554
|
-
const collectionDir =
|
|
10555
|
-
if (!
|
|
11137
|
+
const collectionDir = join6(this.cmsDir, collection);
|
|
11138
|
+
if (!existsSync7(collectionDir)) {
|
|
10556
11139
|
mkdirSync3(collectionDir, { recursive: true });
|
|
10557
11140
|
}
|
|
10558
11141
|
const itemData = stripTransient(item);
|
|
@@ -10572,7 +11155,7 @@ var FileSystemCMSProvider = class {
|
|
|
10572
11155
|
this.validateFilename(filename);
|
|
10573
11156
|
const { unlink } = await import("node:fs/promises");
|
|
10574
11157
|
const filePath = this.draftPath(collection, filename);
|
|
10575
|
-
if (
|
|
11158
|
+
if (existsSync7(filePath)) {
|
|
10576
11159
|
await unlink(filePath);
|
|
10577
11160
|
}
|
|
10578
11161
|
}
|
|
@@ -10600,14 +11183,14 @@ var FileSystemCMSProvider = class {
|
|
|
10600
11183
|
const messages = validation.errors.map((e) => `${e.path}: ${e.message}`).join(", ");
|
|
10601
11184
|
throw new Error(`Cannot publish invalid draft: ${messages}`);
|
|
10602
11185
|
}
|
|
10603
|
-
const collectionDir =
|
|
10604
|
-
if (!
|
|
11186
|
+
const collectionDir = join6(this.cmsDir, collection);
|
|
11187
|
+
if (!existsSync7(collectionDir)) {
|
|
10605
11188
|
mkdirSync3(collectionDir, { recursive: true });
|
|
10606
11189
|
}
|
|
10607
11190
|
const itemData = stripTransient(validation.data);
|
|
10608
|
-
const publishedPath =
|
|
11191
|
+
const publishedPath = join6(collectionDir, `${filename}.json`);
|
|
10609
11192
|
await writeFile3(publishedPath, JSON.stringify(itemData, null, 2), "utf-8");
|
|
10610
|
-
if (
|
|
11193
|
+
if (existsSync7(draftFilePath)) {
|
|
10611
11194
|
await unlink(draftFilePath);
|
|
10612
11195
|
}
|
|
10613
11196
|
return normalizeItem(itemData, filename);
|
|
@@ -10628,18 +11211,18 @@ var FileSystemCMSProvider = class {
|
|
|
10628
11211
|
);
|
|
10629
11212
|
}
|
|
10630
11213
|
const { writeFile: writeFile3, mkdir } = await import("node:fs/promises");
|
|
10631
|
-
if (!
|
|
11214
|
+
if (!existsSync7(this.templatesDir)) {
|
|
10632
11215
|
await mkdir(this.templatesDir, { recursive: true });
|
|
10633
11216
|
}
|
|
10634
|
-
const pageFilePath =
|
|
10635
|
-
if (
|
|
11217
|
+
const pageFilePath = join6(this.templatesDir, `${collectionId}.json`);
|
|
11218
|
+
if (existsSync7(pageFilePath)) {
|
|
10636
11219
|
throw new Error(`Page file already exists: templates/${collectionId}.json`);
|
|
10637
11220
|
}
|
|
10638
11221
|
await writeFile3(pageFilePath, JSON.stringify(pageData, null, 2), "utf-8");
|
|
10639
11222
|
const schemaSource = pageData?.meta?.cms?.source;
|
|
10640
11223
|
if (schemaSource !== "sanity") {
|
|
10641
|
-
const collectionDir =
|
|
10642
|
-
if (!
|
|
11224
|
+
const collectionDir = join6(this.cmsDir, collectionId);
|
|
11225
|
+
if (!existsSync7(collectionDir)) {
|
|
10643
11226
|
await mkdir(collectionDir, { recursive: true });
|
|
10644
11227
|
}
|
|
10645
11228
|
}
|
|
@@ -10656,8 +11239,8 @@ var FileSystemCMSProvider = class {
|
|
|
10656
11239
|
);
|
|
10657
11240
|
}
|
|
10658
11241
|
const { readFile: readFile2, writeFile: writeFile3 } = await import("node:fs/promises");
|
|
10659
|
-
const pageFilePath =
|
|
10660
|
-
if (!
|
|
11242
|
+
const pageFilePath = join6(this.templatesDir, `${collectionId}.json`);
|
|
11243
|
+
if (!existsSync7(pageFilePath)) {
|
|
10661
11244
|
throw new Error(`Collection not found: ${collectionId}`);
|
|
10662
11245
|
}
|
|
10663
11246
|
const content = await readFile2(pageFilePath, "utf-8");
|
|
@@ -10783,8 +11366,8 @@ var DraftPageStore = class {
|
|
|
10783
11366
|
};
|
|
10784
11367
|
|
|
10785
11368
|
// lib/server/fileWatcher.ts
|
|
10786
|
-
import { watch, existsSync as
|
|
10787
|
-
import { basename as basename2, dirname as dirname2, join as
|
|
11369
|
+
import { watch, existsSync as existsSync8, statSync, readdirSync as readdirSync5 } from "node:fs";
|
|
11370
|
+
import { basename as basename2, dirname as dirname2, join as join7, relative } from "node:path";
|
|
10788
11371
|
function watchRecursive(root, listener) {
|
|
10789
11372
|
const nested = [];
|
|
10790
11373
|
const watchedDirs = /* @__PURE__ */ new Set();
|
|
@@ -10800,7 +11383,7 @@ function watchRecursive(root, listener) {
|
|
|
10800
11383
|
attachDir(abs);
|
|
10801
11384
|
try {
|
|
10802
11385
|
for (const e of readdirSync5(abs, { withFileTypes: true })) {
|
|
10803
|
-
const child =
|
|
11386
|
+
const child = join7(abs, e.name);
|
|
10804
11387
|
if (e.isDirectory()) coverNewDir(child);
|
|
10805
11388
|
else listener("rename", relative(root, child));
|
|
10806
11389
|
}
|
|
@@ -10814,7 +11397,7 @@ function watchRecursive(root, listener) {
|
|
|
10814
11397
|
try {
|
|
10815
11398
|
w = watch(absDir, { recursive: true }, (event, filename) => {
|
|
10816
11399
|
if (!filename) return;
|
|
10817
|
-
const abs =
|
|
11400
|
+
const abs = join7(absDir, filename);
|
|
10818
11401
|
listener(event, relative(root, abs));
|
|
10819
11402
|
coverNewDir(abs);
|
|
10820
11403
|
});
|
|
@@ -10840,19 +11423,19 @@ function watchRecursive(root, listener) {
|
|
|
10840
11423
|
return handle;
|
|
10841
11424
|
}
|
|
10842
11425
|
function attachWhenDirExists(dirPath, attach, setWatcher) {
|
|
10843
|
-
if (
|
|
11426
|
+
if (existsSync8(dirPath)) {
|
|
10844
11427
|
setWatcher(attach());
|
|
10845
11428
|
return;
|
|
10846
11429
|
}
|
|
10847
11430
|
const parentDir = dirname2(dirPath);
|
|
10848
11431
|
const targetName = basename2(dirPath);
|
|
10849
|
-
if (!
|
|
11432
|
+
if (!existsSync8(parentDir)) {
|
|
10850
11433
|
return;
|
|
10851
11434
|
}
|
|
10852
11435
|
let parentWatcher = null;
|
|
10853
11436
|
parentWatcher = watch(parentDir, (_event, filename) => {
|
|
10854
11437
|
if (filename !== targetName) return;
|
|
10855
|
-
if (!
|
|
11438
|
+
if (!existsSync8(dirPath)) return;
|
|
10856
11439
|
parentWatcher?.close();
|
|
10857
11440
|
parentWatcher = null;
|
|
10858
11441
|
setWatcher(attach());
|
|
@@ -10882,7 +11465,7 @@ var FileWatcher = class {
|
|
|
10882
11465
|
* Watches both .json and .js files to detect component definition and JavaScript changes
|
|
10883
11466
|
*/
|
|
10884
11467
|
watchComponents(dirPath = projectPaths.components()) {
|
|
10885
|
-
if (!
|
|
11468
|
+
if (!existsSync8(dirPath)) {
|
|
10886
11469
|
return;
|
|
10887
11470
|
}
|
|
10888
11471
|
this.componentsWatcher = watchRecursive(dirPath, async (_event, filename) => {
|
|
@@ -10898,7 +11481,7 @@ var FileWatcher = class {
|
|
|
10898
11481
|
* Start watching pages directory
|
|
10899
11482
|
*/
|
|
10900
11483
|
watchPages(dirPath = projectPaths.pages()) {
|
|
10901
|
-
if (!
|
|
11484
|
+
if (!existsSync8(dirPath)) {
|
|
10902
11485
|
return;
|
|
10903
11486
|
}
|
|
10904
11487
|
this.pagesWatcher = watchRecursive(dirPath, async (_event, filename) => {
|
|
@@ -10938,7 +11521,7 @@ var FileWatcher = class {
|
|
|
10938
11521
|
* Start watching colors.json file
|
|
10939
11522
|
*/
|
|
10940
11523
|
watchColors(filePath = projectPaths.colors()) {
|
|
10941
|
-
if (!
|
|
11524
|
+
if (!existsSync8(filePath)) {
|
|
10942
11525
|
return;
|
|
10943
11526
|
}
|
|
10944
11527
|
const dirPath = getProjectRoot();
|
|
@@ -10995,7 +11578,7 @@ var FileWatcher = class {
|
|
|
10995
11578
|
* Start watching images directory for new source images
|
|
10996
11579
|
*/
|
|
10997
11580
|
watchImages(dirPath = projectPaths.images()) {
|
|
10998
|
-
if (!
|
|
11581
|
+
if (!existsSync8(dirPath)) {
|
|
10999
11582
|
return;
|
|
11000
11583
|
}
|
|
11001
11584
|
this.imagesWatcher = watch(dirPath, { recursive: false }, async (event, filename) => {
|
|
@@ -11022,7 +11605,7 @@ var FileWatcher = class {
|
|
|
11022
11605
|
* Start watching libraries directory for CSS/JS changes
|
|
11023
11606
|
*/
|
|
11024
11607
|
watchLibraries(dirPath = projectPaths.libraries()) {
|
|
11025
|
-
if (!
|
|
11608
|
+
if (!existsSync8(dirPath)) {
|
|
11026
11609
|
return;
|
|
11027
11610
|
}
|
|
11028
11611
|
this.librariesWatcher = watchRecursive(dirPath, async (_event, filename) => {
|
|
@@ -11046,7 +11629,7 @@ var FileWatcher = class {
|
|
|
11046
11629
|
* early-out and the first collection's item edits wouldn't live-reload until a
|
|
11047
11630
|
* dev-server restart.
|
|
11048
11631
|
*/
|
|
11049
|
-
watchAstroPages(dirPath =
|
|
11632
|
+
watchAstroPages(dirPath = join7(getProjectRoot(), "src", "pages")) {
|
|
11050
11633
|
attachWhenDirExists(
|
|
11051
11634
|
dirPath,
|
|
11052
11635
|
() => watchRecursive(dirPath, async (_event, filename) => {
|
|
@@ -11063,7 +11646,7 @@ var FileWatcher = class {
|
|
|
11063
11646
|
}
|
|
11064
11647
|
);
|
|
11065
11648
|
}
|
|
11066
|
-
watchAstroComponents(dirPath =
|
|
11649
|
+
watchAstroComponents(dirPath = join7(getProjectRoot(), "src", "components")) {
|
|
11067
11650
|
attachWhenDirExists(
|
|
11068
11651
|
dirPath,
|
|
11069
11652
|
() => watchRecursive(dirPath, async (_event, filename) => {
|
|
@@ -11076,7 +11659,7 @@ var FileWatcher = class {
|
|
|
11076
11659
|
}
|
|
11077
11660
|
);
|
|
11078
11661
|
}
|
|
11079
|
-
watchAstroContent(dirPath =
|
|
11662
|
+
watchAstroContent(dirPath = join7(getProjectRoot(), "src", "content")) {
|
|
11080
11663
|
attachWhenDirExists(
|
|
11081
11664
|
dirPath,
|
|
11082
11665
|
() => watchRecursive(dirPath, async (_event, filename) => {
|
|
@@ -11279,22 +11862,22 @@ var FileWatcherService = class {
|
|
|
11279
11862
|
};
|
|
11280
11863
|
|
|
11281
11864
|
// lib/server/migrateTemplates.ts
|
|
11282
|
-
import { existsSync as
|
|
11865
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
11283
11866
|
import { rename } from "node:fs/promises";
|
|
11284
|
-
import { join as
|
|
11867
|
+
import { join as join8 } from "node:path";
|
|
11285
11868
|
async function migrateTemplatesDirectory() {
|
|
11286
|
-
const oldDir =
|
|
11869
|
+
const oldDir = join8(projectPaths.pages(), "templates");
|
|
11287
11870
|
const newDir = projectPaths.templates();
|
|
11288
|
-
if (!
|
|
11289
|
-
if (
|
|
11871
|
+
if (!existsSync9(oldDir)) return;
|
|
11872
|
+
if (existsSync9(newDir)) return;
|
|
11290
11873
|
await rename(oldDir, newDir);
|
|
11291
11874
|
console.log("Migrated CMS templates: pages/templates/ \u2192 templates/");
|
|
11292
11875
|
}
|
|
11293
11876
|
|
|
11294
11877
|
// build-astro.ts
|
|
11295
|
-
import { existsSync as
|
|
11878
|
+
import { existsSync as existsSync10, readdirSync as readdirSync6, mkdirSync as mkdirSync4, rmSync, statSync as statSync2, copyFileSync, writeFileSync } from "node:fs";
|
|
11296
11879
|
import { writeFile as writeFile2, readFile } from "node:fs/promises";
|
|
11297
|
-
import { join as
|
|
11880
|
+
import { join as join9 } from "node:path";
|
|
11298
11881
|
import { createHash } from "node:crypto";
|
|
11299
11882
|
|
|
11300
11883
|
// lib/server/astro/tailwindMapper.ts
|
|
@@ -14147,23 +14730,23 @@ function writePageScript(javascript, scriptsDir) {
|
|
|
14147
14730
|
if (!javascript) return [];
|
|
14148
14731
|
const hash = hashContent3(javascript);
|
|
14149
14732
|
const scriptFile = `${hash}.js`;
|
|
14150
|
-
if (!
|
|
14733
|
+
if (!existsSync10(scriptsDir)) {
|
|
14151
14734
|
mkdirSync4(scriptsDir, { recursive: true });
|
|
14152
14735
|
}
|
|
14153
|
-
const fullScriptPath =
|
|
14154
|
-
if (!
|
|
14736
|
+
const fullScriptPath = join9(scriptsDir, scriptFile);
|
|
14737
|
+
if (!existsSync10(fullScriptPath)) {
|
|
14155
14738
|
writeFileSync(fullScriptPath, javascript, "utf-8");
|
|
14156
14739
|
}
|
|
14157
14740
|
return [`/_scripts/${scriptFile}`];
|
|
14158
14741
|
}
|
|
14159
14742
|
function copyDirectory(src, dest, filter) {
|
|
14160
|
-
if (!
|
|
14161
|
-
if (!
|
|
14743
|
+
if (!existsSync10(src)) return;
|
|
14744
|
+
if (!existsSync10(dest)) mkdirSync4(dest, { recursive: true });
|
|
14162
14745
|
const files = readdirSync6(src);
|
|
14163
14746
|
for (const file of files) {
|
|
14164
14747
|
if (filter && !filter(file)) continue;
|
|
14165
|
-
const srcPath =
|
|
14166
|
-
const destPath =
|
|
14748
|
+
const srcPath = join9(src, file);
|
|
14749
|
+
const destPath = join9(dest, file);
|
|
14167
14750
|
const stat = statSync2(srcPath);
|
|
14168
14751
|
if (stat.isDirectory()) copyDirectory(srcPath, destPath, filter);
|
|
14169
14752
|
else copyFileSync(srcPath, destPath);
|
|
@@ -14180,13 +14763,13 @@ function isCMSPage(pageData) {
|
|
|
14180
14763
|
}
|
|
14181
14764
|
function scanJSONFiles(dir, prefix = "") {
|
|
14182
14765
|
const results = [];
|
|
14183
|
-
if (!
|
|
14766
|
+
if (!existsSync10(dir)) return results;
|
|
14184
14767
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
14185
14768
|
for (const entry of entries) {
|
|
14186
14769
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
14187
14770
|
results.push(prefix ? `${prefix}/${entry.name}` : entry.name);
|
|
14188
14771
|
} else if (entry.isDirectory()) {
|
|
14189
|
-
results.push(...scanJSONFiles(
|
|
14772
|
+
results.push(...scanJSONFiles(join9(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name));
|
|
14190
14773
|
}
|
|
14191
14774
|
}
|
|
14192
14775
|
return results;
|
|
@@ -14352,23 +14935,23 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14352
14935
|
const globalLibraries = configService.getLibraries();
|
|
14353
14936
|
const componentLibraries = collectComponentLibraries(globalComponents);
|
|
14354
14937
|
const imageMetadataMap = await buildImageMetadataMap();
|
|
14355
|
-
const outDir = outputDir ||
|
|
14356
|
-
if (
|
|
14938
|
+
const outDir = outputDir || join9(projectPaths.project, "astro-export");
|
|
14939
|
+
if (existsSync10(outDir)) {
|
|
14357
14940
|
rmSync(outDir, { recursive: true, force: true });
|
|
14358
14941
|
}
|
|
14359
14942
|
mkdirSync4(outDir, { recursive: true });
|
|
14360
|
-
const srcDir =
|
|
14361
|
-
const pagesOutDir =
|
|
14362
|
-
const layoutsDir =
|
|
14363
|
-
const stylesDir =
|
|
14364
|
-
const componentsOutDir =
|
|
14365
|
-
const publicDir =
|
|
14366
|
-
const scriptsDir =
|
|
14943
|
+
const srcDir = join9(outDir, "src");
|
|
14944
|
+
const pagesOutDir = join9(srcDir, "pages");
|
|
14945
|
+
const layoutsDir = join9(srcDir, "layouts");
|
|
14946
|
+
const stylesDir = join9(srcDir, "styles");
|
|
14947
|
+
const componentsOutDir = join9(srcDir, "components");
|
|
14948
|
+
const publicDir = join9(outDir, "public");
|
|
14949
|
+
const scriptsDir = join9(publicDir, "_scripts");
|
|
14367
14950
|
for (const d of [srcDir, pagesOutDir, layoutsDir, stylesDir, componentsOutDir, publicDir]) {
|
|
14368
14951
|
mkdirSync4(d, { recursive: true });
|
|
14369
14952
|
}
|
|
14370
14953
|
const pagesDir = projectPaths.pages();
|
|
14371
|
-
if (!
|
|
14954
|
+
if (!existsSync10(pagesDir)) {
|
|
14372
14955
|
console.error("Pages directory not found!");
|
|
14373
14956
|
return { pages: 0, cmsPages: 0, collections: 0, errors: 1 };
|
|
14374
14957
|
}
|
|
@@ -14381,7 +14964,7 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14381
14964
|
for (const file of pageFiles) {
|
|
14382
14965
|
const pageName = file.replace(".json", "");
|
|
14383
14966
|
const basePath = mapPageNameToPath(pageName);
|
|
14384
|
-
const pageContent = await loadJSONFile(
|
|
14967
|
+
const pageContent = await loadJSONFile(join9(pagesDir, file));
|
|
14385
14968
|
if (!pageContent) continue;
|
|
14386
14969
|
try {
|
|
14387
14970
|
const pageData = parseJSON(pageContent);
|
|
@@ -14440,7 +15023,7 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14440
15023
|
for (const file of pageFiles) {
|
|
14441
15024
|
const pageName = file.replace(".json", "");
|
|
14442
15025
|
const basePath = mapPageNameToPath(pageName);
|
|
14443
|
-
const pageContent = await loadJSONFile(
|
|
15026
|
+
const pageContent = await loadJSONFile(join9(pagesDir, file));
|
|
14444
15027
|
if (!pageContent) {
|
|
14445
15028
|
console.warn(` Skipping ${basePath} (empty file)`);
|
|
14446
15029
|
errorCount++;
|
|
@@ -14498,8 +15081,8 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14498
15081
|
if (!css.url.startsWith("/")) continue;
|
|
14499
15082
|
const shouldInline = css.inline !== false;
|
|
14500
15083
|
const relPath = css.url.slice(1);
|
|
14501
|
-
const srcPath =
|
|
14502
|
-
if (!
|
|
15084
|
+
const srcPath = join9(projectPaths.project, relPath);
|
|
15085
|
+
if (!existsSync10(srcPath)) continue;
|
|
14503
15086
|
if (shouldInline) {
|
|
14504
15087
|
try {
|
|
14505
15088
|
inlineContents.set(css.url, await readFile(srcPath, "utf-8"));
|
|
@@ -14513,7 +15096,7 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14513
15096
|
for (const js of buildLibraries.js || []) {
|
|
14514
15097
|
if (js.url.startsWith("/")) {
|
|
14515
15098
|
const relPath = js.url.slice(1);
|
|
14516
|
-
if (
|
|
15099
|
+
if (existsSync10(join9(projectPaths.project, relPath))) {
|
|
14517
15100
|
localLibsToCopy.push(relPath);
|
|
14518
15101
|
}
|
|
14519
15102
|
}
|
|
@@ -14534,9 +15117,9 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14534
15117
|
const cmsConsumerComponents = computeCmsConsumerComponents(globalComponents);
|
|
14535
15118
|
const collectionUrlExpr = /* @__PURE__ */ new Map();
|
|
14536
15119
|
const mergedRichTextFields = /* @__PURE__ */ new Set();
|
|
14537
|
-
if (
|
|
15120
|
+
if (existsSync10(templatesDir)) {
|
|
14538
15121
|
for (const file of readdirSync6(templatesDir).filter((f) => f.endsWith(".json"))) {
|
|
14539
|
-
const tc = await loadJSONFile(
|
|
15122
|
+
const tc = await loadJSONFile(join9(templatesDir, file));
|
|
14540
15123
|
if (!tc) continue;
|
|
14541
15124
|
try {
|
|
14542
15125
|
const pd = parseJSON(tc);
|
|
@@ -14550,10 +15133,10 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14550
15133
|
}
|
|
14551
15134
|
}
|
|
14552
15135
|
}
|
|
14553
|
-
if (
|
|
15136
|
+
if (existsSync10(templatesDir)) {
|
|
14554
15137
|
const templateFiles = readdirSync6(templatesDir).filter((f) => f.endsWith(".json"));
|
|
14555
15138
|
for (const file of templateFiles) {
|
|
14556
|
-
const templateContent = await loadJSONFile(
|
|
15139
|
+
const templateContent = await loadJSONFile(join9(templatesDir, file));
|
|
14557
15140
|
if (!templateContent) continue;
|
|
14558
15141
|
try {
|
|
14559
15142
|
const pageData = parseJSON(templateContent);
|
|
@@ -14642,9 +15225,9 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14642
15225
|
cmsConsumers: cmsConsumerComponents,
|
|
14643
15226
|
collectionUrlExpr
|
|
14644
15227
|
});
|
|
14645
|
-
const astroFileFull =
|
|
15228
|
+
const astroFileFull = join9(pagesOutDir, astroFilePath);
|
|
14646
15229
|
const astroFileDir = astroFileFull.substring(0, astroFileFull.lastIndexOf("/"));
|
|
14647
|
-
if (!
|
|
15230
|
+
if (!existsSync10(astroFileDir)) {
|
|
14648
15231
|
mkdirSync4(astroFileDir, { recursive: true });
|
|
14649
15232
|
}
|
|
14650
15233
|
await writeFile2(astroFileFull, astroContent, "utf-8");
|
|
@@ -14676,7 +15259,7 @@ async function buildAstroProject(_projectRoot, outputDir) {
|
|
|
14676
15259
|
|
|
14677
15260
|
${safelistDirectives}` : `@import "tailwindcss";`;
|
|
14678
15261
|
const globalCSS = [tailwindDirectives, fontCSS, themeColorCSS, variablesCSS, baseCSS, componentCSSCombined].filter(Boolean).join("\n\n");
|
|
14679
|
-
await writeFile2(
|
|
15262
|
+
await writeFile2(join9(stylesDir, "global.css"), globalCSS, "utf-8");
|
|
14680
15263
|
const escForTemplateLiteral = (s) => s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
14681
15264
|
const customHeadLiteral = escForTemplateLiteral(customCode.head || "");
|
|
14682
15265
|
const customBodyStartLiteral = escForTemplateLiteral(customCode.bodyStart || "");
|
|
@@ -14723,7 +15306,7 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
|
|
|
14723
15306
|
</body>
|
|
14724
15307
|
</html>
|
|
14725
15308
|
`;
|
|
14726
|
-
await writeFile2(
|
|
15309
|
+
await writeFile2(join9(layoutsDir, "BaseLayout.astro"), baseLayoutContent, "utf-8");
|
|
14727
15310
|
const emittableComponents = normalizeOrphanTemplateProps(globalComponents);
|
|
14728
15311
|
let _componentFileCount = 0;
|
|
14729
15312
|
for (const [compName, compDef] of Object.entries(emittableComponents)) {
|
|
@@ -14742,7 +15325,7 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
|
|
|
14742
15325
|
collectionUrlExpr
|
|
14743
15326
|
}
|
|
14744
15327
|
);
|
|
14745
|
-
await writeFile2(
|
|
15328
|
+
await writeFile2(join9(componentsOutDir, `${compName}.astro`), astroContent, "utf-8");
|
|
14746
15329
|
_componentFileCount++;
|
|
14747
15330
|
} catch (error) {
|
|
14748
15331
|
console.warn(
|
|
@@ -14793,9 +15376,9 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
|
|
|
14793
15376
|
scriptPaths = writePageScript(result.javascript, scriptsDir);
|
|
14794
15377
|
astroContent = buildSSRFallbackPage(result, importPath, fontPreloads, libraryTags, defaultTheme, scriptPaths);
|
|
14795
15378
|
}
|
|
14796
|
-
const astroFileFull =
|
|
15379
|
+
const astroFileFull = join9(pagesOutDir, result.astroFilePath);
|
|
14797
15380
|
const astroFileDir = astroFileFull.substring(0, astroFileFull.lastIndexOf("/"));
|
|
14798
|
-
if (!
|
|
15381
|
+
if (!existsSync10(astroFileDir)) {
|
|
14799
15382
|
mkdirSync4(astroFileDir, { recursive: true });
|
|
14800
15383
|
}
|
|
14801
15384
|
await writeFile2(astroFileFull, astroContent, "utf-8");
|
|
@@ -14816,31 +15399,31 @@ export const GET: APIRoute = () => {
|
|
|
14816
15399
|
});
|
|
14817
15400
|
};
|
|
14818
15401
|
`;
|
|
14819
|
-
await writeFile2(
|
|
15402
|
+
await writeFile2(join9(pagesOutDir, "robots.txt.ts"), robotsTsContent, "utf-8");
|
|
14820
15403
|
let collectionCount = 0;
|
|
14821
15404
|
if (templateSchemas.length > 0) {
|
|
14822
|
-
const contentDir =
|
|
15405
|
+
const contentDir = join9(srcDir, "content");
|
|
14823
15406
|
mkdirSync4(contentDir, { recursive: true });
|
|
14824
15407
|
const collectionDefs = [];
|
|
14825
15408
|
for (const schema of templateSchemas) {
|
|
14826
|
-
const collectionDir =
|
|
15409
|
+
const collectionDir = join9(contentDir, schema.id);
|
|
14827
15410
|
mkdirSync4(collectionDir, { recursive: true });
|
|
14828
15411
|
const richTextFieldNames = Object.entries(schema.fields || {}).filter(([, fd]) => fd.type === "rich-text").map(([fn]) => fn);
|
|
14829
|
-
const cmsItemsDir =
|
|
14830
|
-
if (
|
|
15412
|
+
const cmsItemsDir = join9(projectPaths.cms(), schema.id);
|
|
15413
|
+
if (existsSync10(cmsItemsDir)) {
|
|
14831
15414
|
const isDevBuild = process.env.MENO_DEV_BUILD === "true";
|
|
14832
15415
|
const itemFiles = readdirSync6(cmsItemsDir).filter(
|
|
14833
15416
|
(f) => f.endsWith(".json") && (isDevBuild || !f.endsWith(`${CMS_DRAFT_SUFFIX}.json`))
|
|
14834
15417
|
);
|
|
14835
15418
|
for (const itemFile of itemFiles) {
|
|
14836
15419
|
try {
|
|
14837
|
-
const rawContent = await readFile(
|
|
15420
|
+
const rawContent = await readFile(join9(cmsItemsDir, itemFile), "utf-8");
|
|
14838
15421
|
const item = JSON.parse(rawContent);
|
|
14839
15422
|
const resolved = { ...item };
|
|
14840
15423
|
for (const fieldName of richTextFieldNames) {
|
|
14841
15424
|
resolved[fieldName] = serializeRichTextValue(resolved[fieldName]);
|
|
14842
15425
|
}
|
|
14843
|
-
await writeFile2(
|
|
15426
|
+
await writeFile2(join9(collectionDir, itemFile), JSON.stringify(resolved, null, 2), "utf-8");
|
|
14844
15427
|
} catch (err) {
|
|
14845
15428
|
console.warn(
|
|
14846
15429
|
` Warning: could not process CMS item ${itemFile}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -14873,29 +15456,29 @@ ${collectionDefs.join(",\n")}
|
|
|
14873
15456
|
|
|
14874
15457
|
export { collections };
|
|
14875
15458
|
`;
|
|
14876
|
-
await writeFile2(
|
|
15459
|
+
await writeFile2(join9(srcDir, "content.config.ts"), configContent, "utf-8");
|
|
14877
15460
|
}
|
|
14878
|
-
const imagesSrcDir =
|
|
14879
|
-
if (
|
|
14880
|
-
copyDirectory(imagesSrcDir,
|
|
14881
|
-
copyDirectory(imagesSrcDir,
|
|
15461
|
+
const imagesSrcDir = join9(projectPaths.project, "images");
|
|
15462
|
+
if (existsSync10(imagesSrcDir)) {
|
|
15463
|
+
copyDirectory(imagesSrcDir, join9(srcDir, "assets", "images"), shouldCopyImageForAstro);
|
|
15464
|
+
copyDirectory(imagesSrcDir, join9(publicDir, "images"));
|
|
14882
15465
|
}
|
|
14883
15466
|
const publicAssetDirs = ["fonts", "icons", "videos", "assets"];
|
|
14884
15467
|
for (const dir of publicAssetDirs) {
|
|
14885
|
-
const srcAssetDir =
|
|
14886
|
-
if (
|
|
14887
|
-
copyDirectory(srcAssetDir,
|
|
15468
|
+
const srcAssetDir = join9(projectPaths.project, dir);
|
|
15469
|
+
if (existsSync10(srcAssetDir)) {
|
|
15470
|
+
copyDirectory(srcAssetDir, join9(publicDir, dir));
|
|
14888
15471
|
}
|
|
14889
15472
|
}
|
|
14890
|
-
const librariesDir =
|
|
14891
|
-
if (
|
|
14892
|
-
copyDirectory(librariesDir,
|
|
15473
|
+
const librariesDir = join9(projectPaths.project, "libraries");
|
|
15474
|
+
if (existsSync10(librariesDir)) {
|
|
15475
|
+
copyDirectory(librariesDir, join9(publicDir, "libraries"));
|
|
14893
15476
|
}
|
|
14894
15477
|
for (const relPath of localLibsToCopy) {
|
|
14895
|
-
const srcPath =
|
|
14896
|
-
const destPath =
|
|
15478
|
+
const srcPath = join9(projectPaths.project, relPath);
|
|
15479
|
+
const destPath = join9(publicDir, relPath);
|
|
14897
15480
|
const destDir = destPath.substring(0, destPath.lastIndexOf("/"));
|
|
14898
|
-
if (destDir && !
|
|
15481
|
+
if (destDir && !existsSync10(destDir)) mkdirSync4(destDir, { recursive: true });
|
|
14899
15482
|
copyFileSync(srcPath, destPath);
|
|
14900
15483
|
}
|
|
14901
15484
|
const packageJson = {
|
|
@@ -14918,7 +15501,7 @@ export { collections };
|
|
|
14918
15501
|
tailwindcss: "^4.0.0"
|
|
14919
15502
|
}
|
|
14920
15503
|
};
|
|
14921
|
-
await writeFile2(
|
|
15504
|
+
await writeFile2(join9(outDir, "package.json"), JSON.stringify(packageJson, null, 2), "utf-8");
|
|
14922
15505
|
const localeCodes = i18nConfig.locales.map((l) => l.code);
|
|
14923
15506
|
const i18nBlock = i18nConfig.locales.length > 1 ? `
|
|
14924
15507
|
i18n: {
|
|
@@ -14938,11 +15521,11 @@ export default defineConfig({${siteUrl ? `
|
|
|
14938
15521
|
},
|
|
14939
15522
|
});
|
|
14940
15523
|
`;
|
|
14941
|
-
await writeFile2(
|
|
15524
|
+
await writeFile2(join9(outDir, "astro.config.mjs"), astroConfig, "utf-8");
|
|
14942
15525
|
const tsConfig = {
|
|
14943
15526
|
extends: "astro/tsconfigs/strict"
|
|
14944
15527
|
};
|
|
14945
|
-
await writeFile2(
|
|
15528
|
+
await writeFile2(join9(outDir, "tsconfig.json"), JSON.stringify(tsConfig, null, 2), "utf-8");
|
|
14946
15529
|
const baseNetlifyToml = `# Generated by Meno's Astro build.
|
|
14947
15530
|
[build]
|
|
14948
15531
|
command = "npm run build"
|
|
@@ -14952,8 +15535,8 @@ export default defineConfig({${siteUrl ? `
|
|
|
14952
15535
|
NODE_VERSION = "22"
|
|
14953
15536
|
`;
|
|
14954
15537
|
const netlifyToml = syncNetlifyLocale404Block(baseNetlifyToml, i18nConfig, pageFiles.includes("404.json"));
|
|
14955
|
-
await writeFile2(
|
|
14956
|
-
await writeFile2(
|
|
15538
|
+
await writeFile2(join9(outDir, "netlify.toml"), netlifyToml, "utf-8");
|
|
15539
|
+
await writeFile2(join9(outDir, "src", "env.d.ts"), '/// <reference path="../.astro/types.d.ts" />\n', "utf-8");
|
|
14957
15540
|
const totalPages = allResults.length;
|
|
14958
15541
|
return {
|
|
14959
15542
|
pages: totalPages - cmsPageCount,
|
|
@@ -15017,6 +15600,7 @@ export {
|
|
|
15017
15600
|
getModuleDir,
|
|
15018
15601
|
getPackageRoot,
|
|
15019
15602
|
getProjectRoot,
|
|
15603
|
+
groupForSectionLabel,
|
|
15020
15604
|
handleCollectionSchemaRoute,
|
|
15021
15605
|
handleCollectionsRoute,
|
|
15022
15606
|
handleCoreApiRoutes,
|
|
@@ -15025,11 +15609,15 @@ export {
|
|
|
15025
15609
|
handleItemsRoute,
|
|
15026
15610
|
handleRouteError,
|
|
15027
15611
|
hashContent,
|
|
15612
|
+
inferGroup,
|
|
15028
15613
|
inspect,
|
|
15029
15614
|
isBun3 as isBun,
|
|
15615
|
+
isColorValue,
|
|
15616
|
+
isThemeCssProject,
|
|
15030
15617
|
jsonResponse,
|
|
15031
15618
|
lineMapToObject,
|
|
15032
15619
|
loadProjectConfig,
|
|
15620
|
+
loadThemeModel,
|
|
15033
15621
|
logError,
|
|
15034
15622
|
logRequest,
|
|
15035
15623
|
logResponseTime,
|
|
@@ -15039,19 +15627,25 @@ export {
|
|
|
15039
15627
|
needsFormHandler,
|
|
15040
15628
|
needsMenoFilter,
|
|
15041
15629
|
packagePaths,
|
|
15630
|
+
parseThemeCss,
|
|
15042
15631
|
processCMSPropsTemplate,
|
|
15043
15632
|
processCMSTemplate,
|
|
15044
15633
|
projectPaths,
|
|
15045
15634
|
readJsonFile,
|
|
15046
15635
|
readTextFile,
|
|
15636
|
+
regenerateThemeCss,
|
|
15047
15637
|
renderPageSSR,
|
|
15048
15638
|
resetFontConfig,
|
|
15049
15639
|
resolvePackagePath,
|
|
15050
15640
|
resolveProjectPath,
|
|
15641
|
+
saveColors,
|
|
15642
|
+
saveVariables,
|
|
15643
|
+
serializeThemeCss,
|
|
15051
15644
|
serveFile,
|
|
15052
15645
|
setProjectRoot,
|
|
15053
15646
|
spawnProcess,
|
|
15054
15647
|
styleToString,
|
|
15648
|
+
themeCssPath,
|
|
15055
15649
|
validateJS,
|
|
15056
15650
|
variableService,
|
|
15057
15651
|
watchRecursive,
|