mechanica 2.0.0-alpha.7 → 2.0.0-alpha.8
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/{page-assets-BjIm0jws.js → page-assets-BCUAuOlo.js} +32 -0
- package/dist/cli.js +5 -2
- package/dist/editor.css +57 -57
- package/dist/editor.js +38 -25
- package/dist/plugin.js +45 -20
- package/dist/types/editor/lib/page-list.d.ts +2 -0
- package/dist/types/vite/dev/middleware.d.ts +5 -3
- package/dist/types/vite/dev/pages-store.d.ts +8 -1
- package/dist/types/vite/dev/query-dev.d.ts +5 -4
- package/package.json +2 -2
- package/src/cli/export.ts +1 -1
- package/src/editor/dialogs/PagesDialog.vue +8 -1
- package/src/editor/lib/page-list.ts +2 -0
- package/src/vite/dev/middleware.ts +29 -10
- package/src/vite/dev/pages-store.ts +56 -1
- package/src/vite/dev/query-dev.ts +12 -6
- package/src/vite/plugin.ts +8 -3
|
@@ -658,8 +658,40 @@ function listPages(mechDir, options = {}) {
|
|
|
658
658
|
}
|
|
659
659
|
items.push(item);
|
|
660
660
|
}
|
|
661
|
+
if (options.generated?.length) items.push(...generatedPageItems(options.generated, options, config));
|
|
661
662
|
return items.sort(comparePages);
|
|
662
663
|
}
|
|
664
|
+
/** File-less pages → list rows: one per logical path, with a `locales` union. */
|
|
665
|
+
function generatedPageItems(generated, options, config) {
|
|
666
|
+
const groups = /* @__PURE__ */ new Map();
|
|
667
|
+
for (const vp of generated) {
|
|
668
|
+
const arr = groups.get(vp.path);
|
|
669
|
+
if (arr) arr.push(vp);
|
|
670
|
+
else groups.set(vp.path, [vp]);
|
|
671
|
+
}
|
|
672
|
+
const items = [];
|
|
673
|
+
for (const [path, vps] of groups) {
|
|
674
|
+
const locales = config ? vps.map((v) => v.locale).filter(Boolean) : [];
|
|
675
|
+
if (options.locale && !locales.includes(options.locale)) continue;
|
|
676
|
+
const source = (options.locale ? vps.find((v) => v.locale === options.locale) : void 0) ?? (config ? vps.find((v) => v.locale === config.default) : void 0) ?? vps[0];
|
|
677
|
+
const embedded = {};
|
|
678
|
+
for (const dataEntry of options.data ?? []) embedded[dataEntry.id] = source.data?.[dataEntry.id] ?? {};
|
|
679
|
+
const dir = path.slice(1).split("/").slice(0, -1).join("/");
|
|
680
|
+
const item = {
|
|
681
|
+
path,
|
|
682
|
+
name: (typeof source.meta?.title === "string" ? source.meta.title : void 0) ?? path.split("/").pop() ?? path,
|
|
683
|
+
folderPath: dir === "" ? null : dir,
|
|
684
|
+
order: 0,
|
|
685
|
+
orderAfter: null,
|
|
686
|
+
draft: false,
|
|
687
|
+
generated: true,
|
|
688
|
+
...embedded
|
|
689
|
+
};
|
|
690
|
+
if (config) item.locales = config.all.filter((code) => locales.includes(code));
|
|
691
|
+
items.push(item);
|
|
692
|
+
}
|
|
693
|
+
return items;
|
|
694
|
+
}
|
|
663
695
|
/** Ordering: by folder, then explicit `orderAfter`, then `order`, then path. */
|
|
664
696
|
function comparePages(a, b) {
|
|
665
697
|
if (a.folderPath === b.folderPath) {
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as readImageManifest, C as getPagePath, I as UPLOADS_PREFIX, J as hasSharp, L as assetFileOf, N as setPageCodec, R as isDerivedAsset, T as listPages, U as updateImageManifest, Y as isRasterImage, a as toBlockMeta, d as readFolderData, f as readFoldersData, i as composedBlockMeta, m as readSiteLocaleOverride, n as blockAssetLinks, p as readSiteData, q as analyzeImageBuffer, r as buildRichTextCodec, t as BLOCKS_MANIFEST_FILE } from "./chunks/page-assets-
|
|
1
|
+
import { B as readImageManifest, C as getPagePath, I as UPLOADS_PREFIX, J as hasSharp, L as assetFileOf, N as setPageCodec, R as isDerivedAsset, T as listPages, U as updateImageManifest, Y as isRasterImage, a as toBlockMeta, d as readFolderData, f as readFoldersData, i as composedBlockMeta, m as readSiteLocaleOverride, n as blockAssetLinks, p as readSiteData, q as analyzeImageBuffer, r as buildRichTextCodec, t as BLOCKS_MANIFEST_FILE } from "./chunks/page-assets-BCUAuOlo.js";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { dirname, join, parse, relative } from "node:path";
|
|
4
4
|
import { applySeoTags, auditPageHtml, buildRobotsTxt, buildSitemap, findUnknownBlocks, generateProject, isPaginatedQuery, localePath, mergeTranslation, migrateContent, paginationVariantPath, registerFieldSchemas, resolveQueryKey, templateBlockIds, validateLinks, walkSchema, walkTree } from "mechanica-shared";
|
|
@@ -367,7 +367,10 @@ async function exportProject(cwd, ssr, options = {}) {
|
|
|
367
367
|
}
|
|
368
368
|
setPageCodec(richText);
|
|
369
369
|
const querySource = {
|
|
370
|
-
listPages: (opts) => listPages(join(cwd, ".mech"),
|
|
370
|
+
listPages: (opts) => listPages(join(cwd, ".mech"), {
|
|
371
|
+
...opts,
|
|
372
|
+
generated: ssr.generatedPages
|
|
373
|
+
}),
|
|
371
374
|
fetchJson: async ({ url, ...init }) => {
|
|
372
375
|
const res = await fetch(url, init);
|
|
373
376
|
if (!res.ok) throw new Error(`${url} responded ${res.status}`);
|
package/dist/editor.css
CHANGED
|
@@ -4802,7 +4802,7 @@ from {
|
|
|
4802
4802
|
justify-content: flex-end;
|
|
4803
4803
|
gap: 8px;
|
|
4804
4804
|
margin-top: 4px;
|
|
4805
|
-
}.mech-pages[data-v-
|
|
4805
|
+
}.mech-pages[data-v-5f70933a] {
|
|
4806
4806
|
display: flex;
|
|
4807
4807
|
align-items: stretch;
|
|
4808
4808
|
justify-content: center;
|
|
@@ -4811,7 +4811,7 @@ from {
|
|
|
4811
4811
|
max-width: 1200px;
|
|
4812
4812
|
height: min(620px, 82vh);
|
|
4813
4813
|
}
|
|
4814
|
-
.mech-pages__card[data-v-
|
|
4814
|
+
.mech-pages__card[data-v-5f70933a] {
|
|
4815
4815
|
display: flex;
|
|
4816
4816
|
flex-direction: column;
|
|
4817
4817
|
flex: 0 1 840px;
|
|
@@ -4821,20 +4821,20 @@ from {
|
|
|
4821
4821
|
box-shadow: var(--mech-shadow-dialog);
|
|
4822
4822
|
overflow: hidden;
|
|
4823
4823
|
}
|
|
4824
|
-
.mech-pages__header[data-v-
|
|
4824
|
+
.mech-pages__header[data-v-5f70933a] {
|
|
4825
4825
|
display: flex;
|
|
4826
4826
|
align-items: center;
|
|
4827
4827
|
gap: 10px;
|
|
4828
4828
|
padding: 18px 14px 12px 24px;
|
|
4829
4829
|
flex: none;
|
|
4830
4830
|
}
|
|
4831
|
-
.mech-pages__title[data-v-
|
|
4831
|
+
.mech-pages__title[data-v-5f70933a] {
|
|
4832
4832
|
margin: 0;
|
|
4833
4833
|
font-size: 16px;
|
|
4834
4834
|
font-weight: 600;
|
|
4835
4835
|
letter-spacing: -0.01em;
|
|
4836
4836
|
}
|
|
4837
|
-
.mech-pages__count[data-v-
|
|
4837
|
+
.mech-pages__count[data-v-5f70933a] {
|
|
4838
4838
|
padding: 2px 8px;
|
|
4839
4839
|
border-radius: var(--mech-radius-pill);
|
|
4840
4840
|
background: var(--mech-active);
|
|
@@ -4842,7 +4842,7 @@ from {
|
|
|
4842
4842
|
font-size: 11.5px;
|
|
4843
4843
|
font-weight: 600;
|
|
4844
4844
|
}
|
|
4845
|
-
.mech-pages__close[data-v-
|
|
4845
|
+
.mech-pages__close[data-v-5f70933a] {
|
|
4846
4846
|
display: flex;
|
|
4847
4847
|
align-items: center;
|
|
4848
4848
|
justify-content: center;
|
|
@@ -4855,25 +4855,25 @@ from {
|
|
|
4855
4855
|
border-radius: var(--mech-radius-pill);
|
|
4856
4856
|
cursor: pointer;
|
|
4857
4857
|
}
|
|
4858
|
-
.mech-pages__close .vicon[data-v-
|
|
4858
|
+
.mech-pages__close .vicon[data-v-5f70933a] {
|
|
4859
4859
|
width: 18px;
|
|
4860
4860
|
height: 18px;
|
|
4861
4861
|
}
|
|
4862
|
-
.mech-pages__close[data-v-
|
|
4862
|
+
.mech-pages__close[data-v-5f70933a]:hover {
|
|
4863
4863
|
background: var(--mech-hover);
|
|
4864
4864
|
color: var(--mech-fg);
|
|
4865
4865
|
}
|
|
4866
|
-
.mech-pages__toolbar[data-v-
|
|
4866
|
+
.mech-pages__toolbar[data-v-5f70933a] {
|
|
4867
4867
|
display: flex;
|
|
4868
4868
|
gap: 8px;
|
|
4869
4869
|
padding: 0 24px 14px;
|
|
4870
4870
|
flex: none;
|
|
4871
4871
|
}
|
|
4872
|
-
.mech-pages__search-wrap[data-v-
|
|
4872
|
+
.mech-pages__search-wrap[data-v-5f70933a] {
|
|
4873
4873
|
position: relative;
|
|
4874
4874
|
flex: 1;
|
|
4875
4875
|
}
|
|
4876
|
-
.mech-pages__search-icon[data-v-
|
|
4876
|
+
.mech-pages__search-icon[data-v-5f70933a] {
|
|
4877
4877
|
position: absolute;
|
|
4878
4878
|
left: 12px;
|
|
4879
4879
|
top: 50%;
|
|
@@ -4883,18 +4883,18 @@ from {
|
|
|
4883
4883
|
color: var(--mech-muted);
|
|
4884
4884
|
pointer-events: none;
|
|
4885
4885
|
}
|
|
4886
|
-
.mech-pages__search[data-v-
|
|
4886
|
+
.mech-pages__search[data-v-5f70933a] {
|
|
4887
4887
|
width: 100%;
|
|
4888
4888
|
padding-left: 36px;
|
|
4889
4889
|
}
|
|
4890
|
-
.mech-pages__content[data-v-
|
|
4890
|
+
.mech-pages__content[data-v-5f70933a] {
|
|
4891
4891
|
display: flex;
|
|
4892
4892
|
gap: 14px;
|
|
4893
4893
|
flex: 1;
|
|
4894
4894
|
min-height: 0;
|
|
4895
4895
|
padding: 0 24px 20px;
|
|
4896
4896
|
}
|
|
4897
|
-
.mech-pages__rail[data-v-
|
|
4897
|
+
.mech-pages__rail[data-v-5f70933a] {
|
|
4898
4898
|
flex: none;
|
|
4899
4899
|
width: 172px;
|
|
4900
4900
|
display: flex;
|
|
@@ -4903,7 +4903,7 @@ from {
|
|
|
4903
4903
|
overflow-y: auto;
|
|
4904
4904
|
padding-right: 2px;
|
|
4905
4905
|
}
|
|
4906
|
-
.mech-pages__rail-item[data-v-
|
|
4906
|
+
.mech-pages__rail-item[data-v-5f70933a] {
|
|
4907
4907
|
display: flex;
|
|
4908
4908
|
align-items: center;
|
|
4909
4909
|
gap: 8px;
|
|
@@ -4917,51 +4917,51 @@ from {
|
|
|
4917
4917
|
color: var(--mech-fg-alt);
|
|
4918
4918
|
cursor: pointer;
|
|
4919
4919
|
}
|
|
4920
|
-
.mech-pages__rail-item .vicon[data-v-
|
|
4920
|
+
.mech-pages__rail-item .vicon[data-v-5f70933a] {
|
|
4921
4921
|
flex: none;
|
|
4922
4922
|
width: 15px;
|
|
4923
4923
|
height: 15px;
|
|
4924
4924
|
color: var(--mech-muted);
|
|
4925
4925
|
}
|
|
4926
|
-
.mech-pages__rail-item[data-v-
|
|
4926
|
+
.mech-pages__rail-item[data-v-5f70933a]:hover {
|
|
4927
4927
|
background: var(--mech-hover);
|
|
4928
4928
|
}
|
|
4929
|
-
.mech-pages__rail-item.is-active[data-v-
|
|
4929
|
+
.mech-pages__rail-item.is-active[data-v-5f70933a] {
|
|
4930
4930
|
background: var(--mech-accent-soft);
|
|
4931
4931
|
color: var(--mech-accent);
|
|
4932
4932
|
font-weight: 500;
|
|
4933
4933
|
}
|
|
4934
|
-
.mech-pages__rail-item.is-active .vicon[data-v-
|
|
4934
|
+
.mech-pages__rail-item.is-active .vicon[data-v-5f70933a] {
|
|
4935
4935
|
color: var(--mech-accent);
|
|
4936
4936
|
}
|
|
4937
|
-
.mech-pages__rail-name[data-v-
|
|
4937
|
+
.mech-pages__rail-name[data-v-5f70933a] {
|
|
4938
4938
|
flex: 1;
|
|
4939
4939
|
min-width: 0;
|
|
4940
4940
|
overflow: hidden;
|
|
4941
4941
|
text-overflow: ellipsis;
|
|
4942
4942
|
white-space: nowrap;
|
|
4943
4943
|
}
|
|
4944
|
-
.mech-pages__rail-count[data-v-
|
|
4944
|
+
.mech-pages__rail-count[data-v-5f70933a] {
|
|
4945
4945
|
flex: none;
|
|
4946
4946
|
font-size: 11px;
|
|
4947
4947
|
color: var(--mech-placeholder);
|
|
4948
4948
|
}
|
|
4949
|
-
.mech-pages__rail-item.is-active .mech-pages__rail-count[data-v-
|
|
4949
|
+
.mech-pages__rail-item.is-active .mech-pages__rail-count[data-v-5f70933a] {
|
|
4950
4950
|
color: var(--mech-accent);
|
|
4951
4951
|
opacity: 0.7;
|
|
4952
4952
|
}
|
|
4953
|
-
.mech-pages__list[data-v-
|
|
4953
|
+
.mech-pages__list[data-v-5f70933a] {
|
|
4954
4954
|
flex: 1;
|
|
4955
4955
|
min-width: 0;
|
|
4956
4956
|
overflow-y: auto;
|
|
4957
4957
|
}
|
|
4958
|
-
.mech-pages__row[data-v-
|
|
4958
|
+
.mech-pages__row[data-v-5f70933a], .mech-pages__thead[data-v-5f70933a] {
|
|
4959
4959
|
display: grid;
|
|
4960
4960
|
grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) 96px;
|
|
4961
4961
|
align-items: center;
|
|
4962
4962
|
column-gap: 12px;
|
|
4963
4963
|
}
|
|
4964
|
-
.mech-pages__thead[data-v-
|
|
4964
|
+
.mech-pages__thead[data-v-5f70933a] {
|
|
4965
4965
|
position: sticky;
|
|
4966
4966
|
top: 0;
|
|
4967
4967
|
z-index: 1;
|
|
@@ -4969,14 +4969,14 @@ from {
|
|
|
4969
4969
|
background: var(--mech-bg);
|
|
4970
4970
|
box-shadow: 0 1px 0 var(--mech-border);
|
|
4971
4971
|
}
|
|
4972
|
-
.mech-pages__thead span[data-v-
|
|
4972
|
+
.mech-pages__thead span[data-v-5f70933a] {
|
|
4973
4973
|
font-size: 10.5px;
|
|
4974
4974
|
font-weight: 600;
|
|
4975
4975
|
text-transform: uppercase;
|
|
4976
4976
|
letter-spacing: 0.05em;
|
|
4977
4977
|
color: var(--mech-muted);
|
|
4978
4978
|
}
|
|
4979
|
-
.mech-pages__group[data-v-
|
|
4979
|
+
.mech-pages__group[data-v-5f70933a] {
|
|
4980
4980
|
display: flex;
|
|
4981
4981
|
align-items: center;
|
|
4982
4982
|
gap: 7px;
|
|
@@ -4995,36 +4995,36 @@ from {
|
|
|
4995
4995
|
border-radius: var(--mech-radius-sm);
|
|
4996
4996
|
cursor: pointer;
|
|
4997
4997
|
}
|
|
4998
|
-
.mech-pages__group .vicon[data-v-
|
|
4998
|
+
.mech-pages__group .vicon[data-v-5f70933a] {
|
|
4999
4999
|
width: 13px;
|
|
5000
5000
|
height: 13px;
|
|
5001
5001
|
}
|
|
5002
|
-
.mech-pages__group[data-v-
|
|
5002
|
+
.mech-pages__group[data-v-5f70933a]:hover {
|
|
5003
5003
|
color: var(--mech-fg);
|
|
5004
5004
|
}
|
|
5005
|
-
.mech-pages__row[data-v-
|
|
5005
|
+
.mech-pages__row[data-v-5f70933a] {
|
|
5006
5006
|
padding: 0 10px;
|
|
5007
5007
|
height: 38px;
|
|
5008
5008
|
border-radius: var(--mech-radius-sm);
|
|
5009
5009
|
cursor: pointer;
|
|
5010
5010
|
}
|
|
5011
|
-
.mech-pages__row.is-active[data-v-
|
|
5011
|
+
.mech-pages__row.is-active[data-v-5f70933a] {
|
|
5012
5012
|
background: var(--mech-accent-soft);
|
|
5013
5013
|
}
|
|
5014
|
-
.mech-pages__cell-name[data-v-
|
|
5014
|
+
.mech-pages__cell-name[data-v-5f70933a] {
|
|
5015
5015
|
display: flex;
|
|
5016
5016
|
align-items: center;
|
|
5017
5017
|
gap: 8px;
|
|
5018
5018
|
min-width: 0;
|
|
5019
5019
|
}
|
|
5020
|
-
.mech-pages__name[data-v-
|
|
5020
|
+
.mech-pages__name[data-v-5f70933a] {
|
|
5021
5021
|
font-size: 13.5px;
|
|
5022
5022
|
font-weight: 500;
|
|
5023
5023
|
overflow: hidden;
|
|
5024
5024
|
text-overflow: ellipsis;
|
|
5025
5025
|
white-space: nowrap;
|
|
5026
5026
|
}
|
|
5027
|
-
.mech-pages__badge[data-v-
|
|
5027
|
+
.mech-pages__badge[data-v-5f70933a] {
|
|
5028
5028
|
flex: none;
|
|
5029
5029
|
font-size: 10px;
|
|
5030
5030
|
font-weight: 600;
|
|
@@ -5036,17 +5036,17 @@ from {
|
|
|
5036
5036
|
border-radius: var(--mech-radius-pill);
|
|
5037
5037
|
padding: 1px 7px;
|
|
5038
5038
|
}
|
|
5039
|
-
.mech-pages__badge.is-draft[data-v-
|
|
5039
|
+
.mech-pages__badge.is-draft[data-v-5f70933a] {
|
|
5040
5040
|
color: var(--mech-muted);
|
|
5041
5041
|
}
|
|
5042
|
-
.mech-pages__locales[data-v-
|
|
5042
|
+
.mech-pages__locales[data-v-5f70933a] {
|
|
5043
5043
|
flex: none;
|
|
5044
5044
|
display: inline-flex;
|
|
5045
5045
|
align-items: center;
|
|
5046
5046
|
gap: 3px;
|
|
5047
5047
|
margin-left: 2px;
|
|
5048
5048
|
}
|
|
5049
|
-
.mech-pages__loc[data-v-
|
|
5049
|
+
.mech-pages__loc[data-v-5f70933a] {
|
|
5050
5050
|
min-width: 15px;
|
|
5051
5051
|
padding: 1px 4px;
|
|
5052
5052
|
border-radius: var(--mech-radius-sm);
|
|
@@ -5060,12 +5060,12 @@ from {
|
|
|
5060
5060
|
letter-spacing: 0.02em;
|
|
5061
5061
|
opacity: 0.5;
|
|
5062
5062
|
}
|
|
5063
|
-
.mech-pages__loc.is-on[data-v-
|
|
5063
|
+
.mech-pages__loc.is-on[data-v-5f70933a] {
|
|
5064
5064
|
background: var(--mech-accent-soft);
|
|
5065
5065
|
color: var(--mech-accent);
|
|
5066
5066
|
opacity: 1;
|
|
5067
5067
|
}
|
|
5068
|
-
.mech-pages__cell-path[data-v-
|
|
5068
|
+
.mech-pages__cell-path[data-v-5f70933a] {
|
|
5069
5069
|
min-width: 0;
|
|
5070
5070
|
overflow: hidden;
|
|
5071
5071
|
text-overflow: ellipsis;
|
|
@@ -5073,16 +5073,16 @@ from {
|
|
|
5073
5073
|
font-size: 12.5px;
|
|
5074
5074
|
color: var(--mech-muted);
|
|
5075
5075
|
}
|
|
5076
|
-
.mech-pages__actions[data-v-
|
|
5076
|
+
.mech-pages__actions[data-v-5f70933a] {
|
|
5077
5077
|
display: flex;
|
|
5078
5078
|
justify-content: flex-end;
|
|
5079
5079
|
gap: 1px;
|
|
5080
5080
|
opacity: 0;
|
|
5081
5081
|
}
|
|
5082
|
-
.mech-pages__row.is-active .mech-pages__actions[data-v-
|
|
5082
|
+
.mech-pages__row.is-active .mech-pages__actions[data-v-5f70933a], .mech-pages__row:hover .mech-pages__actions[data-v-5f70933a] {
|
|
5083
5083
|
opacity: 1;
|
|
5084
5084
|
}
|
|
5085
|
-
.mech-pages__actions button[data-v-
|
|
5085
|
+
.mech-pages__actions button[data-v-5f70933a] {
|
|
5086
5086
|
display: flex;
|
|
5087
5087
|
align-items: center;
|
|
5088
5088
|
justify-content: center;
|
|
@@ -5095,24 +5095,24 @@ from {
|
|
|
5095
5095
|
cursor: pointer;
|
|
5096
5096
|
line-height: 1;
|
|
5097
5097
|
}
|
|
5098
|
-
.mech-pages__actions button .vicon[data-v-
|
|
5098
|
+
.mech-pages__actions button .vicon[data-v-5f70933a] {
|
|
5099
5099
|
width: 15px;
|
|
5100
5100
|
height: 15px;
|
|
5101
5101
|
}
|
|
5102
|
-
.mech-pages__actions button[data-v-
|
|
5102
|
+
.mech-pages__actions button[data-v-5f70933a]:hover {
|
|
5103
5103
|
background: var(--mech-active);
|
|
5104
5104
|
color: var(--mech-fg);
|
|
5105
5105
|
}
|
|
5106
|
-
.mech-pages__actions button.is-danger[data-v-
|
|
5106
|
+
.mech-pages__actions button.is-danger[data-v-5f70933a]:hover {
|
|
5107
5107
|
color: var(--mech-error);
|
|
5108
5108
|
}
|
|
5109
|
-
.mech-pages__empty[data-v-
|
|
5109
|
+
.mech-pages__empty[data-v-5f70933a] {
|
|
5110
5110
|
padding: 24px 10px;
|
|
5111
5111
|
margin: 0;
|
|
5112
5112
|
color: var(--mech-muted);
|
|
5113
5113
|
font-size: 13px;
|
|
5114
5114
|
}
|
|
5115
|
-
.mech-pages__preview[data-v-
|
|
5115
|
+
.mech-pages__preview[data-v-5f70933a] {
|
|
5116
5116
|
flex: none;
|
|
5117
5117
|
align-self: flex-start;
|
|
5118
5118
|
max-height: 100%;
|
|
@@ -5124,18 +5124,18 @@ from {
|
|
|
5124
5124
|
box-shadow: var(--mech-shadow-dialog);
|
|
5125
5125
|
overflow: hidden;
|
|
5126
5126
|
}
|
|
5127
|
-
.mech-pages__preview-frame[data-v-
|
|
5127
|
+
.mech-pages__preview-frame[data-v-5f70933a] {
|
|
5128
5128
|
flex: 0 1 auto;
|
|
5129
5129
|
min-height: 0;
|
|
5130
5130
|
overflow: hidden;
|
|
5131
5131
|
background: var(--mech-app-bg);
|
|
5132
5132
|
}
|
|
5133
|
-
.mech-pages__preview-frame img[data-v-
|
|
5133
|
+
.mech-pages__preview-frame img[data-v-5f70933a] {
|
|
5134
5134
|
display: block;
|
|
5135
5135
|
width: 100%;
|
|
5136
5136
|
height: auto;
|
|
5137
5137
|
}
|
|
5138
|
-
.mech-pages__preview-empty[data-v-
|
|
5138
|
+
.mech-pages__preview-empty[data-v-5f70933a] {
|
|
5139
5139
|
display: flex;
|
|
5140
5140
|
flex-direction: column;
|
|
5141
5141
|
align-items: center;
|
|
@@ -5144,16 +5144,16 @@ from {
|
|
|
5144
5144
|
padding: 48px 16px;
|
|
5145
5145
|
color: var(--mech-muted);
|
|
5146
5146
|
}
|
|
5147
|
-
.mech-pages__preview-empty p[data-v-
|
|
5147
|
+
.mech-pages__preview-empty p[data-v-5f70933a] {
|
|
5148
5148
|
margin: 8px 0 0;
|
|
5149
5149
|
font-size: 13px;
|
|
5150
5150
|
font-weight: 500;
|
|
5151
5151
|
}
|
|
5152
|
-
.mech-pages__preview-empty code[data-v-
|
|
5152
|
+
.mech-pages__preview-empty code[data-v-5f70933a] {
|
|
5153
5153
|
font-size: 11.5px;
|
|
5154
5154
|
color: var(--mech-placeholder);
|
|
5155
5155
|
}
|
|
5156
|
-
.mech-pages__preview-monogram[data-v-
|
|
5156
|
+
.mech-pages__preview-monogram[data-v-5f70933a] {
|
|
5157
5157
|
display: flex;
|
|
5158
5158
|
align-items: center;
|
|
5159
5159
|
justify-content: center;
|
|
@@ -5164,7 +5164,7 @@ from {
|
|
|
5164
5164
|
font-size: 22px;
|
|
5165
5165
|
font-weight: 600;
|
|
5166
5166
|
}
|
|
5167
|
-
.mech-pages__preview-caption[data-v-
|
|
5167
|
+
.mech-pages__preview-caption[data-v-5f70933a] {
|
|
5168
5168
|
flex: none;
|
|
5169
5169
|
display: flex;
|
|
5170
5170
|
flex-direction: column;
|
|
@@ -5172,14 +5172,14 @@ from {
|
|
|
5172
5172
|
padding: 12px 16px 14px;
|
|
5173
5173
|
border-top: 1px solid var(--mech-border);
|
|
5174
5174
|
}
|
|
5175
|
-
.mech-pages__preview-name[data-v-
|
|
5175
|
+
.mech-pages__preview-name[data-v-5f70933a] {
|
|
5176
5176
|
font-size: 13px;
|
|
5177
5177
|
font-weight: 600;
|
|
5178
5178
|
overflow: hidden;
|
|
5179
5179
|
text-overflow: ellipsis;
|
|
5180
5180
|
white-space: nowrap;
|
|
5181
5181
|
}
|
|
5182
|
-
.mech-pages__preview-path[data-v-
|
|
5182
|
+
.mech-pages__preview-path[data-v-5f70933a] {
|
|
5183
5183
|
font-size: 11.5px;
|
|
5184
5184
|
color: var(--mech-muted);
|
|
5185
5185
|
overflow: hidden;
|
|
@@ -5187,7 +5187,7 @@ from {
|
|
|
5187
5187
|
white-space: nowrap;
|
|
5188
5188
|
}
|
|
5189
5189
|
@media (max-width: 1160px) {
|
|
5190
|
-
.mech-pages__preview[data-v-
|
|
5190
|
+
.mech-pages__preview[data-v-5f70933a] {
|
|
5191
5191
|
display: none;
|
|
5192
5192
|
}
|
|
5193
5193
|
}.mech-pagebar[data-v-a0f85ad0] {
|
package/dist/editor.js
CHANGED
|
@@ -1913,31 +1913,35 @@ var _hoisted_18 = {
|
|
|
1913
1913
|
};
|
|
1914
1914
|
var _hoisted_19 = {
|
|
1915
1915
|
key: 2,
|
|
1916
|
+
class: "mech-pages__badge"
|
|
1917
|
+
};
|
|
1918
|
+
var _hoisted_20 = {
|
|
1919
|
+
key: 3,
|
|
1916
1920
|
class: "mech-pages__locales"
|
|
1917
1921
|
};
|
|
1918
|
-
var
|
|
1919
|
-
var
|
|
1920
|
-
var _hoisted_22 = ["onClick"];
|
|
1922
|
+
var _hoisted_21 = ["title"];
|
|
1923
|
+
var _hoisted_22 = { class: "mech-pages__cell-path" };
|
|
1921
1924
|
var _hoisted_23 = ["onClick"];
|
|
1922
1925
|
var _hoisted_24 = ["onClick"];
|
|
1923
|
-
var _hoisted_25 =
|
|
1926
|
+
var _hoisted_25 = ["onClick"];
|
|
1927
|
+
var _hoisted_26 = {
|
|
1924
1928
|
key: 0,
|
|
1925
1929
|
class: "mech-pages__empty"
|
|
1926
1930
|
};
|
|
1927
|
-
var
|
|
1928
|
-
var
|
|
1929
|
-
var
|
|
1930
|
-
var
|
|
1931
|
+
var _hoisted_27 = { class: "mech-pages__preview" };
|
|
1932
|
+
var _hoisted_28 = { class: "mech-pages__preview-frame" };
|
|
1933
|
+
var _hoisted_29 = ["src", "data-path"];
|
|
1934
|
+
var _hoisted_30 = {
|
|
1931
1935
|
key: 1,
|
|
1932
1936
|
class: "mech-pages__preview-empty"
|
|
1933
1937
|
};
|
|
1934
|
-
var
|
|
1935
|
-
var
|
|
1938
|
+
var _hoisted_31 = { class: "mech-pages__preview-monogram" };
|
|
1939
|
+
var _hoisted_32 = {
|
|
1936
1940
|
key: 0,
|
|
1937
1941
|
class: "mech-pages__preview-caption"
|
|
1938
1942
|
};
|
|
1939
|
-
var
|
|
1940
|
-
var
|
|
1943
|
+
var _hoisted_33 = { class: "mech-pages__preview-name" };
|
|
1944
|
+
var _hoisted_34 = { class: "mech-pages__preview-path" };
|
|
1941
1945
|
//#endregion
|
|
1942
1946
|
//#region src/editor/dialogs/PagesDialog.vue
|
|
1943
1947
|
var PagesDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
@@ -2035,6 +2039,13 @@ var PagesDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
|
|
|
2035
2039
|
function onRowMenu(event, page) {
|
|
2036
2040
|
if (!contextMenu) return;
|
|
2037
2041
|
activePath.value = page.path;
|
|
2042
|
+
if (page.generated) {
|
|
2043
|
+
contextMenu.openAt(event, [{
|
|
2044
|
+
label: "Open",
|
|
2045
|
+
onClick: () => open(page)
|
|
2046
|
+
}]);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2038
2049
|
contextMenu.openAt(event, [
|
|
2039
2050
|
{
|
|
2040
2051
|
label: "Open",
|
|
@@ -2199,17 +2210,19 @@ var PagesDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
|
|
|
2199
2210
|
createElementVNode("span", _hoisted_15, [
|
|
2200
2211
|
createElementVNode("span", _hoisted_16, toDisplayString(page.name || page.path), 1),
|
|
2201
2212
|
page.draft ? (openBlock(), createElementBlock("span", _hoisted_17, "Draft")) : createCommentVNode("", true),
|
|
2202
|
-
page.
|
|
2203
|
-
|
|
2213
|
+
page.generated ? (openBlock(), createElementBlock("span", _hoisted_18, "Generated")) : createCommentVNode("", true),
|
|
2214
|
+
page.path === currentLogical.value ? (openBlock(), createElementBlock("span", _hoisted_19, "Current")) : createCommentVNode("", true),
|
|
2215
|
+
unref(localeConfig) && page.locales ? (openBlock(), createElementBlock("span", _hoisted_20, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(localeCodes), (code) => {
|
|
2204
2216
|
return openBlock(), createElementBlock("span", {
|
|
2205
2217
|
key: code,
|
|
2206
2218
|
class: normalizeClass(["mech-pages__loc", { "is-on": page.locales.includes(code) }]),
|
|
2207
2219
|
title: `${localeName(code)}: ${page.locales.includes(code) ? "translated" : "not translated"}`
|
|
2208
|
-
}, toDisplayString(code), 11,
|
|
2220
|
+
}, toDisplayString(code), 11, _hoisted_21);
|
|
2209
2221
|
}), 128))])) : createCommentVNode("", true)
|
|
2210
2222
|
]),
|
|
2211
|
-
createElementVNode("span",
|
|
2212
|
-
|
|
2223
|
+
createElementVNode("span", _hoisted_22, toDisplayString(page.path), 1),
|
|
2224
|
+
!page.generated ? (openBlock(), createElementBlock("span", {
|
|
2225
|
+
key: 0,
|
|
2213
2226
|
class: "mech-pages__actions",
|
|
2214
2227
|
onClick: _cache[3] || (_cache[3] = withModifiers(() => {}, ["stop"]))
|
|
2215
2228
|
}, [
|
|
@@ -2217,34 +2230,34 @@ var PagesDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
|
|
|
2217
2230
|
type: "button",
|
|
2218
2231
|
title: "Rename",
|
|
2219
2232
|
onClick: ($event) => startEdit(page)
|
|
2220
|
-
}, [createVNode(VIcon_default, { name: "pencil" })], 8,
|
|
2233
|
+
}, [createVNode(VIcon_default, { name: "pencil" })], 8, _hoisted_23),
|
|
2221
2234
|
createElementVNode("button", {
|
|
2222
2235
|
type: "button",
|
|
2223
2236
|
title: "Duplicate",
|
|
2224
2237
|
onClick: ($event) => startDuplicate(page)
|
|
2225
|
-
}, [createVNode(VIcon_default, { name: "copy" })], 8,
|
|
2238
|
+
}, [createVNode(VIcon_default, { name: "copy" })], 8, _hoisted_24),
|
|
2226
2239
|
createElementVNode("button", {
|
|
2227
2240
|
type: "button",
|
|
2228
2241
|
title: "Delete",
|
|
2229
2242
|
class: "is-danger",
|
|
2230
2243
|
onClick: ($event) => confirmRemove(page)
|
|
2231
|
-
}, [createVNode(VIcon_default, { name: "trash" })], 8,
|
|
2232
|
-
])
|
|
2244
|
+
}, [createVNode(VIcon_default, { name: "trash" })], 8, _hoisted_25)
|
|
2245
|
+
])) : createCommentVNode("", true)
|
|
2233
2246
|
], 42, _hoisted_14);
|
|
2234
2247
|
}), 128))], 64);
|
|
2235
2248
|
}), 128)),
|
|
2236
|
-
!rows.value.length ? (openBlock(), createElementBlock("p",
|
|
2249
|
+
!rows.value.length ? (openBlock(), createElementBlock("p", _hoisted_26, toDisplayString(query.value ? `No pages match “${query.value}”.` : "No pages yet."), 1)) : createCommentVNode("", true)
|
|
2237
2250
|
], 512)])
|
|
2238
|
-
]), createElementVNode("aside",
|
|
2251
|
+
]), createElementVNode("aside", _hoisted_27, [createElementVNode("div", _hoisted_28, [previewPage.value && !thumbFailed.has(previewPage.value.path) ? (openBlock(), createElementBlock("img", {
|
|
2239
2252
|
key: previewPage.value.path,
|
|
2240
2253
|
src: thumbSrc(previewPage.value),
|
|
2241
2254
|
"data-path": previewPage.value.path,
|
|
2242
2255
|
alt: "",
|
|
2243
2256
|
onError: onThumbError
|
|
2244
|
-
}, null, 40,
|
|
2257
|
+
}, null, 40, _hoisted_29)) : (openBlock(), createElementBlock("div", _hoisted_30, [createElementVNode("span", _hoisted_31, toDisplayString(monogram.value), 1), previewPage.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[8] || (_cache[8] = createElementVNode("p", null, "No preview yet", -1)), _cache[9] || (_cache[9] = createElementVNode("code", null, "mechanica thumbs", -1))], 64)) : createCommentVNode("", true)]))]), previewPage.value ? (openBlock(), createElementBlock("div", _hoisted_32, [createElementVNode("span", _hoisted_33, toDisplayString(previewPage.value.name || previewPage.value.path), 1), createElementVNode("span", _hoisted_34, toDisplayString(previewPage.value.path), 1)])) : createCommentVNode("", true)])]);
|
|
2245
2258
|
};
|
|
2246
2259
|
}
|
|
2247
|
-
}), [["__scopeId", "data-v-
|
|
2260
|
+
}), [["__scopeId", "data-v-5f70933a"]]);
|
|
2248
2261
|
//#endregion
|
|
2249
2262
|
//#region src/editor/components/PageBar.vue?vue&type=script&setup=true&lang.ts
|
|
2250
2263
|
var _hoisted_1$3 = { class: "mech-pagebar__icon" };
|
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as renamePage, D as pageUrlOf, E as movePage, F as translationsOf, G as wasRecentlyMutated, H as saveUpload, K as writeFileAtomic, M as setPageBlocks, N as setPageCodec, O as pageVersion, P as setPageDraft, S as fillImageMeta, T as listPages, V as saveDerivedAsset, W as markMutated, _ as createTranslation, a as toBlockMeta, b as duplicatePage, c as mergeLocaleFolderData, d as readFolderData, g as createPage, h as PageExistsError, i as composedBlockMeta, j as savePage, k as readPage, l as mergeLocaleSiteData, o as folderOf, p as readSiteData, r as buildRichTextCodec, s as mergeFolderData, t as BLOCKS_MANIFEST_FILE, u as mergeSiteData, v as deletePage, w as listFolders, x as fillContentDefaults, y as deleteTranslation, z as listImages } from "./chunks/page-assets-
|
|
1
|
+
import { A as renamePage, D as pageUrlOf, E as movePage, F as translationsOf, G as wasRecentlyMutated, H as saveUpload, K as writeFileAtomic, M as setPageBlocks, N as setPageCodec, O as pageVersion, P as setPageDraft, S as fillImageMeta, T as listPages, V as saveDerivedAsset, W as markMutated, _ as createTranslation, a as toBlockMeta, b as duplicatePage, c as mergeLocaleFolderData, d as readFolderData, g as createPage, h as PageExistsError, i as composedBlockMeta, j as savePage, k as readPage, l as mergeLocaleSiteData, o as folderOf, p as readSiteData, r as buildRichTextCodec, s as mergeFolderData, t as BLOCKS_MANIFEST_FILE, u as mergeSiteData, v as deletePage, w as listFolders, x as fillContentDefaults, y as deleteTranslation, z as listImages } from "./chunks/page-assets-BCUAuOlo.js";
|
|
2
2
|
import fs, { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import { basename, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { parseVueRequest } from "@vitejs/plugin-vue";
|
|
@@ -796,11 +796,15 @@ function generateSsrEntry(options) {
|
|
|
796
796
|
/**
|
|
797
797
|
* The dev server's {@link QuerySource}: pages come live from the `.mech`
|
|
798
798
|
* store; `useFetch` URLs are fetched server-side (no CORS, matching how the
|
|
799
|
-
* export resolves them at build time).
|
|
799
|
+
* export resolves them at build time). `generated` pages (plugin `generatePages`)
|
|
800
|
+
* are merged into the page listing so `usePages()` sees them, like the export.
|
|
800
801
|
*/
|
|
801
|
-
function devQuerySource(mechDir) {
|
|
802
|
+
function devQuerySource(mechDir, generated) {
|
|
802
803
|
return {
|
|
803
|
-
listPages: (options) => listPages(mechDir,
|
|
804
|
+
listPages: (options) => listPages(mechDir, {
|
|
805
|
+
...options,
|
|
806
|
+
generated
|
|
807
|
+
}),
|
|
804
808
|
fetchJson: async ({ url, ...init }) => {
|
|
805
809
|
const res = await fetch(url, init);
|
|
806
810
|
if (!res.ok) throw new Error(`${url} responded ${res.status}`);
|
|
@@ -813,8 +817,8 @@ function devQuerySource(mechDir) {
|
|
|
813
817
|
* form `"<type>.<json-args>"` (the same encoding the runtime composables use);
|
|
814
818
|
* `context.page` selects the chunk of a paginated query.
|
|
815
819
|
*/
|
|
816
|
-
function resolveDevQuery(mechDir, key, context = {}) {
|
|
817
|
-
return resolveQueryKey(devQuerySource(mechDir), key, context);
|
|
820
|
+
function resolveDevQuery(mechDir, key, context = {}, generated) {
|
|
821
|
+
return resolveQueryKey(devQuerySource(mechDir, generated), key, context);
|
|
818
822
|
}
|
|
819
823
|
//#endregion
|
|
820
824
|
//#region src/vite/dev/page-state.ts
|
|
@@ -1082,6 +1086,15 @@ function createDevMiddleware(mechDir, options = {}) {
|
|
|
1082
1086
|
return fs.createReadStream(file).pipe(res);
|
|
1083
1087
|
};
|
|
1084
1088
|
try {
|
|
1089
|
+
const gen = options.generated ? await options.generated() : null;
|
|
1090
|
+
/** Reject a mutation targeting a file-less generated page. */
|
|
1091
|
+
const blockGenerated = (path) => {
|
|
1092
|
+
if (path && gen?.logical.has(path)) {
|
|
1093
|
+
json({ error: "This page is generated and read-only" }, 409);
|
|
1094
|
+
return true;
|
|
1095
|
+
}
|
|
1096
|
+
return false;
|
|
1097
|
+
};
|
|
1085
1098
|
if (pathname.startsWith("/assets/")) return serveFrom("assets", "/assets/");
|
|
1086
1099
|
if (pathname.startsWith("/thumbs/")) {
|
|
1087
1100
|
res.setHeader("cache-control", "no-cache");
|
|
@@ -1095,7 +1108,7 @@ function createDevMiddleware(mechDir, options = {}) {
|
|
|
1095
1108
|
return json(await resolveDevQuery(mechDir, key, {
|
|
1096
1109
|
page: Number(query.get("page") ?? "") || void 0,
|
|
1097
1110
|
locale: variantCode(query.get("locale"))
|
|
1098
|
-
}));
|
|
1111
|
+
}, gen?.pages));
|
|
1099
1112
|
}
|
|
1100
1113
|
if (pathname === "/upload" && req.method === "POST") {
|
|
1101
1114
|
const body = await readBody(req);
|
|
@@ -1111,11 +1124,14 @@ function createDevMiddleware(mechDir, options = {}) {
|
|
|
1111
1124
|
const pathParam = query.get("path");
|
|
1112
1125
|
if (!pathParam) return json({ error: "Missing path" }, 400);
|
|
1113
1126
|
await options.ready?.();
|
|
1114
|
-
const generated =
|
|
1127
|
+
const generated = gen?.byServed.get(pathParam);
|
|
1115
1128
|
if (generated) return json(buildGeneratedState(mechDir, generated, config));
|
|
1116
1129
|
return json(buildPageState(mechDir, pathParam, config));
|
|
1117
1130
|
}
|
|
1118
|
-
if (pathname === "/pages" && req.method === "GET") return json(listPages(mechDir,
|
|
1131
|
+
if (pathname === "/pages" && req.method === "GET") return json(listPages(mechDir, {
|
|
1132
|
+
...config ? { locales: config } : {},
|
|
1133
|
+
generated: gen?.pages
|
|
1134
|
+
}));
|
|
1119
1135
|
if (pathname === "/blocks" && req.method === "GET") {
|
|
1120
1136
|
if (!options.blocks) return json({ error: "Block listing unavailable" }, 503);
|
|
1121
1137
|
return json(await options.blocks());
|
|
@@ -1123,11 +1139,13 @@ function createDevMiddleware(mechDir, options = {}) {
|
|
|
1123
1139
|
if (pathname === "/pages" && req.method === "DELETE") {
|
|
1124
1140
|
const pathParam = query.get("path");
|
|
1125
1141
|
if (!pathParam) return json({ error: "Missing path" }, 400);
|
|
1142
|
+
if (blockGenerated(pathParam)) return;
|
|
1126
1143
|
return json({ success: deletePage(mechDir, pathParam) });
|
|
1127
1144
|
}
|
|
1128
1145
|
if (pathname === "/pages/draft" && req.method === "POST") {
|
|
1129
1146
|
const pathParam = query.get("path");
|
|
1130
1147
|
if (!pathParam) return json({ error: "Missing path" }, 400);
|
|
1148
|
+
if (blockGenerated(pathParam)) return;
|
|
1131
1149
|
const body = JSON.parse((await readBody(req)).toString("utf-8"));
|
|
1132
1150
|
setPageDraft(mechDir, pathParam, body.draft === true, variantCode(query.get("locale")));
|
|
1133
1151
|
return json({
|
|
@@ -1139,6 +1157,7 @@ function createDevMiddleware(mechDir, options = {}) {
|
|
|
1139
1157
|
const pathParam = query.get("path");
|
|
1140
1158
|
const locale = query.get("locale");
|
|
1141
1159
|
if (!pathParam || !locale) return json({ error: "Missing path or locale" }, 400);
|
|
1160
|
+
if (blockGenerated(pathParam)) return;
|
|
1142
1161
|
if (!config || locale === config.default || !config.all.includes(locale)) return json({ error: "Unknown locale" }, 400);
|
|
1143
1162
|
try {
|
|
1144
1163
|
createTranslation(mechDir, pathParam, locale);
|
|
@@ -1152,11 +1171,13 @@ function createDevMiddleware(mechDir, options = {}) {
|
|
|
1152
1171
|
const pathParam = query.get("path");
|
|
1153
1172
|
const locale = query.get("locale");
|
|
1154
1173
|
if (!pathParam || !locale) return json({ error: "Missing path or locale" }, 400);
|
|
1174
|
+
if (blockGenerated(pathParam)) return;
|
|
1155
1175
|
return json({ success: deleteTranslation(mechDir, pathParam, locale) });
|
|
1156
1176
|
}
|
|
1157
1177
|
if (pathname === "/pages/duplicate" && req.method === "POST") {
|
|
1158
1178
|
const pathParam = query.get("path");
|
|
1159
1179
|
if (!pathParam) return json({ error: "Missing path" }, 400);
|
|
1180
|
+
if (blockGenerated(pathParam)) return;
|
|
1160
1181
|
const body = JSON.parse((await readBody(req)).toString("utf-8"));
|
|
1161
1182
|
if (!body.path || !body.name) return json({ error: "path and name are required" }, 400);
|
|
1162
1183
|
try {
|
|
@@ -1169,16 +1190,19 @@ function createDevMiddleware(mechDir, options = {}) {
|
|
|
1169
1190
|
if (pathname === "/pages" && req.method === "POST") {
|
|
1170
1191
|
const body = JSON.parse((await readBody(req)).toString("utf-8"));
|
|
1171
1192
|
const pathParam = query.get("path");
|
|
1172
|
-
if (pathParam)
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1193
|
+
if (pathParam) {
|
|
1194
|
+
if (blockGenerated(pathParam)) return;
|
|
1195
|
+
try {
|
|
1196
|
+
const path = typeof body.path === "string" && body.path.trim() && body.path.trim() !== pathParam ? movePage(mechDir, pathParam, body.path).path : pathParam;
|
|
1197
|
+
if (body.name) renamePage(mechDir, path, body.name);
|
|
1198
|
+
return json({
|
|
1199
|
+
success: true,
|
|
1200
|
+
path
|
|
1201
|
+
});
|
|
1202
|
+
} catch (error) {
|
|
1203
|
+
if (error instanceof PageExistsError) return json({ error: { path: "Page already exists" } }, 400);
|
|
1204
|
+
throw error;
|
|
1205
|
+
}
|
|
1182
1206
|
}
|
|
1183
1207
|
if (!body.path || !body.name) return json({ error: "path and name are required" }, 400);
|
|
1184
1208
|
if (config) {
|
|
@@ -1195,7 +1219,7 @@ function createDevMiddleware(mechDir, options = {}) {
|
|
|
1195
1219
|
if (pathname === "/save" && req.method === "POST") {
|
|
1196
1220
|
const pathParam = query.get("path");
|
|
1197
1221
|
if (!pathParam) return json({ error: "Missing path" }, 400);
|
|
1198
|
-
if ((
|
|
1222
|
+
if (blockGenerated(pathParam)) return;
|
|
1199
1223
|
const locale = variantCode(query.get("locale"));
|
|
1200
1224
|
const body = JSON.parse((await readBody(req)).toString("utf-8"));
|
|
1201
1225
|
if (!body.force && typeof body.version === "string") {
|
|
@@ -1473,6 +1497,7 @@ function mechanica(options = {}) {
|
|
|
1473
1497
|
logical.add(page.path);
|
|
1474
1498
|
}
|
|
1475
1499
|
return {
|
|
1500
|
+
pages,
|
|
1476
1501
|
byServed,
|
|
1477
1502
|
logical
|
|
1478
1503
|
};
|
|
@@ -10,6 +10,8 @@ export interface PageItem {
|
|
|
10
10
|
* sites — for the language coverage badges. Absent when i18n is off.
|
|
11
11
|
*/
|
|
12
12
|
locales?: string[];
|
|
13
|
+
/** A programmatically generated page (plugin `generatePages`) — read-only, no file. */
|
|
14
|
+
generated?: boolean;
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Fetch the project's pages from the dev server, memoized for the session so
|
|
@@ -14,11 +14,13 @@ export interface DevMiddlewareOptions {
|
|
|
14
14
|
/** The site's locale config (multi-language sites); null/absent = i18n off. */
|
|
15
15
|
locales?: LocalesConfig | null;
|
|
16
16
|
/**
|
|
17
|
-
* Generated routes (plugin `generatePages`):
|
|
18
|
-
* `/
|
|
19
|
-
*
|
|
17
|
+
* Generated routes (plugin `generatePages`): the baked page list (for the
|
|
18
|
+
* `/pages` + `/query` listings), a served-path → page map (for `/state`), and
|
|
19
|
+
* the set of their logical paths (for the read-only mutation guards). Absent
|
|
20
|
+
* when no providers are configured.
|
|
20
21
|
*/
|
|
21
22
|
generated?: () => Promise<{
|
|
23
|
+
pages: VirtualPage[];
|
|
22
24
|
byServed: Map<string, VirtualPage>;
|
|
23
25
|
logical: Set<string>;
|
|
24
26
|
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Block, type ContentBlock, type LocalesConfig } from 'mechanica-shared';
|
|
1
|
+
import { type Block, type ContentBlock, type LocalesConfig, type VirtualPage } from 'mechanica-shared';
|
|
2
2
|
import { type PageDoc, type RichTextCodec } from 'mechanica-shared/page-format';
|
|
3
3
|
/** Shape of a page file under `<mech>/pages` (the parsed `.page.md` document). */
|
|
4
4
|
export type PageFile = PageDoc;
|
|
@@ -49,6 +49,12 @@ export interface PageListItem {
|
|
|
49
49
|
* i18n is off. Translation files never get their own list row.
|
|
50
50
|
*/
|
|
51
51
|
locales?: string[];
|
|
52
|
+
/**
|
|
53
|
+
* True for a programmatically generated page ({@link VirtualPage} from the
|
|
54
|
+
* plugin's `generatePages`): it has no file, so the editor lists it read-only
|
|
55
|
+
* (no rename / duplicate / delete / translate).
|
|
56
|
+
*/
|
|
57
|
+
generated?: boolean;
|
|
52
58
|
[key: string]: unknown;
|
|
53
59
|
}
|
|
54
60
|
/** Thrown by {@link createPage} when the target page already exists. */
|
|
@@ -150,4 +156,5 @@ export declare function listPages(mechDir: string, options?: {
|
|
|
150
156
|
}[];
|
|
151
157
|
locales?: LocalesConfig;
|
|
152
158
|
locale?: string;
|
|
159
|
+
generated?: VirtualPage[];
|
|
153
160
|
}): PageListItem[];
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { type QueryContext, type QuerySource } from 'mechanica-shared';
|
|
1
|
+
import { type QueryContext, type QuerySource, type VirtualPage } from 'mechanica-shared';
|
|
2
2
|
/**
|
|
3
3
|
* The dev server's {@link QuerySource}: pages come live from the `.mech`
|
|
4
4
|
* store; `useFetch` URLs are fetched server-side (no CORS, matching how the
|
|
5
|
-
* export resolves them at build time).
|
|
5
|
+
* export resolves them at build time). `generated` pages (plugin `generatePages`)
|
|
6
|
+
* are merged into the page listing so `usePages()` sees them, like the export.
|
|
6
7
|
*/
|
|
7
|
-
export declare function devQuerySource(mechDir: string): QuerySource;
|
|
8
|
+
export declare function devQuerySource(mechDir: string, generated?: VirtualPage[]): QuerySource;
|
|
8
9
|
/**
|
|
9
10
|
* Resolve a dev-mode query key against the local `.mech` store. Keys are of the
|
|
10
11
|
* form `"<type>.<json-args>"` (the same encoding the runtime composables use);
|
|
11
12
|
* `context.page` selects the chunk of a paginated query.
|
|
12
13
|
*/
|
|
13
|
-
export declare function resolveDevQuery(mechDir: string, key: string, context?: QueryContext): Promise<unknown>;
|
|
14
|
+
export declare function resolveDevQuery(mechDir: string, key: string, context?: QueryContext, generated?: VirtualPage[]): Promise<unknown>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mechanica",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.8",
|
|
4
4
|
"description": "Build Vue 3 websites with a visual block editor",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"prepublishOnly": "bun run build"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"mechanica-shared": "2.0.0-alpha.
|
|
66
|
+
"mechanica-shared": "2.0.0-alpha.8",
|
|
67
67
|
"@vue/compiler-sfc": "^3.5.38",
|
|
68
68
|
"compact-json-schema": "^0.1.5",
|
|
69
69
|
"estree-walker": "^3.0.3",
|
package/src/cli/export.ts
CHANGED
|
@@ -474,7 +474,7 @@ export async function exportProject(
|
|
|
474
474
|
// installed so page data read by `listPages` matches what pages render with.
|
|
475
475
|
setPageCodec(richText)
|
|
476
476
|
const querySource: QuerySource = {
|
|
477
|
-
listPages: (opts) => listPages(join(cwd, '.mech'), opts),
|
|
477
|
+
listPages: (opts) => listPages(join(cwd, '.mech'), { ...opts, generated: ssr.generatedPages }),
|
|
478
478
|
fetchJson: async ({ url, ...init }) => {
|
|
479
479
|
const res = await fetch(url, init as RequestInit)
|
|
480
480
|
if (!res.ok) throw new Error(`${url} responded ${res.status}`)
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
<span class="mech-pages__cell-name">
|
|
84
84
|
<span class="mech-pages__name">{{ page.name || page.path }}</span>
|
|
85
85
|
<span v-if="page.draft" class="mech-pages__badge is-draft">Draft</span>
|
|
86
|
+
<span v-if="page.generated" class="mech-pages__badge">Generated</span>
|
|
86
87
|
<span v-if="page.path === currentLogical" class="mech-pages__badge">Current</span>
|
|
87
88
|
<span v-if="localeConfig && page.locales" class="mech-pages__locales">
|
|
88
89
|
<span
|
|
@@ -96,7 +97,8 @@
|
|
|
96
97
|
</span>
|
|
97
98
|
</span>
|
|
98
99
|
<span class="mech-pages__cell-path">{{ page.path }}</span>
|
|
99
|
-
|
|
100
|
+
<!-- Generated pages have no file — they're read-only (no edit actions). -->
|
|
101
|
+
<span v-if="!page.generated" class="mech-pages__actions" @click.stop>
|
|
100
102
|
<button type="button" title="Rename" @click="startEdit(page)"><VIcon name="pencil" /></button>
|
|
101
103
|
<button type="button" title="Duplicate" @click="startDuplicate(page)"><VIcon name="copy" /></button>
|
|
102
104
|
<button type="button" title="Delete" class="is-danger" @click="confirmRemove(page)">
|
|
@@ -275,6 +277,11 @@ const open = (page: PageItem) => openPath(page.path)
|
|
|
275
277
|
function onRowMenu(event: MouseEvent, page: PageItem) {
|
|
276
278
|
if (!contextMenu) return
|
|
277
279
|
activePath.value = page.path
|
|
280
|
+
// Generated pages have no file — offer only "Open" (read-only view).
|
|
281
|
+
if (page.generated) {
|
|
282
|
+
contextMenu.openAt(event, [{ label: 'Open', onClick: () => open(page) }])
|
|
283
|
+
return
|
|
284
|
+
}
|
|
278
285
|
contextMenu.openAt(event, [
|
|
279
286
|
{ label: 'Open', onClick: () => open(page) },
|
|
280
287
|
{ label: 'Rename', onClick: () => startEdit(page) },
|
|
@@ -10,6 +10,8 @@ export interface PageItem {
|
|
|
10
10
|
* sites — for the language coverage badges. Absent when i18n is off.
|
|
11
11
|
*/
|
|
12
12
|
locales?: string[]
|
|
13
|
+
/** A programmatically generated page (plugin `generatePages`) — read-only, no file. */
|
|
14
|
+
generated?: boolean
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
let pagesPromise: Promise<PageItem[]> | null = null
|
|
@@ -53,11 +53,12 @@ export interface DevMiddlewareOptions {
|
|
|
53
53
|
/** The site's locale config (multi-language sites); null/absent = i18n off. */
|
|
54
54
|
locales?: LocalesConfig | null
|
|
55
55
|
/**
|
|
56
|
-
* Generated routes (plugin `generatePages`):
|
|
57
|
-
* `/
|
|
58
|
-
*
|
|
56
|
+
* Generated routes (plugin `generatePages`): the baked page list (for the
|
|
57
|
+
* `/pages` + `/query` listings), a served-path → page map (for `/state`), and
|
|
58
|
+
* the set of their logical paths (for the read-only mutation guards). Absent
|
|
59
|
+
* when no providers are configured.
|
|
59
60
|
*/
|
|
60
|
-
generated?: () => Promise<{ byServed: Map<string, VirtualPage>; logical: Set<string> }>
|
|
61
|
+
generated?: () => Promise<{ pages: VirtualPage[]; byServed: Map<string, VirtualPage>; logical: Set<string> }>
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
/**
|
|
@@ -105,6 +106,18 @@ export function createDevMiddleware(
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
try {
|
|
109
|
+
// Generated pages (plugin `generatePages`), resolved once (cached
|
|
110
|
+
// upstream): used to serve their state, list them, and reject writes.
|
|
111
|
+
const gen = options.generated ? await options.generated() : null
|
|
112
|
+
/** Reject a mutation targeting a file-less generated page. */
|
|
113
|
+
const blockGenerated = (path: string | null): boolean => {
|
|
114
|
+
if (path && gen?.logical.has(path)) {
|
|
115
|
+
json({ error: 'This page is generated and read-only' }, 409)
|
|
116
|
+
return true
|
|
117
|
+
}
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
|
|
108
121
|
if (pathname.startsWith('/assets/')) return serveFrom('assets', '/assets/')
|
|
109
122
|
if (pathname.startsWith('/thumbs/')) {
|
|
110
123
|
// Regenerated in place by `mechanica thumbs` — always revalidate.
|
|
@@ -121,7 +134,9 @@ export function createDevMiddleware(
|
|
|
121
134
|
// `page` selects the chunk of a paginated query (variant URLs like /blog/2);
|
|
122
135
|
// `locale` scopes a `getPages` listing to a translation.
|
|
123
136
|
const page = Number(query.get('page') ?? '') || undefined
|
|
124
|
-
return json(
|
|
137
|
+
return json(
|
|
138
|
+
await resolveDevQuery(mechDir, key, { page, locale: variantCode(query.get('locale')) }, gen?.pages),
|
|
139
|
+
)
|
|
125
140
|
}
|
|
126
141
|
|
|
127
142
|
if (pathname === '/upload' && req.method === 'POST') {
|
|
@@ -144,13 +159,13 @@ export function createDevMiddleware(
|
|
|
144
159
|
const pathParam = query.get('path')
|
|
145
160
|
if (!pathParam) return json({ error: 'Missing path' }, 400)
|
|
146
161
|
await options.ready?.()
|
|
147
|
-
const generated =
|
|
162
|
+
const generated = gen?.byServed.get(pathParam)
|
|
148
163
|
if (generated) return json(buildGeneratedState(mechDir, generated, config))
|
|
149
164
|
return json(buildPageState(mechDir, pathParam, config))
|
|
150
165
|
}
|
|
151
166
|
|
|
152
167
|
if (pathname === '/pages' && req.method === 'GET') {
|
|
153
|
-
return json(listPages(mechDir, config ? { locales: config } : {}))
|
|
168
|
+
return json(listPages(mechDir, { ...(config ? { locales: config } : {}), generated: gen?.pages }))
|
|
154
169
|
}
|
|
155
170
|
|
|
156
171
|
if (pathname === '/blocks' && req.method === 'GET') {
|
|
@@ -161,12 +176,14 @@ export function createDevMiddleware(
|
|
|
161
176
|
if (pathname === '/pages' && req.method === 'DELETE') {
|
|
162
177
|
const pathParam = query.get('path')
|
|
163
178
|
if (!pathParam) return json({ error: 'Missing path' }, 400)
|
|
179
|
+
if (blockGenerated(pathParam)) return
|
|
164
180
|
return json({ success: deletePage(mechDir, pathParam) })
|
|
165
181
|
}
|
|
166
182
|
|
|
167
183
|
if (pathname === '/pages/draft' && req.method === 'POST') {
|
|
168
184
|
const pathParam = query.get('path')
|
|
169
185
|
if (!pathParam) return json({ error: 'Missing path' }, 400)
|
|
186
|
+
if (blockGenerated(pathParam)) return
|
|
170
187
|
const body = JSON.parse((await readBody(req)).toString('utf-8'))
|
|
171
188
|
setPageDraft(mechDir, pathParam, body.draft === true, variantCode(query.get('locale')))
|
|
172
189
|
return json({ success: true, draft: body.draft === true })
|
|
@@ -178,6 +195,7 @@ export function createDevMiddleware(
|
|
|
178
195
|
const pathParam = query.get('path')
|
|
179
196
|
const locale = query.get('locale')
|
|
180
197
|
if (!pathParam || !locale) return json({ error: 'Missing path or locale' }, 400)
|
|
198
|
+
if (blockGenerated(pathParam)) return
|
|
181
199
|
if (!config || locale === config.default || !config.all.includes(locale)) {
|
|
182
200
|
return json({ error: 'Unknown locale' }, 400)
|
|
183
201
|
}
|
|
@@ -194,12 +212,14 @@ export function createDevMiddleware(
|
|
|
194
212
|
const pathParam = query.get('path')
|
|
195
213
|
const locale = query.get('locale')
|
|
196
214
|
if (!pathParam || !locale) return json({ error: 'Missing path or locale' }, 400)
|
|
215
|
+
if (blockGenerated(pathParam)) return
|
|
197
216
|
return json({ success: deleteTranslation(mechDir, pathParam, locale) })
|
|
198
217
|
}
|
|
199
218
|
|
|
200
219
|
if (pathname === '/pages/duplicate' && req.method === 'POST') {
|
|
201
220
|
const pathParam = query.get('path')
|
|
202
221
|
if (!pathParam) return json({ error: 'Missing path' }, 400)
|
|
222
|
+
if (blockGenerated(pathParam)) return
|
|
203
223
|
const body = JSON.parse((await readBody(req)).toString('utf-8'))
|
|
204
224
|
if (!body.path || !body.name) return json({ error: 'path and name are required' }, 400)
|
|
205
225
|
try {
|
|
@@ -214,6 +234,7 @@ export function createDevMiddleware(
|
|
|
214
234
|
const body = JSON.parse((await readBody(req)).toString('utf-8'))
|
|
215
235
|
const pathParam = query.get('path')
|
|
216
236
|
if (pathParam) {
|
|
237
|
+
if (blockGenerated(pathParam)) return
|
|
217
238
|
// Editing an existing page: optionally move it (new path) and/or rename it.
|
|
218
239
|
try {
|
|
219
240
|
const wantsMove = typeof body.path === 'string' && body.path.trim() && body.path.trim() !== pathParam
|
|
@@ -248,9 +269,7 @@ export function createDevMiddleware(
|
|
|
248
269
|
if (!pathParam) return json({ error: 'Missing path' }, 400)
|
|
249
270
|
// Generated pages (plugin `generatePages`) have no file — reject a save
|
|
250
271
|
// rather than writing a stray `.page.md` at their path.
|
|
251
|
-
if ((
|
|
252
|
-
return json({ error: 'This page is generated and read-only' }, 409)
|
|
253
|
-
}
|
|
272
|
+
if (blockGenerated(pathParam)) return
|
|
254
273
|
// A non-default `locale` writes to that translation's variant file; the
|
|
255
274
|
// default locale (or i18n off) writes the base page.
|
|
256
275
|
const locale = variantCode(query.get('locale'))
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type Block,
|
|
10
10
|
type ContentBlock,
|
|
11
11
|
type LocalesConfig,
|
|
12
|
+
type VirtualPage,
|
|
12
13
|
} from 'mechanica-shared'
|
|
13
14
|
import { parsePage, serializePage, type PageDoc, type RichTextCodec } from 'mechanica-shared/page-format'
|
|
14
15
|
import { applyImageManifest, harvestImageMeta, readImageManifest, updateImageManifest } from './assets-store'
|
|
@@ -128,6 +129,12 @@ export interface PageListItem {
|
|
|
128
129
|
* i18n is off. Translation files never get their own list row.
|
|
129
130
|
*/
|
|
130
131
|
locales?: string[]
|
|
132
|
+
/**
|
|
133
|
+
* True for a programmatically generated page ({@link VirtualPage} from the
|
|
134
|
+
* plugin's `generatePages`): it has no file, so the editor lists it read-only
|
|
135
|
+
* (no rename / duplicate / delete / translate).
|
|
136
|
+
*/
|
|
137
|
+
generated?: boolean
|
|
131
138
|
[key: string]: unknown
|
|
132
139
|
}
|
|
133
140
|
|
|
@@ -451,7 +458,7 @@ export function listFolders(mechDir: string): { id: string; path: string; name:
|
|
|
451
458
|
*/
|
|
452
459
|
export function listPages(
|
|
453
460
|
mechDir: string,
|
|
454
|
-
options: { data?: { id: string }[]; locales?: LocalesConfig; locale?: string } = {},
|
|
461
|
+
options: { data?: { id: string }[]; locales?: LocalesConfig; locale?: string; generated?: VirtualPage[] } = {},
|
|
455
462
|
): PageListItem[] {
|
|
456
463
|
const pagesDir = join(mechDir, 'pages')
|
|
457
464
|
if (!fs.existsSync(pagesDir)) return []
|
|
@@ -509,9 +516,57 @@ export function listPages(
|
|
|
509
516
|
items.push(item)
|
|
510
517
|
}
|
|
511
518
|
|
|
519
|
+
// Programmatically generated pages (plugin `generatePages`) join the listing —
|
|
520
|
+
// one read-only row per logical path — so they show in the editor's page
|
|
521
|
+
// browser, resolve in `usePages()` queries, and pass `mechanica shot`'s
|
|
522
|
+
// page-exists check, just like file pages.
|
|
523
|
+
if (options.generated?.length) items.push(...generatedPageItems(options.generated, options, config))
|
|
524
|
+
|
|
512
525
|
return items.sort(comparePages)
|
|
513
526
|
}
|
|
514
527
|
|
|
528
|
+
/** File-less pages → list rows: one per logical path, with a `locales` union. */
|
|
529
|
+
function generatedPageItems(
|
|
530
|
+
generated: VirtualPage[],
|
|
531
|
+
options: { data?: { id: string }[]; locale?: string },
|
|
532
|
+
config: LocalesConfig | null,
|
|
533
|
+
): PageListItem[] {
|
|
534
|
+
const groups = new Map<string, VirtualPage[]>()
|
|
535
|
+
for (const vp of generated) {
|
|
536
|
+
const arr = groups.get(vp.path)
|
|
537
|
+
if (arr) arr.push(vp)
|
|
538
|
+
else groups.set(vp.path, [vp])
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const items: PageListItem[] = []
|
|
542
|
+
for (const [path, vps] of groups) {
|
|
543
|
+
const locales = config ? (vps.map((v) => v.locale).filter(Boolean) as string[]) : []
|
|
544
|
+
// Locale-scoped listing (a translated query): skip a page absent in it.
|
|
545
|
+
if (options.locale && !locales.includes(options.locale)) continue
|
|
546
|
+
const source =
|
|
547
|
+
(options.locale ? vps.find((v) => v.locale === options.locale) : undefined) ??
|
|
548
|
+
(config ? vps.find((v) => v.locale === config.default) : undefined) ??
|
|
549
|
+
vps[0]!
|
|
550
|
+
const embedded: Record<string, unknown> = {}
|
|
551
|
+
for (const dataEntry of options.data ?? []) embedded[dataEntry.id] = source.data?.[dataEntry.id] ?? {}
|
|
552
|
+
|
|
553
|
+
const dir = path.slice(1).split('/').slice(0, -1).join('/')
|
|
554
|
+
const item: PageListItem = {
|
|
555
|
+
path,
|
|
556
|
+
name: (typeof source.meta?.title === 'string' ? source.meta.title : undefined) ?? path.split('/').pop() ?? path,
|
|
557
|
+
folderPath: dir === '' ? null : dir,
|
|
558
|
+
order: 0,
|
|
559
|
+
orderAfter: null,
|
|
560
|
+
draft: false,
|
|
561
|
+
generated: true,
|
|
562
|
+
...embedded,
|
|
563
|
+
}
|
|
564
|
+
if (config) item.locales = config.all.filter((code) => locales.includes(code))
|
|
565
|
+
items.push(item)
|
|
566
|
+
}
|
|
567
|
+
return items
|
|
568
|
+
}
|
|
569
|
+
|
|
515
570
|
/** Ordering: by folder, then explicit `orderAfter`, then `order`, then path. */
|
|
516
571
|
function comparePages(a: PageListItem, b: PageListItem): number {
|
|
517
572
|
if (a.folderPath === b.folderPath) {
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { resolveQueryKey, type QueryContext, type QuerySource } from 'mechanica-shared'
|
|
1
|
+
import { resolveQueryKey, type QueryContext, type QuerySource, type VirtualPage } from 'mechanica-shared'
|
|
2
2
|
import { listPages } from './pages-store'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The dev server's {@link QuerySource}: pages come live from the `.mech`
|
|
6
6
|
* store; `useFetch` URLs are fetched server-side (no CORS, matching how the
|
|
7
|
-
* export resolves them at build time).
|
|
7
|
+
* export resolves them at build time). `generated` pages (plugin `generatePages`)
|
|
8
|
+
* are merged into the page listing so `usePages()` sees them, like the export.
|
|
8
9
|
*/
|
|
9
|
-
export function devQuerySource(mechDir: string): QuerySource {
|
|
10
|
+
export function devQuerySource(mechDir: string, generated?: VirtualPage[]): QuerySource {
|
|
10
11
|
return {
|
|
11
|
-
listPages: (options) => listPages(mechDir, options),
|
|
12
|
+
listPages: (options) => listPages(mechDir, { ...options, generated }),
|
|
12
13
|
fetchJson: async ({ url, ...init }) => {
|
|
13
14
|
const res = await fetch(url, init as RequestInit)
|
|
14
15
|
if (!res.ok) throw new Error(`${url} responded ${res.status}`)
|
|
@@ -22,6 +23,11 @@ export function devQuerySource(mechDir: string): QuerySource {
|
|
|
22
23
|
* form `"<type>.<json-args>"` (the same encoding the runtime composables use);
|
|
23
24
|
* `context.page` selects the chunk of a paginated query.
|
|
24
25
|
*/
|
|
25
|
-
export function resolveDevQuery(
|
|
26
|
-
|
|
26
|
+
export function resolveDevQuery(
|
|
27
|
+
mechDir: string,
|
|
28
|
+
key: string,
|
|
29
|
+
context: QueryContext = {},
|
|
30
|
+
generated?: VirtualPage[],
|
|
31
|
+
): Promise<unknown> {
|
|
32
|
+
return resolveQueryKey(devQuerySource(mechDir, generated), key, context)
|
|
27
33
|
}
|
package/src/vite/plugin.ts
CHANGED
|
@@ -165,8 +165,13 @@ export function mechanica(options: MechanicaPluginOptions = {}): Plugin {
|
|
|
165
165
|
}
|
|
166
166
|
return generatedPagesCache
|
|
167
167
|
}
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
interface GeneratedIndex {
|
|
169
|
+
pages: VirtualPage[]
|
|
170
|
+
byServed: Map<string, VirtualPage>
|
|
171
|
+
logical: Set<string>
|
|
172
|
+
}
|
|
173
|
+
let generatedIndexCache: Promise<GeneratedIndex> | null = null
|
|
174
|
+
const loadGeneratedIndex = (): Promise<GeneratedIndex> => {
|
|
170
175
|
if (!generatedIndexCache) {
|
|
171
176
|
generatedIndexCache = loadGeneratedPages().then((pages) => {
|
|
172
177
|
const byServed = new Map<string, VirtualPage>()
|
|
@@ -175,7 +180,7 @@ export function mechanica(options: MechanicaPluginOptions = {}): Plugin {
|
|
|
175
180
|
byServed.set(generatedServedPath(page, locales), page)
|
|
176
181
|
logical.add(page.path)
|
|
177
182
|
}
|
|
178
|
-
return { byServed, logical }
|
|
183
|
+
return { pages, byServed, logical }
|
|
179
184
|
})
|
|
180
185
|
}
|
|
181
186
|
return generatedIndexCache
|