meno-core 1.0.52 → 1.0.53
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/build-astro.ts +183 -13
- package/build-next.ts +1361 -0
- package/build-static.ts +7 -5
- package/dist/bin/cli.js +2 -2
- package/dist/build-static.js +6 -6
- package/dist/chunks/{chunk-HNLUO36W.js → chunk-GZHGVVW3.js} +2 -2
- package/dist/chunks/chunk-GZHGVVW3.js.map +7 -0
- package/dist/chunks/{chunk-LPVETICS.js → chunk-H3GJ4H2U.js} +185 -1
- package/dist/chunks/chunk-H3GJ4H2U.js.map +7 -0
- package/dist/chunks/{chunk-CXCBV2M7.js → chunk-IGYR22T6.js} +76 -270
- package/dist/chunks/chunk-IGYR22T6.js.map +7 -0
- package/dist/chunks/{chunk-LHLHPYSP.js → chunk-JGP5A3Y5.js} +12 -11
- package/dist/chunks/chunk-JGP5A3Y5.js.map +7 -0
- package/dist/chunks/{chunk-7NIC4I3V.js → chunk-JGWFTO6P.js} +167 -21
- package/dist/chunks/chunk-JGWFTO6P.js.map +7 -0
- package/dist/chunks/{chunk-EDQSMAMP.js → chunk-O3NAGJP4.js} +85 -4
- package/dist/chunks/chunk-O3NAGJP4.js.map +7 -0
- package/dist/chunks/{chunk-H4JSCDNW.js → chunk-QB2LNO4W.js} +24 -1
- package/dist/chunks/chunk-QB2LNO4W.js.map +7 -0
- package/dist/chunks/{chunk-A725KYFK.js → chunk-R6XHAFBF.js} +561 -112
- package/dist/chunks/chunk-R6XHAFBF.js.map +7 -0
- package/dist/chunks/{chunk-J23ZX5AP.js → chunk-X754AHS5.js} +277 -1
- package/dist/chunks/chunk-X754AHS5.js.map +7 -0
- package/dist/chunks/{chunk-2QK6U5UK.js → chunk-YBLHKYFF.js} +12 -2
- package/dist/chunks/chunk-YBLHKYFF.js.map +7 -0
- package/dist/chunks/{constants-GWBAD66U.js → constants-STK2YBIW.js} +2 -2
- package/dist/entries/server-router.js +7 -7
- package/dist/lib/client/index.js +354 -59
- package/dist/lib/client/index.js.map +4 -4
- package/dist/lib/server/index.js +1458 -190
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +202 -34
- package/dist/lib/shared/index.js.map +4 -4
- package/dist/lib/test-utils/index.js +1 -1
- package/entries/client-router.tsx +5 -165
- package/lib/client/ErrorBoundary.test.tsx +27 -25
- package/lib/client/ErrorBoundary.tsx +34 -19
- package/lib/client/core/ComponentBuilder.ts +19 -2
- package/lib/client/core/builders/embedBuilder.ts +8 -4
- package/lib/client/core/builders/listBuilder.ts +23 -4
- package/lib/client/fontFamiliesService.test.ts +76 -0
- package/lib/client/fontFamiliesService.ts +69 -0
- package/lib/client/hmrCssReload.ts +160 -0
- package/lib/client/hooks/useColorVariables.ts +2 -0
- package/lib/client/index.ts +4 -0
- package/lib/client/meno-filter/ui.ts +2 -0
- package/lib/client/routing/RouteLoader.test.ts +2 -2
- package/lib/client/routing/RouteLoader.ts +8 -2
- package/lib/client/routing/Router.tsx +81 -15
- package/lib/client/scripts/ScriptExecutor.test.ts +143 -0
- package/lib/client/scripts/ScriptExecutor.ts +56 -2
- package/lib/client/styles/StyleInjector.ts +20 -5
- package/lib/client/styles/UtilityClassCollector.ts +7 -1
- package/lib/client/styles/cspNonce.test.ts +67 -0
- package/lib/client/styles/cspNonce.ts +63 -0
- package/lib/client/templateEngine.test.ts +80 -0
- package/lib/client/templateEngine.ts +5 -0
- package/lib/server/astro/cmsPageEmitter.ts +35 -5
- package/lib/server/astro/componentEmitter.ts +61 -5
- package/lib/server/astro/nodeToAstro.ts +149 -11
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +264 -0
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +184 -0
- package/lib/server/createServer.ts +11 -0
- package/lib/server/draftPageStore.ts +49 -0
- package/lib/server/fileWatcher.ts +62 -2
- package/lib/server/index.ts +13 -1
- package/lib/server/providers/fileSystemPageProvider.ts +8 -0
- package/lib/server/routes/api/components.ts +9 -4
- package/lib/server/routes/api/core-routes.ts +2 -2
- package/lib/server/routes/api/pages.ts +14 -22
- package/lib/server/routes/api/shared.ts +56 -0
- package/lib/server/routes/index.ts +90 -0
- package/lib/server/routes/pages.ts +13 -6
- package/lib/server/services/componentService.test.ts +199 -2
- package/lib/server/services/componentService.ts +354 -49
- package/lib/server/services/fileWatcherService.ts +4 -24
- package/lib/server/services/pageService.test.ts +23 -0
- package/lib/server/services/pageService.ts +124 -6
- package/lib/server/ssr/attributeBuilder.ts +8 -2
- package/lib/server/ssr/buildErrorOverlay.ts +1 -1
- package/lib/server/ssr/errorOverlay.test.ts +21 -2
- package/lib/server/ssr/errorOverlay.ts +38 -11
- package/lib/server/ssr/htmlGenerator.test.ts +53 -13
- package/lib/server/ssr/htmlGenerator.ts +71 -27
- package/lib/server/ssr/liveReloadIntegration.test.ts +123 -2
- package/lib/server/ssr/ssrRenderer.test.ts +67 -0
- package/lib/server/ssr/ssrRenderer.ts +94 -9
- package/lib/server/websocketManager.ts +0 -1
- package/lib/shared/componentRefs.ts +45 -0
- package/lib/shared/constants.ts +8 -0
- package/lib/shared/cssGeneration.ts +2 -0
- package/lib/shared/cssProperties.ts +184 -0
- package/lib/shared/expressionEvaluator.ts +54 -0
- package/lib/shared/fontCss.ts +101 -0
- package/lib/shared/fontLoader.ts +8 -86
- package/lib/shared/friendlyError.test.ts +87 -0
- package/lib/shared/friendlyError.ts +121 -0
- package/lib/shared/hrefRefs.test.ts +130 -0
- package/lib/shared/hrefRefs.ts +100 -0
- package/lib/shared/index.ts +52 -0
- package/lib/shared/inlineSvgStyleRules.test.ts +108 -0
- package/lib/shared/inlineSvgStyleRules.ts +134 -0
- package/lib/shared/interfaces/contentProvider.ts +13 -0
- package/lib/shared/itemTemplateUtils.test.ts +14 -0
- package/lib/shared/itemTemplateUtils.ts +4 -1
- package/lib/shared/registry/NodeTypeDefinition.ts +1 -1
- package/lib/shared/registry/nodeTypes/LinkNodeType.ts +1 -1
- package/lib/shared/slugTranslator.test.ts +24 -0
- package/lib/shared/slugTranslator.ts +24 -0
- package/lib/shared/styleNodeUtils.ts +4 -1
- package/lib/shared/tree/PathBuilder.test.ts +128 -1
- package/lib/shared/tree/PathBuilder.ts +83 -31
- package/lib/shared/types/comment.ts +99 -0
- package/lib/shared/types/index.ts +12 -0
- package/lib/shared/types/rendering.ts +8 -0
- package/lib/shared/utilityClassConfig.ts +4 -2
- package/lib/shared/utilityClassMapper.test.ts +24 -0
- package/lib/shared/validation/commentValidators.ts +69 -0
- package/lib/shared/validation/index.ts +1 -0
- package/lib/shared/viewportUnits.integration.test.ts +42 -0
- package/lib/shared/viewportUnits.test.ts +103 -0
- package/lib/shared/viewportUnits.ts +63 -0
- package/lib/test-utils/dom-setup.ts +6 -0
- package/package.json +1 -1
- package/dist/chunks/chunk-2QK6U5UK.js.map +0 -7
- package/dist/chunks/chunk-7NIC4I3V.js.map +0 -7
- package/dist/chunks/chunk-A725KYFK.js.map +0 -7
- package/dist/chunks/chunk-CXCBV2M7.js.map +0 -7
- package/dist/chunks/chunk-EDQSMAMP.js.map +0 -7
- package/dist/chunks/chunk-H4JSCDNW.js.map +0 -7
- package/dist/chunks/chunk-HNLUO36W.js.map +0 -7
- package/dist/chunks/chunk-J23ZX5AP.js.map +0 -7
- package/dist/chunks/chunk-LHLHPYSP.js.map +0 -7
- package/dist/chunks/chunk-LPVETICS.js.map +0 -7
- /package/dist/chunks/{constants-GWBAD66U.js.map → constants-STK2YBIW.js.map} +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CachedConfigLoader,
|
|
3
|
-
buildSlugIndex,
|
|
4
3
|
colorService,
|
|
5
4
|
formatJsonErrorMessage,
|
|
6
5
|
generateFontCSS,
|
|
@@ -13,9 +12,8 @@ import {
|
|
|
13
12
|
loadProjectConfig,
|
|
14
13
|
mapPageNameToPath,
|
|
15
14
|
parseJSON,
|
|
16
|
-
resolveSlugToPageId,
|
|
17
15
|
variableService
|
|
18
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-IGYR22T6.js";
|
|
19
17
|
import {
|
|
20
18
|
configService
|
|
21
19
|
} from "./chunk-2MHDV5BF.js";
|
|
@@ -37,14 +35,18 @@ import {
|
|
|
37
35
|
writeFile
|
|
38
36
|
} from "./chunk-WQFG7PAH.js";
|
|
39
37
|
import {
|
|
40
|
-
extractStringValue
|
|
41
|
-
|
|
38
|
+
extractStringValue,
|
|
39
|
+
rewriteComponentRefs
|
|
40
|
+
} from "./chunk-QB2LNO4W.js";
|
|
42
41
|
import {
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
buildSlugIndex,
|
|
43
|
+
isPathWithinRoot,
|
|
44
|
+
resolveSlugToPageId
|
|
45
|
+
} from "./chunk-X754AHS5.js";
|
|
45
46
|
import {
|
|
46
|
-
addItemUrls
|
|
47
|
-
|
|
47
|
+
addItemUrls,
|
|
48
|
+
toFriendlyError
|
|
49
|
+
} from "./chunk-JGWFTO6P.js";
|
|
48
50
|
import {
|
|
49
51
|
parseLocaleFromPath
|
|
50
52
|
} from "./chunk-AZQYF6KE.js";
|
|
@@ -55,7 +57,7 @@ import {
|
|
|
55
57
|
SERVER_PORT,
|
|
56
58
|
WEBSOCKET_STATES,
|
|
57
59
|
init_constants
|
|
58
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-YBLHKYFF.js";
|
|
59
61
|
|
|
60
62
|
// lib/server/pageCache.ts
|
|
61
63
|
var PageCache = class {
|
|
@@ -540,6 +542,39 @@ var PageService = class {
|
|
|
540
542
|
const lineMap = buildLineMap(content);
|
|
541
543
|
this.pageCache.set(path2, content, lineMap);
|
|
542
544
|
}
|
|
545
|
+
/**
|
|
546
|
+
* Re-read a single page from disk via the provider and refresh the cache.
|
|
547
|
+
*
|
|
548
|
+
* Used by the file watcher for live reload. Provider-based (not a hardcoded
|
|
549
|
+
* JSON read), so it works for any format — the FileSystem provider returns the
|
|
550
|
+
* raw JSON and the Astro provider returns the parsed model serialized to JSON.
|
|
551
|
+
*
|
|
552
|
+
* @returns true if the cache changed (page updated or confirmed-deleted) and a
|
|
553
|
+
* reload broadcast is warranted; false if the read failed transiently (e.g. a
|
|
554
|
+
* partial mid-write or parse error), in which case the cache is left intact and
|
|
555
|
+
* the next watcher event retries.
|
|
556
|
+
*/
|
|
557
|
+
async reloadPageFromDisk(path2) {
|
|
558
|
+
if (!this.provider) return false;
|
|
559
|
+
let content = null;
|
|
560
|
+
try {
|
|
561
|
+
content = await this.provider.get(path2);
|
|
562
|
+
} catch {
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
if (content) {
|
|
566
|
+
this.pageCache.set(path2, content, buildLineMap(content));
|
|
567
|
+
return true;
|
|
568
|
+
}
|
|
569
|
+
try {
|
|
570
|
+
if (!await this.provider.exists(path2)) {
|
|
571
|
+
this.pageCache.delete(path2);
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
} catch {
|
|
575
|
+
}
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
543
578
|
/**
|
|
544
579
|
* Delete page from cache and optionally from storage
|
|
545
580
|
*
|
|
@@ -616,6 +651,19 @@ var PageService = class {
|
|
|
616
651
|
getLineMap(path2) {
|
|
617
652
|
return this.pageCache.getLineMap(path2);
|
|
618
653
|
}
|
|
654
|
+
/**
|
|
655
|
+
* Pages base directory — provider-aware (e.g. `src/pages` for astro projects),
|
|
656
|
+
* falling back to `projectPaths.pages()` when the provider doesn't expose
|
|
657
|
+
* `baseDir()` (or no provider is set). Used by the folder/move/rename ops that
|
|
658
|
+
* touch the filesystem directly.
|
|
659
|
+
*/
|
|
660
|
+
pagesBaseDir() {
|
|
661
|
+
return this.provider?.baseDir?.() ?? projectPaths.pages();
|
|
662
|
+
}
|
|
663
|
+
/** Page file extension (e.g. `.astro` for astro projects), defaulting to `.json`. */
|
|
664
|
+
pageExt() {
|
|
665
|
+
return this.provider?.extension?.() ?? ".json";
|
|
666
|
+
}
|
|
619
667
|
/**
|
|
620
668
|
* Get all page folders
|
|
621
669
|
*
|
|
@@ -624,7 +672,7 @@ var PageService = class {
|
|
|
624
672
|
* @returns Sorted array of folder names
|
|
625
673
|
*/
|
|
626
674
|
getAllFolders() {
|
|
627
|
-
const pagesDir =
|
|
675
|
+
const pagesDir = this.pagesBaseDir();
|
|
628
676
|
if (!existsSync(pagesDir)) {
|
|
629
677
|
return [];
|
|
630
678
|
}
|
|
@@ -666,7 +714,7 @@ var PageService = class {
|
|
|
666
714
|
throw new Error("Each folder segment can only contain lowercase letters, numbers, dashes, and underscores");
|
|
667
715
|
}
|
|
668
716
|
}
|
|
669
|
-
const pagesDir =
|
|
717
|
+
const pagesDir = this.pagesBaseDir();
|
|
670
718
|
const folderPath = join(pagesDir, trimmed);
|
|
671
719
|
if (existsSync(folderPath)) {
|
|
672
720
|
throw new Error("Folder already exists");
|
|
@@ -684,12 +732,13 @@ var PageService = class {
|
|
|
684
732
|
*/
|
|
685
733
|
async movePage(pagePath, newFolder) {
|
|
686
734
|
const { rename } = await import("fs/promises");
|
|
687
|
-
const pagesDir =
|
|
735
|
+
const pagesDir = this.pagesBaseDir();
|
|
736
|
+
const ext = this.pageExt();
|
|
688
737
|
const pageName = pagePath === "/" ? "index" : pagePath.substring(1);
|
|
689
738
|
const slashIndex = pageName.lastIndexOf("/");
|
|
690
739
|
const baseName = slashIndex >= 0 ? pageName.substring(slashIndex + 1) : pageName;
|
|
691
|
-
const sourceFile = join(pagesDir, `${pageName}
|
|
692
|
-
const targetFile = newFolder ? join(pagesDir, newFolder, `${baseName}
|
|
740
|
+
const sourceFile = join(pagesDir, `${pageName}${ext}`);
|
|
741
|
+
const targetFile = newFolder ? join(pagesDir, newFolder, `${baseName}${ext}`) : join(pagesDir, `${baseName}${ext}`);
|
|
693
742
|
if (newFolder) {
|
|
694
743
|
const targetDir = join(pagesDir, newFolder);
|
|
695
744
|
if (!existsSync(targetDir)) {
|
|
@@ -719,6 +768,60 @@ var PageService = class {
|
|
|
719
768
|
}
|
|
720
769
|
}
|
|
721
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* Rename a page (file rename + cache update).
|
|
773
|
+
*
|
|
774
|
+
* Renames the underlying .json file from `oldPath`'s filename to `newPath`'s
|
|
775
|
+
* filename, keeping the same folder. Updates the in-memory cache so the
|
|
776
|
+
* page is now keyed at `newPath`. Folder moves stay with `movePage()` —
|
|
777
|
+
* this is purely "change the URL slug of an existing page" for
|
|
778
|
+
* single-locale projects.
|
|
779
|
+
*
|
|
780
|
+
* @param oldPath - Current page path (e.g., "/about" or "/blog/post")
|
|
781
|
+
* @param newPath - New page path with same folder (e.g., "/about-us" or "/blog/new-slug")
|
|
782
|
+
* @throws {Error} If oldPath doesn't exist, newPath already exists, or paths
|
|
783
|
+
* reference different folders.
|
|
784
|
+
*/
|
|
785
|
+
async renamePage(oldPath, newPath) {
|
|
786
|
+
if (oldPath === newPath) return;
|
|
787
|
+
if (newPath === "/") {
|
|
788
|
+
throw new Error("Cannot rename a page to the index path");
|
|
789
|
+
}
|
|
790
|
+
if (!newPath.startsWith("/")) {
|
|
791
|
+
throw new Error('newPath must start with "/"');
|
|
792
|
+
}
|
|
793
|
+
const oldName = oldPath === "/" ? "index" : oldPath.substring(1);
|
|
794
|
+
const newName = newPath.substring(1);
|
|
795
|
+
const oldSlash = oldName.lastIndexOf("/");
|
|
796
|
+
const newSlash = newName.lastIndexOf("/");
|
|
797
|
+
const oldFolder = oldSlash >= 0 ? oldName.substring(0, oldSlash) : "";
|
|
798
|
+
const newFolder = newSlash >= 0 ? newName.substring(0, newSlash) : "";
|
|
799
|
+
if (oldFolder !== newFolder) {
|
|
800
|
+
throw new Error("renamePage only changes the filename; use movePage to change folder");
|
|
801
|
+
}
|
|
802
|
+
const baseSegment = newSlash >= 0 ? newName.substring(newSlash + 1) : newName;
|
|
803
|
+
if (!/^[a-z0-9][a-z0-9-_]*$/.test(baseSegment)) {
|
|
804
|
+
throw new Error("Page slug can only contain lowercase letters, numbers, dashes, and underscores");
|
|
805
|
+
}
|
|
806
|
+
const { rename } = await import("fs/promises");
|
|
807
|
+
const pagesDir = this.pagesBaseDir();
|
|
808
|
+
const ext = this.pageExt();
|
|
809
|
+
const sourceFile = join(pagesDir, `${oldName}${ext}`);
|
|
810
|
+
const targetFile = join(pagesDir, `${newName}${ext}`);
|
|
811
|
+
if (!existsSync(sourceFile)) {
|
|
812
|
+
throw new Error(`Page not found: ${oldPath}`);
|
|
813
|
+
}
|
|
814
|
+
if (existsSync(targetFile)) {
|
|
815
|
+
throw new Error(`Page already exists: ${newPath}`);
|
|
816
|
+
}
|
|
817
|
+
await rename(sourceFile, targetFile);
|
|
818
|
+
const content = this.pageCache.getContent(oldPath);
|
|
819
|
+
if (content) {
|
|
820
|
+
this.pageCache.delete(oldPath);
|
|
821
|
+
const lineMap = buildLineMap(content);
|
|
822
|
+
this.pageCache.set(newPath, content, lineMap);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
722
825
|
/**
|
|
723
826
|
* Get slug mappings for all pages
|
|
724
827
|
*
|
|
@@ -765,6 +868,7 @@ var ComponentService = class {
|
|
|
765
868
|
componentCategories = /* @__PURE__ */ new Map();
|
|
766
869
|
fs;
|
|
767
870
|
loader;
|
|
871
|
+
writer;
|
|
768
872
|
loadErrors = /* @__PURE__ */ new Map();
|
|
769
873
|
loadWarnings = [];
|
|
770
874
|
/**
|
|
@@ -772,11 +876,26 @@ var ComponentService = class {
|
|
|
772
876
|
*
|
|
773
877
|
* @param options - Optional configuration for dependency injection
|
|
774
878
|
* @param options.fs - Optional file system interface for testing
|
|
775
|
-
* @param options.loader - Optional component loader interface for testing
|
|
879
|
+
* @param options.loader - Optional component loader interface for testing (READ)
|
|
880
|
+
* @param options.writer - Optional component writer for non-JSON on-disk formats
|
|
881
|
+
* (WRITE). When omitted, the service writes JSON exactly as before — legacy
|
|
882
|
+
* projects are byte-identical. The astro bootstrap injects an AstroComponentWriter
|
|
883
|
+
* so saves emit `.astro` files under `src/components/`.
|
|
776
884
|
*/
|
|
777
885
|
constructor(options) {
|
|
778
886
|
this.fs = options?.fs;
|
|
779
887
|
this.loader = options?.loader;
|
|
888
|
+
this.writer = options?.writer;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Base components directory — `<root>/components` for JSON, or whatever the
|
|
892
|
+
* injected writer owns (e.g. `<root>/src/components` for astro). All path
|
|
893
|
+
* resolution (categories, folders, save/move/rename targets) routes through
|
|
894
|
+
* here so a single switch covers every write op.
|
|
895
|
+
* @internal
|
|
896
|
+
*/
|
|
897
|
+
componentsBaseDir() {
|
|
898
|
+
return this.writer ? this.writer.componentsDir() : projectPaths.components();
|
|
780
899
|
}
|
|
781
900
|
/**
|
|
782
901
|
* Load all components from the components directory
|
|
@@ -795,28 +914,32 @@ var ComponentService = class {
|
|
|
795
914
|
* ```
|
|
796
915
|
*/
|
|
797
916
|
async loadAllComponents() {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
917
|
+
const nextComponents = /* @__PURE__ */ new Map();
|
|
918
|
+
const nextCategories = /* @__PURE__ */ new Map();
|
|
919
|
+
const nextErrors = /* @__PURE__ */ new Map();
|
|
920
|
+
let nextWarnings = [];
|
|
802
921
|
let loadedComponents;
|
|
803
922
|
if (this.loader) {
|
|
804
923
|
loadedComponents = await this.loader.loadDirectory(projectPaths.components());
|
|
805
924
|
} else {
|
|
806
925
|
const result = await loadComponentDirectory(projectPaths.components());
|
|
807
926
|
loadedComponents = result.components;
|
|
808
|
-
|
|
927
|
+
nextWarnings = result.warnings;
|
|
809
928
|
for (const error of result.errors) {
|
|
810
|
-
|
|
929
|
+
nextErrors.set(error.componentName, error);
|
|
811
930
|
}
|
|
812
931
|
}
|
|
813
932
|
loadedComponents.forEach((value, key) => {
|
|
814
933
|
const componentWithCategory = value;
|
|
815
934
|
const category = componentWithCategory._category;
|
|
816
|
-
|
|
935
|
+
nextCategories.set(key, category);
|
|
817
936
|
const { _category, _relativePath, ...cleanDef } = componentWithCategory;
|
|
818
|
-
|
|
937
|
+
nextComponents.set(key, cleanDef);
|
|
819
938
|
});
|
|
939
|
+
this.components = nextComponents;
|
|
940
|
+
this.componentCategories = nextCategories;
|
|
941
|
+
this.loadErrors = nextErrors;
|
|
942
|
+
this.loadWarnings = nextWarnings;
|
|
820
943
|
}
|
|
821
944
|
/**
|
|
822
945
|
* Get component by name
|
|
@@ -968,10 +1091,60 @@ var ComponentService = class {
|
|
|
968
1091
|
* @internal
|
|
969
1092
|
*/
|
|
970
1093
|
getComponentDir(name, category) {
|
|
971
|
-
const componentsDir =
|
|
972
|
-
const cat =
|
|
1094
|
+
const componentsDir = this.componentsBaseDir();
|
|
1095
|
+
const cat = this.resolveComponentCategory(name, category);
|
|
973
1096
|
return cat ? join2(componentsDir, cat) : componentsDir;
|
|
974
1097
|
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Resolve which category a save should land in. Priority:
|
|
1100
|
+
* 1. Explicit caller-supplied category (including '' for root)
|
|
1101
|
+
* 2. In-memory categories cache
|
|
1102
|
+
* 3. Existing file on disk (defense against a stale/empty cache)
|
|
1103
|
+
* Falls back to root only when none of the above resolves. The disk
|
|
1104
|
+
* scan in step 3 closes the race where a save fires while
|
|
1105
|
+
* loadAllComponents has been called but hasn't rebuilt the cache yet.
|
|
1106
|
+
* @internal
|
|
1107
|
+
*/
|
|
1108
|
+
resolveComponentCategory(name, category) {
|
|
1109
|
+
if (category !== void 0) {
|
|
1110
|
+
return category || void 0;
|
|
1111
|
+
}
|
|
1112
|
+
if (this.componentCategories.has(name)) {
|
|
1113
|
+
return this.componentCategories.get(name);
|
|
1114
|
+
}
|
|
1115
|
+
return this.findComponentCategoryOnDisk(name);
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Look on disk for an existing `<name>.json` and return its category.
|
|
1119
|
+
* Returns undefined for "exists at root" or "not found anywhere" — both
|
|
1120
|
+
* map to writing at root, which is the right default.
|
|
1121
|
+
* @internal
|
|
1122
|
+
*/
|
|
1123
|
+
findComponentCategoryOnDisk(name) {
|
|
1124
|
+
const componentsDir = this.componentsBaseDir();
|
|
1125
|
+
if (!existsSync2(componentsDir)) return void 0;
|
|
1126
|
+
const ext = this.writer ? null : ".json";
|
|
1127
|
+
const existsAt = (dir) => {
|
|
1128
|
+
if (ext) return existsSync2(join2(dir, `${name}${ext}`));
|
|
1129
|
+
try {
|
|
1130
|
+
return readdirSync2(dir, { withFileTypes: true }).some(
|
|
1131
|
+
(e) => e.isFile() && (e.name === `${name}.json` || e.name === `${name}.astro`)
|
|
1132
|
+
);
|
|
1133
|
+
} catch {
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
if (existsAt(componentsDir)) return void 0;
|
|
1138
|
+
try {
|
|
1139
|
+
for (const entry of readdirSync2(componentsDir, { withFileTypes: true })) {
|
|
1140
|
+
if (entry.isDirectory() && existsAt(join2(componentsDir, entry.name))) {
|
|
1141
|
+
return entry.name;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
} catch {
|
|
1145
|
+
}
|
|
1146
|
+
return void 0;
|
|
1147
|
+
}
|
|
975
1148
|
/**
|
|
976
1149
|
* Get component JavaScript from .js file
|
|
977
1150
|
*
|
|
@@ -1029,18 +1202,23 @@ var ComponentService = class {
|
|
|
1029
1202
|
* ```
|
|
1030
1203
|
*/
|
|
1031
1204
|
async saveComponent(name, data, category) {
|
|
1032
|
-
const writeFile2 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("fs/promises")).writeFile;
|
|
1033
1205
|
const dataWithoutJS = JSON.parse(JSON.stringify(data));
|
|
1034
1206
|
if (dataWithoutJS?.component?.javascript !== void 0) {
|
|
1035
1207
|
delete dataWithoutJS.component.javascript;
|
|
1036
1208
|
}
|
|
1037
|
-
const targetCategory =
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1040
|
-
|
|
1209
|
+
const targetCategory = this.resolveComponentCategory(name, category);
|
|
1210
|
+
const componentsDir = this.componentsBaseDir();
|
|
1211
|
+
const componentDir = targetCategory ? join2(componentsDir, targetCategory) : componentsDir;
|
|
1212
|
+
if (this.writer) {
|
|
1213
|
+
await this.writer.writeComponent(componentDir, name, dataWithoutJS);
|
|
1214
|
+
} else {
|
|
1215
|
+
const writeFile2 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("fs/promises")).writeFile;
|
|
1216
|
+
if (targetCategory && !existsSync2(componentDir)) {
|
|
1217
|
+
mkdirSync2(componentDir, { recursive: true });
|
|
1218
|
+
}
|
|
1219
|
+
const filePath = join2(componentDir, `${name}.json`);
|
|
1220
|
+
await writeFile2(filePath, JSON.stringify(dataWithoutJS, null, 2), "utf-8");
|
|
1041
1221
|
}
|
|
1042
|
-
const filePath = join2(componentDir, `${name}.json`);
|
|
1043
|
-
await writeFile2(filePath, JSON.stringify(dataWithoutJS, null, 2), "utf-8");
|
|
1044
1222
|
this.components.set(name, dataWithoutJS);
|
|
1045
1223
|
this.componentCategories.set(name, targetCategory);
|
|
1046
1224
|
}
|
|
@@ -1062,8 +1240,15 @@ var ComponentService = class {
|
|
|
1062
1240
|
* ```
|
|
1063
1241
|
*/
|
|
1064
1242
|
async saveComponentJavaScript(name, javascript) {
|
|
1065
|
-
const writeFile2 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("fs/promises")).writeFile;
|
|
1066
1243
|
const componentDir = this.getComponentDir(name);
|
|
1244
|
+
if (this.writer) {
|
|
1245
|
+
const current = this.components.get(name);
|
|
1246
|
+
await this.writer.writeJavaScript(componentDir, name, javascript || "", current);
|
|
1247
|
+
const next = current ? { ...current, component: { ...current.component, javascript: javascript || "" } } : { component: { javascript: javascript || "" } };
|
|
1248
|
+
this.components.set(name, next);
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
const writeFile2 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("fs/promises")).writeFile;
|
|
1067
1252
|
const jsFilePath = join2(componentDir, `${name}.js`);
|
|
1068
1253
|
await writeFile2(jsFilePath, javascript || "", "utf-8");
|
|
1069
1254
|
const componentPath = join2(componentDir, `${name}.json`);
|
|
@@ -1101,8 +1286,15 @@ var ComponentService = class {
|
|
|
1101
1286
|
* ```
|
|
1102
1287
|
*/
|
|
1103
1288
|
async saveComponentCSS(name, css) {
|
|
1104
|
-
const writeFile2 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("fs/promises")).writeFile;
|
|
1105
1289
|
const componentDir = this.getComponentDir(name);
|
|
1290
|
+
if (this.writer) {
|
|
1291
|
+
const current = this.components.get(name);
|
|
1292
|
+
await this.writer.writeCSS(componentDir, name, css || "", current);
|
|
1293
|
+
const next = current ? { ...current, component: { ...current.component, css: css || "" } } : { component: { css: css || "" } };
|
|
1294
|
+
this.components.set(name, next);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
const writeFile2 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("fs/promises")).writeFile;
|
|
1106
1298
|
const cssFilePath = join2(componentDir, `${name}.css`);
|
|
1107
1299
|
await writeFile2(cssFilePath, css || "", "utf-8");
|
|
1108
1300
|
const componentPath = join2(componentDir, `${name}.json`);
|
|
@@ -1137,7 +1329,7 @@ var ComponentService = class {
|
|
|
1137
1329
|
* ```
|
|
1138
1330
|
*/
|
|
1139
1331
|
getAllFolders() {
|
|
1140
|
-
const componentsDir =
|
|
1332
|
+
const componentsDir = this.componentsBaseDir();
|
|
1141
1333
|
if (!existsSync2(componentsDir)) {
|
|
1142
1334
|
return [];
|
|
1143
1335
|
}
|
|
@@ -1172,7 +1364,7 @@ var ComponentService = class {
|
|
|
1172
1364
|
if (sanitized !== folderName.trim()) {
|
|
1173
1365
|
throw new Error("Folder name can only contain lowercase letters, numbers, dashes, and underscores");
|
|
1174
1366
|
}
|
|
1175
|
-
const componentsDir =
|
|
1367
|
+
const componentsDir = this.componentsBaseDir();
|
|
1176
1368
|
const folderPath = join2(componentsDir, folderName);
|
|
1177
1369
|
if (existsSync2(folderPath)) {
|
|
1178
1370
|
throw new Error("Folder already exists");
|
|
@@ -1206,19 +1398,23 @@ var ComponentService = class {
|
|
|
1206
1398
|
if (currentCategory === targetCategory) {
|
|
1207
1399
|
return;
|
|
1208
1400
|
}
|
|
1209
|
-
const componentsDir =
|
|
1401
|
+
const componentsDir = this.componentsBaseDir();
|
|
1210
1402
|
const sourceDir = currentCategory ? join2(componentsDir, currentCategory) : componentsDir;
|
|
1211
1403
|
const targetDir = targetCategory ? join2(componentsDir, targetCategory) : componentsDir;
|
|
1212
|
-
if (
|
|
1213
|
-
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
|
|
1404
|
+
if (this.writer) {
|
|
1405
|
+
await this.writer.moveComponent(sourceDir, targetDir, name);
|
|
1406
|
+
} else {
|
|
1407
|
+
if (targetCategory && !existsSync2(targetDir)) {
|
|
1408
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
1409
|
+
}
|
|
1410
|
+
const extensions = [".json", ".js", ".css"];
|
|
1411
|
+
const { rename } = await import("fs/promises");
|
|
1412
|
+
for (const ext of extensions) {
|
|
1413
|
+
const sourcePath = join2(sourceDir, `${name}${ext}`);
|
|
1414
|
+
const targetPath = join2(targetDir, `${name}${ext}`);
|
|
1415
|
+
if (existsSync2(sourcePath)) {
|
|
1416
|
+
await rename(sourcePath, targetPath);
|
|
1417
|
+
}
|
|
1222
1418
|
}
|
|
1223
1419
|
}
|
|
1224
1420
|
this.componentCategories.set(name, targetCategory);
|
|
@@ -1232,6 +1428,112 @@ var ComponentService = class {
|
|
|
1232
1428
|
}
|
|
1233
1429
|
}
|
|
1234
1430
|
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Rename a component across the project.
|
|
1433
|
+
*
|
|
1434
|
+
* Renames the component's `.json`/`.js`/`.css` files on disk (respecting its
|
|
1435
|
+
* category folder), updates the in-memory caches, rewrites every
|
|
1436
|
+
* `{ type: "component", component: "<oldName>" }` reference inside other
|
|
1437
|
+
* component structures, and — if `pageService` is provided — also walks every
|
|
1438
|
+
* page/template and rewrites references there, saving any pages that changed.
|
|
1439
|
+
*
|
|
1440
|
+
* Matching is structural (only `node.component` string equality), not
|
|
1441
|
+
* text-based, so it never touches HTML tags, CSS classes, prop values, or
|
|
1442
|
+
* embedded markup that happen to contain the name as a substring.
|
|
1443
|
+
*
|
|
1444
|
+
* @throws {Error} If old/new name is missing, names are identical, new name
|
|
1445
|
+
* isn't a valid JS identifier, the source component doesn't exist, or the
|
|
1446
|
+
* new name is already taken.
|
|
1447
|
+
*/
|
|
1448
|
+
async renameComponent(oldName, newName, pageService) {
|
|
1449
|
+
if (!oldName || typeof oldName !== "string") {
|
|
1450
|
+
throw new Error("oldName is required");
|
|
1451
|
+
}
|
|
1452
|
+
if (!newName || typeof newName !== "string") {
|
|
1453
|
+
throw new Error("newName is required");
|
|
1454
|
+
}
|
|
1455
|
+
const trimmedNew = newName.trim();
|
|
1456
|
+
if (oldName === trimmedNew) {
|
|
1457
|
+
throw new Error("newName must differ from oldName");
|
|
1458
|
+
}
|
|
1459
|
+
if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(trimmedNew)) {
|
|
1460
|
+
throw new Error("newName must be a valid JavaScript identifier");
|
|
1461
|
+
}
|
|
1462
|
+
if (!this.components.has(oldName)) {
|
|
1463
|
+
throw new Error(`Component "${oldName}" not found`);
|
|
1464
|
+
}
|
|
1465
|
+
if (this.components.has(trimmedNew)) {
|
|
1466
|
+
throw new Error(`Component "${trimmedNew}" already exists`);
|
|
1467
|
+
}
|
|
1468
|
+
const category = this.componentCategories.get(oldName);
|
|
1469
|
+
const componentsDir = this.componentsBaseDir();
|
|
1470
|
+
const dir = category ? join2(componentsDir, category) : componentsDir;
|
|
1471
|
+
if (this.writer) {
|
|
1472
|
+
if (await this.writer.componentExists(dir, trimmedNew)) {
|
|
1473
|
+
throw new Error(`Component "${trimmedNew}" already exists in components/${category ?? ""}`);
|
|
1474
|
+
}
|
|
1475
|
+
await this.writer.moveComponent(dir, dir, oldName, trimmedNew);
|
|
1476
|
+
} else {
|
|
1477
|
+
for (const ext of [".json", ".js", ".css"]) {
|
|
1478
|
+
if (existsSync2(join2(dir, `${trimmedNew}${ext}`))) {
|
|
1479
|
+
throw new Error(`File "${trimmedNew}${ext}" already exists in components/${category ?? ""}`);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
const { rename } = await import("fs/promises");
|
|
1483
|
+
for (const ext of [".json", ".js", ".css"]) {
|
|
1484
|
+
const src = join2(dir, `${oldName}${ext}`);
|
|
1485
|
+
const dst = join2(dir, `${trimmedNew}${ext}`);
|
|
1486
|
+
if (existsSync2(src)) {
|
|
1487
|
+
await rename(src, dst);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
const def = this.components.get(oldName);
|
|
1492
|
+
this.components.delete(oldName);
|
|
1493
|
+
this.componentCategories.delete(oldName);
|
|
1494
|
+
this.components.set(trimmedNew, def);
|
|
1495
|
+
this.componentCategories.set(trimmedNew, category);
|
|
1496
|
+
let componentRefs = 0;
|
|
1497
|
+
const writeFile2 = this.fs ? this.fs.writeFile.bind(this.fs) : (await import("fs/promises")).writeFile;
|
|
1498
|
+
for (const [name, otherDef] of this.components.entries()) {
|
|
1499
|
+
if (name === trimmedNew) continue;
|
|
1500
|
+
const structure = otherDef.component?.structure;
|
|
1501
|
+
if (!structure) continue;
|
|
1502
|
+
if (rewriteComponentRefs(structure, oldName, trimmedNew)) {
|
|
1503
|
+
const otherCategory = this.componentCategories.get(name);
|
|
1504
|
+
const otherDir = otherCategory ? join2(componentsDir, otherCategory) : componentsDir;
|
|
1505
|
+
const persisted = JSON.parse(JSON.stringify(otherDef));
|
|
1506
|
+
if (persisted?.component?.javascript !== void 0) {
|
|
1507
|
+
delete persisted.component.javascript;
|
|
1508
|
+
}
|
|
1509
|
+
if (this.writer) {
|
|
1510
|
+
await this.writer.writeComponent(otherDir, name, persisted);
|
|
1511
|
+
} else {
|
|
1512
|
+
await writeFile2(join2(otherDir, `${name}.json`), JSON.stringify(persisted, null, 2), "utf-8");
|
|
1513
|
+
}
|
|
1514
|
+
componentRefs++;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
let pageRefs = 0;
|
|
1518
|
+
if (pageService) {
|
|
1519
|
+
const pagePaths = pageService.getAllPagePaths();
|
|
1520
|
+
for (const path2 of pagePaths) {
|
|
1521
|
+
const pageData = pageService.getPageData(path2);
|
|
1522
|
+
if (!pageData || !("root" in pageData) || !pageData.root) continue;
|
|
1523
|
+
if (rewriteComponentRefs(pageData.root, oldName, trimmedNew)) {
|
|
1524
|
+
await pageService.savePage(path2, pageData);
|
|
1525
|
+
pageRefs++;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
return {
|
|
1530
|
+
oldName,
|
|
1531
|
+
newName: trimmedNew,
|
|
1532
|
+
category,
|
|
1533
|
+
componentRefs,
|
|
1534
|
+
pageRefs
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1235
1537
|
};
|
|
1236
1538
|
|
|
1237
1539
|
// lib/server/services/EnumService.ts
|
|
@@ -1328,13 +1630,9 @@ var EnumService = class extends CachedConfigLoader {
|
|
|
1328
1630
|
};
|
|
1329
1631
|
var enumService = new EnumService();
|
|
1330
1632
|
|
|
1331
|
-
// lib/server/services/fileWatcherService.ts
|
|
1332
|
-
import { join as join3 } from "path";
|
|
1333
|
-
import { existsSync as existsSync4 } from "fs";
|
|
1334
|
-
|
|
1335
1633
|
// lib/server/fileWatcher.ts
|
|
1336
1634
|
import { watch, existsSync as existsSync3 } from "fs";
|
|
1337
|
-
import { basename, dirname } from "path";
|
|
1635
|
+
import { basename, dirname, join as join3 } from "path";
|
|
1338
1636
|
function attachWhenDirExists(dirPath, attach, setWatcher) {
|
|
1339
1637
|
if (existsSync3(dirPath)) {
|
|
1340
1638
|
setWatcher(attach());
|
|
@@ -1369,6 +1667,10 @@ var FileWatcher = class {
|
|
|
1369
1667
|
imagesWatcher = null;
|
|
1370
1668
|
librariesWatcher = null;
|
|
1371
1669
|
projectConfigWatcher = null;
|
|
1670
|
+
// Astro-format projects keep pages/components/CMS under src/ as .astro/.json.
|
|
1671
|
+
astroPagesWatcher = null;
|
|
1672
|
+
astroComponentsWatcher = null;
|
|
1673
|
+
astroContentWatcher = null;
|
|
1372
1674
|
/**
|
|
1373
1675
|
* Start watching components directory
|
|
1374
1676
|
* Watches both .json and .js files to detect component definition and JavaScript changes
|
|
@@ -1565,6 +1867,39 @@ var FileWatcher = class {
|
|
|
1565
1867
|
}
|
|
1566
1868
|
);
|
|
1567
1869
|
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Astro-format projects keep pages/components/CMS under src/ as .astro/.json.
|
|
1872
|
+
* The onPageChange/onComponentChange/onCMSChange callbacks are format-agnostic
|
|
1873
|
+
* (reload goes through the active provider), so file→editor live reload works
|
|
1874
|
+
* for `.astro` the same way it does for JSON. These are no-ops in JSON projects
|
|
1875
|
+
* (the src/ dirs don't exist).
|
|
1876
|
+
*/
|
|
1877
|
+
watchAstroPages(dirPath = join3(getProjectRoot(), "src", "pages")) {
|
|
1878
|
+
if (!existsSync3(dirPath)) return;
|
|
1879
|
+
this.astroPagesWatcher = watch(dirPath, { recursive: true }, async (_event, filename) => {
|
|
1880
|
+
if (filename && filename.endsWith(".astro") && !filename.includes("[")) {
|
|
1881
|
+
const pagePath = mapPageNameToPath(filename.replace(/\.astro$/, ""));
|
|
1882
|
+
if (this.callbacks.onPageChange) await this.callbacks.onPageChange(pagePath);
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
watchAstroComponents(dirPath = join3(getProjectRoot(), "src", "components")) {
|
|
1887
|
+
if (!existsSync3(dirPath)) return;
|
|
1888
|
+
this.astroComponentsWatcher = watch(dirPath, { recursive: true }, async (_event, filename) => {
|
|
1889
|
+
if (filename && filename.endsWith(".astro") && this.callbacks.onComponentChange) {
|
|
1890
|
+
await this.callbacks.onComponentChange();
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
watchAstroContent(dirPath = join3(getProjectRoot(), "src", "content")) {
|
|
1895
|
+
if (!existsSync3(dirPath)) return;
|
|
1896
|
+
this.astroContentWatcher = watch(dirPath, { recursive: true }, async (_event, filename) => {
|
|
1897
|
+
if (filename && filename.endsWith(".json") && this.callbacks.onCMSChange) {
|
|
1898
|
+
const collection = filename.split("/")[0];
|
|
1899
|
+
await this.callbacks.onCMSChange(collection);
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1568
1903
|
/**
|
|
1569
1904
|
* Start watching all directories
|
|
1570
1905
|
*/
|
|
@@ -1579,6 +1914,9 @@ var FileWatcher = class {
|
|
|
1579
1914
|
this.watchImages();
|
|
1580
1915
|
this.watchLibraries();
|
|
1581
1916
|
this.watchProjectConfig();
|
|
1917
|
+
this.watchAstroPages();
|
|
1918
|
+
this.watchAstroComponents();
|
|
1919
|
+
this.watchAstroContent();
|
|
1582
1920
|
}
|
|
1583
1921
|
/**
|
|
1584
1922
|
* Stop watching all directories
|
|
@@ -1624,6 +1962,18 @@ var FileWatcher = class {
|
|
|
1624
1962
|
this.projectConfigWatcher.close();
|
|
1625
1963
|
this.projectConfigWatcher = null;
|
|
1626
1964
|
}
|
|
1965
|
+
if (this.astroPagesWatcher) {
|
|
1966
|
+
this.astroPagesWatcher.close();
|
|
1967
|
+
this.astroPagesWatcher = null;
|
|
1968
|
+
}
|
|
1969
|
+
if (this.astroComponentsWatcher) {
|
|
1970
|
+
this.astroComponentsWatcher.close();
|
|
1971
|
+
this.astroComponentsWatcher = null;
|
|
1972
|
+
}
|
|
1973
|
+
if (this.astroContentWatcher) {
|
|
1974
|
+
this.astroContentWatcher.close();
|
|
1975
|
+
this.astroContentWatcher = null;
|
|
1976
|
+
}
|
|
1627
1977
|
}
|
|
1628
1978
|
/**
|
|
1629
1979
|
* Check if watchers are active
|
|
@@ -1656,18 +2006,8 @@ var FileWatcherService = class {
|
|
|
1656
2006
|
this.wsManager.broadcastUpdate("all");
|
|
1657
2007
|
},
|
|
1658
2008
|
onPageChange: async (pagePath) => {
|
|
1659
|
-
|
|
1660
|
-
const filePath = pageName.startsWith("templates/") ? join3(projectPaths.templates(), `${pageName.substring("templates/".length)}.json`) : join3(projectPaths.pages(), `${pageName}.json`);
|
|
1661
|
-
const content = await loadJSONFile(filePath);
|
|
1662
|
-
if (content) {
|
|
1663
|
-
const lineMap = buildLineMap(content);
|
|
1664
|
-
this.pageCache.set(pagePath, content, lineMap);
|
|
2009
|
+
if (await this.pageService.reloadPageFromDisk(pagePath)) {
|
|
1665
2010
|
this.wsManager.broadcastUpdate(pagePath);
|
|
1666
|
-
} else {
|
|
1667
|
-
if (!existsSync4(filePath)) {
|
|
1668
|
-
this.pageCache.delete(pagePath);
|
|
1669
|
-
this.wsManager.broadcastUpdate(pagePath);
|
|
1670
|
-
}
|
|
1671
2011
|
}
|
|
1672
2012
|
if (pagePath.startsWith("/templates/") && this.cmsService) {
|
|
1673
2013
|
if (this.refreshSchemasTimer !== null) {
|
|
@@ -1862,6 +2202,41 @@ function jsonResponse(data, options = {}) {
|
|
|
1862
2202
|
function errorResponse(message, status = 500) {
|
|
1863
2203
|
return jsonResponse({ error: message }, { status });
|
|
1864
2204
|
}
|
|
2205
|
+
function fnv1a(str) {
|
|
2206
|
+
let hash = 2166136261;
|
|
2207
|
+
for (let i = 0; i < str.length; i++) {
|
|
2208
|
+
hash ^= str.charCodeAt(i);
|
|
2209
|
+
hash = Math.imul(hash, 16777619);
|
|
2210
|
+
}
|
|
2211
|
+
return (hash >>> 0).toString(36);
|
|
2212
|
+
}
|
|
2213
|
+
function cachedJsonResponse(req, body, options = {}) {
|
|
2214
|
+
const corsHeaders = createCorsHeaders();
|
|
2215
|
+
const contentType = options.contentType ?? "application/json";
|
|
2216
|
+
const etag = `W/"${fnv1a(body)}"`;
|
|
2217
|
+
const ifNoneMatch = req.headers.get("if-none-match");
|
|
2218
|
+
if (ifNoneMatch && ifNoneMatch === etag) {
|
|
2219
|
+
return new Response(null, {
|
|
2220
|
+
status: 304,
|
|
2221
|
+
headers: {
|
|
2222
|
+
"ETag": etag,
|
|
2223
|
+
"Cache-Control": "no-cache",
|
|
2224
|
+
...corsHeaders
|
|
2225
|
+
}
|
|
2226
|
+
});
|
|
2227
|
+
}
|
|
2228
|
+
return new Response(body, {
|
|
2229
|
+
status: options.status ?? 200,
|
|
2230
|
+
headers: {
|
|
2231
|
+
"Content-Type": contentType,
|
|
2232
|
+
"ETag": etag,
|
|
2233
|
+
// `no-cache` (not `no-store`) — browser may keep the body but must
|
|
2234
|
+
// revalidate every time. Pairs with the 304 branch above.
|
|
2235
|
+
"Cache-Control": "no-cache",
|
|
2236
|
+
...corsHeaders
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
1865
2240
|
|
|
1866
2241
|
// lib/server/routes/api/cms.ts
|
|
1867
2242
|
function handleCollectionsRoute(cmsService) {
|
|
@@ -1958,32 +2333,21 @@ function handlePageDataRoute(url, pageService) {
|
|
|
1958
2333
|
return jsonResponse({ error: "Page not found" }, { status: 404 });
|
|
1959
2334
|
}
|
|
1960
2335
|
}
|
|
1961
|
-
function handlePageContentRoute(url, pageService) {
|
|
2336
|
+
function handlePageContentRoute(req, url, pageService) {
|
|
1962
2337
|
const page = url.searchParams.get("page") || "/";
|
|
1963
2338
|
const content = pageService.getPage(page);
|
|
1964
|
-
const corsHeaders = createCorsHeaders();
|
|
1965
2339
|
if (content) {
|
|
1966
|
-
return
|
|
1967
|
-
headers: {
|
|
1968
|
-
"Content-Type": "application/json",
|
|
1969
|
-
"Cache-Control": "no-store, max-age=0",
|
|
1970
|
-
"Pragma": "no-cache",
|
|
1971
|
-
"Expires": "0",
|
|
1972
|
-
...corsHeaders
|
|
1973
|
-
}
|
|
1974
|
-
});
|
|
1975
|
-
} else {
|
|
1976
|
-
return new Response("Page not found", {
|
|
1977
|
-
status: 404,
|
|
1978
|
-
headers: {
|
|
1979
|
-
"Content-Type": "application/json",
|
|
1980
|
-
"Cache-Control": "no-store, max-age=0",
|
|
1981
|
-
"Pragma": "no-cache",
|
|
1982
|
-
"Expires": "0",
|
|
1983
|
-
...corsHeaders
|
|
1984
|
-
}
|
|
1985
|
-
});
|
|
2340
|
+
return cachedJsonResponse(req, content);
|
|
1986
2341
|
}
|
|
2342
|
+
const corsHeaders = createCorsHeaders();
|
|
2343
|
+
return new Response("Page not found", {
|
|
2344
|
+
status: 404,
|
|
2345
|
+
headers: {
|
|
2346
|
+
"Content-Type": "application/json",
|
|
2347
|
+
"Cache-Control": "no-store, max-age=0",
|
|
2348
|
+
...corsHeaders
|
|
2349
|
+
}
|
|
2350
|
+
});
|
|
1987
2351
|
}
|
|
1988
2352
|
function handleSlugMappingsRoute(pageService) {
|
|
1989
2353
|
const mappings = pageService.getSlugMappings();
|
|
@@ -1991,7 +2355,7 @@ function handleSlugMappingsRoute(pageService) {
|
|
|
1991
2355
|
}
|
|
1992
2356
|
|
|
1993
2357
|
// lib/server/routes/api/components.ts
|
|
1994
|
-
function handleComponentsRoute(componentService) {
|
|
2358
|
+
function handleComponentsRoute(req, componentService) {
|
|
1995
2359
|
const componentsWithCategories = componentService.getAllComponentsWithCategories();
|
|
1996
2360
|
const result = {};
|
|
1997
2361
|
for (const [name, info] of Object.entries(componentsWithCategories)) {
|
|
@@ -2004,7 +2368,7 @@ function handleComponentsRoute(componentService) {
|
|
|
2004
2368
|
if (diagnostics.errors.length > 0 || diagnostics.warnings.length > 0) {
|
|
2005
2369
|
result._diagnostics = diagnostics;
|
|
2006
2370
|
}
|
|
2007
|
-
return
|
|
2371
|
+
return cachedJsonResponse(req, JSON.stringify(result));
|
|
2008
2372
|
}
|
|
2009
2373
|
function handleComponentDataRoute(url, componentService) {
|
|
2010
2374
|
const componentName = url.pathname.replace("/api/component-data/", "");
|
|
@@ -2293,13 +2657,13 @@ async function handleCoreApiRoutes(req, url, context) {
|
|
|
2293
2657
|
}
|
|
2294
2658
|
if (url.pathname === API_ROUTES.PAGE_CONTENT && req.method === "GET") {
|
|
2295
2659
|
return await handleRouteError(
|
|
2296
|
-
() => Promise.resolve(handlePageContentRoute(url, pageService)),
|
|
2660
|
+
() => Promise.resolve(handlePageContentRoute(req, url, pageService)),
|
|
2297
2661
|
"Failed to fetch page content"
|
|
2298
2662
|
);
|
|
2299
2663
|
}
|
|
2300
2664
|
if (url.pathname === API_ROUTES.COMPONENTS && req.method === "GET") {
|
|
2301
2665
|
return await handleRouteError(
|
|
2302
|
-
() => Promise.resolve(handleComponentsRoute(componentService)),
|
|
2666
|
+
() => Promise.resolve(handleComponentsRoute(req, componentService)),
|
|
2303
2667
|
"Failed to fetch components list"
|
|
2304
2668
|
);
|
|
2305
2669
|
}
|
|
@@ -2503,13 +2867,21 @@ function safeJsonForScript(data) {
|
|
|
2503
2867
|
}
|
|
2504
2868
|
function generateErrorPage(error, context, cspNonce) {
|
|
2505
2869
|
const errorInfo = extractErrorInfo(error);
|
|
2870
|
+
const friendly = toFriendlyError(errorInfo.message);
|
|
2506
2871
|
const errorMessage = escapeHtml(errorInfo.message);
|
|
2507
2872
|
const errorStack = errorInfo.stack ? escapeHtml(errorInfo.stack) : "";
|
|
2873
|
+
const renderInline = (s) => escapeHtml(s).replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
2874
|
+
const friendlyTitle = escapeHtml(friendly.title);
|
|
2875
|
+
const friendlyMessage = renderInline(friendly.friendlyMessage);
|
|
2876
|
+
const friendlyHint = friendly.hint ? renderInline(friendly.hint) : "";
|
|
2508
2877
|
const nonceAttr = cspNonce ? ` nonce="${cspNonce}"` : "";
|
|
2509
2878
|
const errorDataJson = safeJsonForScript({
|
|
2510
2879
|
type: "PREVIEW_ERROR",
|
|
2511
2880
|
error: {
|
|
2512
|
-
message:
|
|
2881
|
+
message: friendly.friendlyMessage,
|
|
2882
|
+
title: friendly.title,
|
|
2883
|
+
hint: friendly.hint,
|
|
2884
|
+
raw: errorInfo.message,
|
|
2513
2885
|
stack: errorInfo.stack,
|
|
2514
2886
|
context
|
|
2515
2887
|
}
|
|
@@ -2523,7 +2895,7 @@ function generateErrorPage(error, context, cspNonce) {
|
|
|
2523
2895
|
<meta charset="UTF-8">
|
|
2524
2896
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2525
2897
|
<title>Preview Error</title>
|
|
2526
|
-
<style>
|
|
2898
|
+
<style${nonceAttr}>
|
|
2527
2899
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
2528
2900
|
body {
|
|
2529
2901
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
@@ -2560,6 +2932,21 @@ function generateErrorPage(error, context, cspNonce) {
|
|
|
2560
2932
|
text-transform: uppercase;
|
|
2561
2933
|
letter-spacing: 0.5px;
|
|
2562
2934
|
}
|
|
2935
|
+
.error-friendly {
|
|
2936
|
+
font-size: 15px;
|
|
2937
|
+
line-height: 1.6;
|
|
2938
|
+
color: #e8e8e8;
|
|
2939
|
+
margin-bottom: 16px;
|
|
2940
|
+
}
|
|
2941
|
+
.error-friendly code {
|
|
2942
|
+
font-family: 'SF Mono', Menlo, Monaco, 'Courier New', monospace;
|
|
2943
|
+
font-size: 13px;
|
|
2944
|
+
background: #1a1a1a;
|
|
2945
|
+
border: 1px solid #444;
|
|
2946
|
+
border-radius: 4px;
|
|
2947
|
+
padding: 1px 5px;
|
|
2948
|
+
color: #ffb86b;
|
|
2949
|
+
}
|
|
2563
2950
|
.error-message {
|
|
2564
2951
|
background: #1a1a1a;
|
|
2565
2952
|
border: 1px solid #444;
|
|
@@ -2637,25 +3024,24 @@ function generateErrorPage(error, context, cspNonce) {
|
|
|
2637
3024
|
<div class="error-container">
|
|
2638
3025
|
<div class="error-header">
|
|
2639
3026
|
<span class="error-icon">\u26A0\uFE0F</span>
|
|
2640
|
-
<span class="error-title"
|
|
3027
|
+
<span class="error-title">${friendlyTitle}</span>
|
|
2641
3028
|
</div>
|
|
2642
3029
|
<div class="error-body">
|
|
2643
3030
|
${context ? `<div class="error-context">${escapeHtml(context)}</div>` : ""}
|
|
2644
|
-
<
|
|
2645
|
-
${errorStack ? `
|
|
3031
|
+
<p class="error-friendly">${friendlyMessage}</p>
|
|
2646
3032
|
<div class="error-stack">
|
|
2647
3033
|
<button class="stack-toggle" id="stackToggle">
|
|
2648
3034
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2649
3035
|
<polyline points="9 18 15 12 9 6"></polyline>
|
|
2650
3036
|
</svg>
|
|
2651
|
-
<span>Show
|
|
3037
|
+
<span>Show technical details</span>
|
|
2652
3038
|
</button>
|
|
2653
|
-
<div class="stack-content" id="stackContent">${
|
|
3039
|
+
<div class="stack-content" id="stackContent"><div class="error-message">${errorMessage}</div>${errorStack ? `
|
|
3040
|
+
${errorStack}` : ""}</div>
|
|
2654
3041
|
</div>
|
|
2655
|
-
` : ""}
|
|
2656
3042
|
</div>
|
|
2657
3043
|
<div class="error-footer">
|
|
2658
|
-
<span class="error-hint"
|
|
3044
|
+
<span class="error-hint">${friendlyHint}</span>
|
|
2659
3045
|
<button class="copy-btn" id="copyBtn">
|
|
2660
3046
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2661
3047
|
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
@@ -2683,7 +3069,7 @@ function generateErrorPage(error, context, cspNonce) {
|
|
|
2683
3069
|
stackToggle.addEventListener('click', function() {
|
|
2684
3070
|
var isVisible = stackContent.classList.toggle('visible');
|
|
2685
3071
|
stackToggle.classList.toggle('expanded', isVisible);
|
|
2686
|
-
stackToggle.querySelector('span').textContent = isVisible ? 'Hide
|
|
3072
|
+
stackToggle.querySelector('span').textContent = isVisible ? 'Hide technical details' : 'Show technical details';
|
|
2687
3073
|
});
|
|
2688
3074
|
}
|
|
2689
3075
|
|
|
@@ -2729,7 +3115,7 @@ function generateCspNonce() {
|
|
|
2729
3115
|
async function handlePageRoute(url, context, req) {
|
|
2730
3116
|
const { pageService, componentService, cmsService, injectLiveReload, isEditor, serverPort } = context;
|
|
2731
3117
|
const pagePath = url.pathname;
|
|
2732
|
-
const injectEditorAttrs = req?.headers.get(EDITOR_HEADER) === "1";
|
|
3118
|
+
const injectEditorAttrs = injectLiveReload === true || req?.headers.get(EDITOR_HEADER) === "1";
|
|
2733
3119
|
const cspNonce = generateCspNonce();
|
|
2734
3120
|
const i18nConfig = await loadI18nConfig();
|
|
2735
3121
|
const { locale, pathWithoutLocale } = parseLocaleFromPath(pagePath, i18nConfig);
|
|
@@ -2849,7 +3235,7 @@ async function handlePageRoute(url, context, req) {
|
|
|
2849
3235
|
});
|
|
2850
3236
|
}
|
|
2851
3237
|
}
|
|
2852
|
-
const pageContent = pageService.getPage(lookupPath);
|
|
3238
|
+
const pageContent = context.draftPageStore?.get(lookupPath) ?? pageService.getPage(lookupPath);
|
|
2853
3239
|
if (pageContent) {
|
|
2854
3240
|
try {
|
|
2855
3241
|
const pageData = parseJSON(pageContent);
|
|
@@ -3020,6 +3406,53 @@ async function handleRoutes(req, url, server, context) {
|
|
|
3020
3406
|
logResponseTime(startTime, req);
|
|
3021
3407
|
return void 0;
|
|
3022
3408
|
}
|
|
3409
|
+
if (url.pathname === "/__draft-page" && req.method === "POST") {
|
|
3410
|
+
const corsHeaders = { "Access-Control-Allow-Origin": "*" };
|
|
3411
|
+
const store = context.draftPageStore;
|
|
3412
|
+
const ws = context.wsManager;
|
|
3413
|
+
if (!store || !ws || ws.getClientCount() === 0) {
|
|
3414
|
+
logResponseTime(startTime, req);
|
|
3415
|
+
return new Response(null, { status: 204, headers: corsHeaders });
|
|
3416
|
+
}
|
|
3417
|
+
try {
|
|
3418
|
+
const body = await req.json();
|
|
3419
|
+
if (typeof body.path === "string" && typeof body.content === "string" && body.path.startsWith("/")) {
|
|
3420
|
+
store.set(body.path, body.content);
|
|
3421
|
+
ws.broadcastUpdate(body.path);
|
|
3422
|
+
}
|
|
3423
|
+
} catch {
|
|
3424
|
+
}
|
|
3425
|
+
logResponseTime(startTime, req);
|
|
3426
|
+
return new Response(null, { status: 204, headers: corsHeaders });
|
|
3427
|
+
}
|
|
3428
|
+
if (url.pathname === "/__draft-page/clear" && req.method === "POST") {
|
|
3429
|
+
const corsHeaders = { "Access-Control-Allow-Origin": "*" };
|
|
3430
|
+
const store = context.draftPageStore;
|
|
3431
|
+
if (store) {
|
|
3432
|
+
try {
|
|
3433
|
+
const body = await req.json();
|
|
3434
|
+
if (typeof body.path === "string") {
|
|
3435
|
+
store.clear(body.path);
|
|
3436
|
+
context.wsManager?.broadcastUpdate(body.path);
|
|
3437
|
+
}
|
|
3438
|
+
} catch {
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
logResponseTime(startTime, req);
|
|
3442
|
+
return new Response(null, { status: 204, headers: corsHeaders });
|
|
3443
|
+
}
|
|
3444
|
+
if (url.pathname === "/__draft-page/clients" && req.method === "GET") {
|
|
3445
|
+
const count = context.wsManager?.getClientCount() ?? 0;
|
|
3446
|
+
logResponseTime(startTime, req);
|
|
3447
|
+
return new Response(JSON.stringify({ count }), {
|
|
3448
|
+
status: 200,
|
|
3449
|
+
headers: {
|
|
3450
|
+
"Content-Type": "application/json",
|
|
3451
|
+
"Cache-Control": "no-store",
|
|
3452
|
+
"Access-Control-Allow-Origin": "*"
|
|
3453
|
+
}
|
|
3454
|
+
});
|
|
3455
|
+
}
|
|
3023
3456
|
const cmsContext = cmsService && cmsProvider ? { cmsService, cmsProvider } : void 0;
|
|
3024
3457
|
const apiResponse = await handleApiRoutes(
|
|
3025
3458
|
req,
|
|
@@ -3081,6 +3514,13 @@ async function handleRoutes(req, url, server, context) {
|
|
|
3081
3514
|
return response2;
|
|
3082
3515
|
}
|
|
3083
3516
|
}
|
|
3517
|
+
if (url.pathname.startsWith("/api/")) {
|
|
3518
|
+
logResponseTime(startTime, req);
|
|
3519
|
+
return new Response(
|
|
3520
|
+
JSON.stringify({ error: "Not Found", path: url.pathname }),
|
|
3521
|
+
{ status: 404, headers: { "Content-Type": "application/json" } }
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
3084
3524
|
if (url.pathname === "/" || url.pathname.startsWith("/") && !url.pathname.includes(".")) {
|
|
3085
3525
|
const response2 = await handlePageRoute(url, context, req);
|
|
3086
3526
|
logResponseTime(startTime, req);
|
|
@@ -3109,6 +3549,7 @@ async function createServer(config) {
|
|
|
3109
3549
|
wsManager,
|
|
3110
3550
|
cmsService,
|
|
3111
3551
|
cmsProvider,
|
|
3552
|
+
draftPageStore,
|
|
3112
3553
|
additionalRoutes = [],
|
|
3113
3554
|
onWSMessage,
|
|
3114
3555
|
injectLiveReload,
|
|
@@ -3124,7 +3565,9 @@ async function createServer(config) {
|
|
|
3124
3565
|
cmsService,
|
|
3125
3566
|
cmsProvider,
|
|
3126
3567
|
injectLiveReload,
|
|
3127
|
-
isEditor
|
|
3568
|
+
isEditor,
|
|
3569
|
+
wsManager,
|
|
3570
|
+
draftPageStore
|
|
3128
3571
|
};
|
|
3129
3572
|
let boundPort;
|
|
3130
3573
|
let lastError;
|
|
@@ -3210,7 +3653,7 @@ async function createServer(config) {
|
|
|
3210
3653
|
}
|
|
3211
3654
|
|
|
3212
3655
|
// lib/server/providers/fileSystemPageProvider.ts
|
|
3213
|
-
import { existsSync as
|
|
3656
|
+
import { existsSync as existsSync4, readdirSync as readdirSync3, mkdirSync as mkdirSync3, rmdirSync as rmdirSync3 } from "fs";
|
|
3214
3657
|
import { join as join4, dirname as dirname2 } from "path";
|
|
3215
3658
|
|
|
3216
3659
|
// lib/shared/utils/fileUtils.ts
|
|
@@ -3219,7 +3662,7 @@ var stripExtension = (name) => {
|
|
|
3219
3662
|
const lastDotIndex = name.lastIndexOf(".");
|
|
3220
3663
|
return lastDotIndex > 0 ? name.substring(0, lastDotIndex) : name;
|
|
3221
3664
|
};
|
|
3222
|
-
var
|
|
3665
|
+
var mapPageNameToPath2 = (pageName) => {
|
|
3223
3666
|
return pageName === "index" ? "/" : `/${pageName}`;
|
|
3224
3667
|
};
|
|
3225
3668
|
var mapPathToPageName = (path2) => {
|
|
@@ -3244,10 +3687,10 @@ var FileSystemPageProvider = class {
|
|
|
3244
3687
|
}
|
|
3245
3688
|
async loadAll() {
|
|
3246
3689
|
const pages = /* @__PURE__ */ new Map();
|
|
3247
|
-
if (
|
|
3690
|
+
if (existsSync4(this.pagesDir)) {
|
|
3248
3691
|
await this.scanDirectory(this.pagesDir, "", pages);
|
|
3249
3692
|
}
|
|
3250
|
-
if (this.templatesDir &&
|
|
3693
|
+
if (this.templatesDir && existsSync4(this.templatesDir)) {
|
|
3251
3694
|
await this.scanDirectory(this.templatesDir, "templates", pages);
|
|
3252
3695
|
}
|
|
3253
3696
|
return pages;
|
|
@@ -3266,7 +3709,7 @@ var FileSystemPageProvider = class {
|
|
|
3266
3709
|
const pageName = prefix ? `${prefix}/${stripExtension(entry.name)}` : stripExtension(entry.name);
|
|
3267
3710
|
const content = await loadJSONFile2(join4(dir, entry.name));
|
|
3268
3711
|
if (content) {
|
|
3269
|
-
const pagePath =
|
|
3712
|
+
const pagePath = mapPageNameToPath2(pageName);
|
|
3270
3713
|
pages.set(pagePath, content);
|
|
3271
3714
|
}
|
|
3272
3715
|
}),
|
|
@@ -3307,7 +3750,7 @@ var FileSystemPageProvider = class {
|
|
|
3307
3750
|
const { writeFile: writeFile2 } = await import("fs/promises");
|
|
3308
3751
|
const filePath = this.resolveFilePath(path2);
|
|
3309
3752
|
const dir = dirname2(filePath);
|
|
3310
|
-
if (!
|
|
3753
|
+
if (!existsSync4(dir)) {
|
|
3311
3754
|
mkdirSync3(dir, { recursive: true });
|
|
3312
3755
|
}
|
|
3313
3756
|
await writeFile2(filePath, content, "utf-8");
|
|
@@ -3316,7 +3759,7 @@ var FileSystemPageProvider = class {
|
|
|
3316
3759
|
const { unlink } = await import("fs/promises");
|
|
3317
3760
|
const filePath = this.resolveFilePath(path2);
|
|
3318
3761
|
const rootDir = this.resolveRootDir(path2);
|
|
3319
|
-
if (
|
|
3762
|
+
if (existsSync4(filePath)) {
|
|
3320
3763
|
await unlink(filePath);
|
|
3321
3764
|
let dir = dirname2(filePath);
|
|
3322
3765
|
while (dir !== rootDir) {
|
|
@@ -3336,7 +3779,13 @@ var FileSystemPageProvider = class {
|
|
|
3336
3779
|
}
|
|
3337
3780
|
async exists(path2) {
|
|
3338
3781
|
const filePath = this.resolveFilePath(path2);
|
|
3339
|
-
return
|
|
3782
|
+
return existsSync4(filePath);
|
|
3783
|
+
}
|
|
3784
|
+
baseDir() {
|
|
3785
|
+
return this.pagesDir;
|
|
3786
|
+
}
|
|
3787
|
+
extension() {
|
|
3788
|
+
return ".json";
|
|
3340
3789
|
}
|
|
3341
3790
|
};
|
|
3342
3791
|
|
|
@@ -3369,4 +3818,4 @@ export {
|
|
|
3369
3818
|
createServer,
|
|
3370
3819
|
FileSystemPageProvider
|
|
3371
3820
|
};
|
|
3372
|
-
//# sourceMappingURL=chunk-
|
|
3821
|
+
//# sourceMappingURL=chunk-R6XHAFBF.js.map
|