@phtngyn/cms 0.1.0-beta.0 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +260 -6
- package/package.json +31 -16
- package/src/Authoring.d.ts +13 -1
- package/src/Configuration-Br7tE9Qt.d.ts +68 -0
- package/src/{AuthoringSchema-BQ5kbhVB.js → Configuration-CA_jvQKG.js} +599 -492
- package/src/Configuration-CA_jvQKG.js.map +1 -0
- package/src/Configuration.d.ts +2 -44
- package/src/Configuration.js +0 -1
- package/src/Configuration.js.map +1 -1
- package/src/{DocumentEngine-Da0kWotL.js → DocumentEngine-CgiBQU4g.js} +110 -84
- package/src/DocumentEngine-CgiBQU4g.js.map +1 -0
- package/src/DocumentModel-BB9igGC7.d.ts +34 -0
- package/src/DocumentModel.d.ts +2 -30
- package/src/DocumentModel.js +13 -3
- package/src/DocumentModel.js.map +1 -1
- package/src/Forms-BsJMev5_.d.ts +74 -0
- package/src/Forms-CfcI3tPU.js +79 -0
- package/src/Forms-CfcI3tPU.js.map +1 -0
- package/src/Forms-DxbkU3_5.js +367 -0
- package/src/Forms-DxbkU3_5.js.map +1 -0
- package/src/Forms.d.ts +35 -0
- package/src/Forms.js +13 -0
- package/src/Forms.js.map +1 -0
- package/src/HttpPaths-c9nLZ9H2.js +21 -0
- package/src/HttpPaths-c9nLZ9H2.js.map +1 -0
- package/src/I18n-DZK4CkJF.d.ts +21 -0
- package/src/I18n.d.ts +2 -0
- package/src/I18n.js +1 -0
- package/src/Module.js +78 -81
- package/src/Module.js.map +1 -1
- package/src/S3MediaObjects-JKMAWyVB.js +2664 -0
- package/src/S3MediaObjects-JKMAWyVB.js.map +1 -0
- package/src/Seed.d.ts +6 -0
- package/src/Seed.js +34 -0
- package/src/Seed.js.map +1 -0
- package/src/Seo-402YumpT.d.ts +198 -0
- package/src/{connection-ZGtL54qY.js → connection-B-iz0Y9Y.js} +5 -7
- package/src/connection-B-iz0Y9Y.js.map +1 -0
- package/src/{installation-BNKaVjni.js → installation-DgF2M1gL.js} +1162 -2147
- package/src/installation-DgF2M1gL.js.map +1 -0
- package/src/internal/authoring-catalog.js +153 -0
- package/src/internal/authoring-catalog.js.map +1 -0
- package/src/internal/authoring-compiler.js +2 -2
- package/src/internal/authoring-compiler.js.map +1 -1
- package/src/internal/authoring-metadata.js +68 -49
- package/src/internal/authoring-metadata.js.map +1 -1
- package/src/internal/cms-config.js +0 -1
- package/src/internal/cms-config.js.map +1 -1
- package/src/internal/configuration.js +25 -13
- package/src/internal/configuration.js.map +1 -1
- package/src/internal/document-model.js +66 -20
- package/src/internal/document-model.js.map +1 -1
- package/src/internal/form-capabilities.js +12 -0
- package/src/internal/form-capabilities.js.map +1 -0
- package/src/internal/i18n.js +47 -0
- package/src/internal/i18n.js.map +1 -0
- package/src/internal/studio-app.js +66 -0
- package/src/internal/studio-app.js.map +1 -0
- package/src/location-CFKTeK1K.js +35 -0
- package/src/location-CFKTeK1K.js.map +1 -0
- package/src/{metadata-Czvi3E4K.js → metadata-BEZPWh-3.js} +3 -72
- package/src/metadata-BEZPWh-3.js.map +1 -0
- package/src/{models-DS5ClMsH.js → models-BWm1pTAf.js} +9 -31
- package/src/models-BWm1pTAf.js.map +1 -0
- package/src/models-DwYioyNV.js +726 -0
- package/src/models-DwYioyNV.js.map +1 -0
- package/src/runtime/blocks.js +30 -0
- package/src/runtime/blocks.js.map +1 -0
- package/src/runtime/collaboration.js +9 -4
- package/src/runtime/collaboration.js.map +1 -1
- package/src/runtime/delivery/DocumentRenderer.js +10 -4
- package/src/runtime/delivery/DocumentRenderer.js.map +1 -1
- package/src/runtime/delivery/blocks.js +8 -0
- package/src/runtime/delivery/blocks.js.map +1 -0
- package/src/runtime/delivery/context.js +12 -0
- package/src/runtime/delivery/context.js.map +1 -0
- package/src/runtime/delivery/document-page.js +14 -12
- package/src/runtime/delivery/document-page.js.map +1 -1
- package/src/runtime/delivery/document.js +21 -3
- package/src/runtime/delivery/document.js.map +1 -1
- package/src/runtime/delivery/error.js +11 -0
- package/src/runtime/delivery/error.js.map +1 -0
- package/src/runtime/delivery/plugin.js +41 -0
- package/src/runtime/delivery/plugin.js.map +1 -0
- package/src/runtime/delivery/types.d.ts +5 -0
- package/src/runtime/delivery/useCmsDocumentSeo.d.ts +56 -0
- package/src/runtime/delivery/useCmsDocumentSeo.js +99 -0
- package/src/runtime/delivery/useCmsDocumentSeo.js.map +1 -0
- package/src/runtime/delivery/useDocument.js +5 -2
- package/src/runtime/delivery/useDocument.js.map +1 -1
- package/src/runtime/delivery/useDocumentQuery.js +5 -2
- package/src/runtime/delivery/useDocumentQuery.js.map +1 -1
- package/src/runtime/forms/document.js +38 -0
- package/src/runtime/forms/document.js.map +1 -0
- package/src/runtime/forms/useCmsForm.js +149 -0
- package/src/runtime/forms/useCmsForm.js.map +1 -0
- package/src/runtime/i18n/translator.js +22 -0
- package/src/runtime/i18n/translator.js.map +1 -0
- package/src/runtime/i18n/useCmsI18n.d.ts +9 -0
- package/src/runtime/i18n/useCmsI18n.js +29 -0
- package/src/runtime/i18n/useCmsI18n.js.map +1 -0
- package/src/runtime/installation.js +1 -1
- package/src/runtime/plugin.js +3 -3
- package/src/runtime/plugin.js.map +1 -1
- package/src/runtime/preview/comark-document.js +0 -1
- package/src/runtime/preview/comark-document.js.map +1 -1
- package/src/runtime/preview/connection.js +1 -1
- package/src/runtime/preview/controller.js +37 -11
- package/src/runtime/preview/controller.js.map +1 -1
- package/src/runtime/preview/overlay.js +65 -0
- package/src/runtime/preview/overlay.js.map +1 -0
- package/src/runtime/preview/plugin.client.js +59 -61
- package/src/runtime/preview/plugin.client.js.map +1 -1
- package/src/runtime/preview/preview.css +72 -4
- package/src/runtime/preview/protocol.js.map +1 -1
- package/src/runtime/preview/store.js +1 -1
- package/src/runtime/preview/targets.js +0 -1
- package/src/runtime/preview/targets.js.map +1 -1
- package/src/runtime/sitemap-entries.js +17 -0
- package/src/runtime/sitemap-entries.js.map +1 -0
- package/src/runtime/sitemap.js +24 -0
- package/src/runtime/sitemap.js.map +1 -0
- package/src/runtime/studio-app.js +5 -1
- package/src/runtime/studio-app.js.map +1 -1
- package/src/runtime/studio-entry.js +36 -0
- package/src/runtime/studio-entry.js.map +1 -0
- package/src/runtime/studio-navigation.js +11 -0
- package/src/runtime/studio-navigation.js.map +1 -0
- package/src/scalars-B2lgGUnj.js +26 -0
- package/src/scalars-B2lgGUnj.js.map +1 -0
- package/src/{store-Nl4uxEXx.js → store-DjJs3I7k.js} +12 -7
- package/src/store-DjJs3I7k.js.map +1 -0
- package/studio/CreateDocumentDialog-CTeIFlPb.js +256 -0
- package/studio/collaboration-DcK2F6v9.js +4566 -0
- package/studio/documentPlanning-Bfv-d7OU.js +29820 -0
- package/studio/dom-to-image-more.min-D6jGGdrW.js +877 -0
- package/studio/editor-Bzlh3zXt.js +30827 -0
- package/studio/host.css +3 -0
- package/studio/host.js +46614 -0
- package/studio/rolldown-runtime-B0aSnxlc.js +20 -0
- package/dist/studio/assets/ActivityEntryRow-D--fRvWI.js +0 -1
- package/dist/studio/assets/ActivityView-D-XBBUHb.js +0 -1
- package/dist/studio/assets/CreateDocumentDialog-CH8UH1QM.js +0 -1
- package/dist/studio/assets/DocumentsView-CxRpmAxt.js +0 -34
- package/dist/studio/assets/DropdownMenu-BSb1l7fr.js +0 -1
- package/dist/studio/assets/Input-DVao38h7.js +0 -3
- package/dist/studio/assets/MediaView-BHrPIjxB.js +0 -1
- package/dist/studio/assets/Modal-VNrXGa5C.js +0 -1
- package/dist/studio/assets/OverviewView-DfP6-mLZ.js +0 -1
- package/dist/studio/assets/Select-BJZIs9sY.js +0 -1
- package/dist/studio/assets/SettingsView-CerHe-ua.js +0 -1
- package/dist/studio/assets/Skeleton-CWJNi3nS.js +0 -1
- package/dist/studio/assets/StudioPageHeader-DP9HDAF2.js +0 -1
- package/dist/studio/assets/UsersView-cP3REkH2.js +0 -1
- package/dist/studio/assets/collaboration-BLnCzfvV.js +0 -4
- package/dist/studio/assets/context-dY4fNbn3.js +0 -1
- package/dist/studio/assets/editor-CE07wW83.js +0 -137
- package/dist/studio/assets/gateway-BAyQgCse.js +0 -10
- package/dist/studio/assets/index-CjoaPLhZ.js +0 -28
- package/dist/studio/assets/index-s2rPqCmP.css +0 -2
- package/dist/studio/assets/render-BkxbK2St.js +0 -83
- package/dist/studio/assets/rolldown-runtime-DK3Fl9T5.js +0 -1
- package/dist/studio/assets/useMediaLibrary-D7qR7aBf.js +0 -1
- package/dist/studio/index.html +0 -76
- package/src/AuthoringSchema-BQ5kbhVB.js.map +0 -1
- package/src/Configuration-BGYFD2N1.js +0 -150
- package/src/Configuration-BGYFD2N1.js.map +0 -1
- package/src/DocumentEngine-Da0kWotL.js.map +0 -1
- package/src/HttpPaths-86qLGB90.js +0 -15
- package/src/HttpPaths-86qLGB90.js.map +0 -1
- package/src/StudioHttp-BTLLooh0.js +0 -916
- package/src/StudioHttp-BTLLooh0.js.map +0 -1
- package/src/connection-ZGtL54qY.js.map +0 -1
- package/src/installation-BNKaVjni.js.map +0 -1
- package/src/internal/component-metadata.js +0 -37
- package/src/internal/component-metadata.js.map +0 -1
- package/src/metadata-Czvi3E4K.js.map +0 -1
- package/src/models-Citx8jBi.js +0 -306
- package/src/models-Citx8jBi.js.map +0 -1
- package/src/models-DS5ClMsH.js.map +0 -1
- package/src/store-Nl4uxEXx.js.map +0 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { n as formAuthoring } from "../Forms-DxbkU3_5.js";
|
|
2
|
+
import { l as validateCollectionBody, n as ComponentDefinition, t as AuthoringCatalog } from "../Configuration-CA_jvQKG.js";
|
|
3
|
+
import { resolveBlockAuthoring } from "./authoring-metadata.js";
|
|
4
|
+
import { Crypto, Effect, Encoding, FileSystem, Path, Schema } from "effect";
|
|
5
|
+
import { addTemplate, addVitePlugin, updateTemplates } from "@nuxt/kit";
|
|
6
|
+
import { createCheckerByJson } from "vue-component-meta";
|
|
7
|
+
|
|
8
|
+
//#region src/internal/authoring-catalog.ts
|
|
9
|
+
/** A complete, validated snapshot shared by Vite and Nitro through ordinary imports. */
|
|
10
|
+
const registerAuthoringCatalog = Effect.fn("CmsAuthoring.register")(function* (nuxt, documents, forms = {}) {
|
|
11
|
+
const fs = yield* FileSystem.FileSystem;
|
|
12
|
+
const path = yield* Path.Path;
|
|
13
|
+
const crypto = yield* Crypto.Crypto;
|
|
14
|
+
const run = Effect.runPromiseWith(yield* Effect.context());
|
|
15
|
+
let contents = "export default { revision: \"empty\", components: [] }\n";
|
|
16
|
+
let blockContents = "export default {}\n";
|
|
17
|
+
let discovered = [];
|
|
18
|
+
let dependencies = /* @__PURE__ */ new Set();
|
|
19
|
+
let queue = Promise.resolve();
|
|
20
|
+
const servers = /* @__PURE__ */ new Set();
|
|
21
|
+
let initialized = false;
|
|
22
|
+
let rejected = false;
|
|
23
|
+
const template = addTemplate({
|
|
24
|
+
filename: "cms/authoring.mjs",
|
|
25
|
+
write: true,
|
|
26
|
+
getContents: () => contents
|
|
27
|
+
});
|
|
28
|
+
const blocks = addTemplate({
|
|
29
|
+
filename: "cms/blocks.mjs",
|
|
30
|
+
write: true,
|
|
31
|
+
getContents: () => blockContents
|
|
32
|
+
});
|
|
33
|
+
nuxt.options.alias["#cms/blocks"] = blocks.dst;
|
|
34
|
+
nuxt.options.alias["#cms/authoring"] = template.dst;
|
|
35
|
+
nuxt.hook("nitro:config", (config) => {
|
|
36
|
+
config.externals ||= {};
|
|
37
|
+
config.externals.inline ||= [];
|
|
38
|
+
config.externals.inline.push(template.dst);
|
|
39
|
+
});
|
|
40
|
+
const compile = Effect.fn("CmsAuthoring.compile")(function* () {
|
|
41
|
+
const checker = createCheckerByJson(nuxt.options.rootDir, {
|
|
42
|
+
compilerOptions: {
|
|
43
|
+
strict: true,
|
|
44
|
+
target: "ESNext",
|
|
45
|
+
module: "ESNext",
|
|
46
|
+
moduleResolution: "Bundler",
|
|
47
|
+
baseUrl: nuxt.options.rootDir,
|
|
48
|
+
paths: Object.fromEntries(Object.entries(nuxt.options.alias).map(([key, value]) => [`${key}/*`, [`${value}/*`]]))
|
|
49
|
+
},
|
|
50
|
+
files: []
|
|
51
|
+
}, { schema: true });
|
|
52
|
+
const definitions = [];
|
|
53
|
+
const registrations = [];
|
|
54
|
+
try {
|
|
55
|
+
for (const component of discovered) {
|
|
56
|
+
if (!component.filePath.endsWith(".vue") || component.filePath.includes("/node_modules/")) continue;
|
|
57
|
+
if (!(yield* fs.exists(component.filePath))) continue;
|
|
58
|
+
const source = yield* fs.readFileString(component.filePath);
|
|
59
|
+
const form = forms[component.kebabName];
|
|
60
|
+
const definition = form ? ComponentDefinition.make({
|
|
61
|
+
name: component.kebabName,
|
|
62
|
+
authoring: formAuthoring(form)
|
|
63
|
+
}) : yield* resolveBlockAuthoring(checker, component.filePath, component.kebabName, source);
|
|
64
|
+
if (!definition) continue;
|
|
65
|
+
if (definitions.some((existing) => existing.name === definition.name)) throw new Error(`Duplicate authoring block ${definition.name}.`);
|
|
66
|
+
definitions.push(definition);
|
|
67
|
+
component.global = false;
|
|
68
|
+
registrations.push({
|
|
69
|
+
name: definition.name,
|
|
70
|
+
filePath: component.filePath
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
for (const name of Object.keys(forms)) if (!definitions.some((definition) => definition.name === name)) throw new Error(`Missing Vue form renderer ${name}.`);
|
|
74
|
+
const components = definitions.sort((left, right) => left.name.localeCompare(right.name));
|
|
75
|
+
for (const collection of documents.types) {
|
|
76
|
+
const issues = validateCollectionBody(components, collection.body);
|
|
77
|
+
if (issues.length) throw new Error(`${collection.label}: ${issues.join(" ")}`);
|
|
78
|
+
}
|
|
79
|
+
const encoded = yield* Schema.encodeEffect(Schema.fromJsonString(Schema.Array(ComponentDefinition)))(components);
|
|
80
|
+
const revision = Encoding.encodeHex(yield* crypto.digest("SHA-256", new TextEncoder().encode(encoded)));
|
|
81
|
+
const next = `export default ${yield* Schema.encodeEffect(Schema.fromJsonString(AuthoringCatalog))({
|
|
82
|
+
revision,
|
|
83
|
+
components
|
|
84
|
+
})}\n`;
|
|
85
|
+
registrations.sort((left, right) => left.name.localeCompare(right.name));
|
|
86
|
+
const nextBlocks = [...registrations.map((component, index) => `import Block${index} from ${JSON.stringify(component.filePath)}`), `export default { ${registrations.map((component, index) => `${JSON.stringify(component.name)}: Block${index}`).join(", ")} }`].join("\n");
|
|
87
|
+
const changed = next !== contents || nextBlocks !== blockContents;
|
|
88
|
+
blockContents = nextBlocks;
|
|
89
|
+
if (rejected) {
|
|
90
|
+
rejected = false;
|
|
91
|
+
for (const server of servers) server.ws.send({
|
|
92
|
+
type: "update",
|
|
93
|
+
updates: []
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (!changed) return false;
|
|
97
|
+
contents = next;
|
|
98
|
+
return true;
|
|
99
|
+
} finally {
|
|
100
|
+
const discoveredDependencies = new Set(checker.getProgram()?.getSourceFiles().map((file) => path.resolve(file.fileName)).filter((file) => !file.includes("/node_modules/")));
|
|
101
|
+
dependencies = /* @__PURE__ */ new Set([...dependencies, ...discoveredDependencies]);
|
|
102
|
+
for (const server of servers) server.watcher.add([...dependencies]);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
function rebuild() {
|
|
106
|
+
const current = queue.then(() => run(compile()));
|
|
107
|
+
queue = current.then(() => void 0, () => void 0);
|
|
108
|
+
return current;
|
|
109
|
+
}
|
|
110
|
+
function report(error) {
|
|
111
|
+
rejected = true;
|
|
112
|
+
for (const server of servers) server.ws.send({
|
|
113
|
+
type: "error",
|
|
114
|
+
err: {
|
|
115
|
+
message: error.message,
|
|
116
|
+
stack: error.stack ?? "",
|
|
117
|
+
plugin: "cms-authoring"
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return run(Effect.logError("Authoring update rejected; keeping the last valid catalog.", error));
|
|
121
|
+
}
|
|
122
|
+
function update() {
|
|
123
|
+
return rebuild().then((changed) => changed ? updateTemplates({ filter: (candidate) => [template.dst, blocks.dst].includes(candidate.dst) }) : void 0).catch((error) => report(error instanceof Error ? error : new Error(String(error))));
|
|
124
|
+
}
|
|
125
|
+
nuxt.hook("components:extend", (components) => {
|
|
126
|
+
discovered = components;
|
|
127
|
+
const candidate = rebuild().then(() => void 0);
|
|
128
|
+
if (initialized) return candidate.catch((error) => report(error instanceof Error ? error : new Error(String(error))));
|
|
129
|
+
initialized = true;
|
|
130
|
+
return candidate;
|
|
131
|
+
});
|
|
132
|
+
addVitePlugin({
|
|
133
|
+
name: "cms-authoring-catalog",
|
|
134
|
+
configureServer(server) {
|
|
135
|
+
servers.add(server);
|
|
136
|
+
server.watcher.add([...dependencies]);
|
|
137
|
+
const changed = (filename) => {
|
|
138
|
+
const absolute = path.resolve(filename);
|
|
139
|
+
if (dependencies.has(absolute) || discovered.some((component) => component.filePath === absolute)) update();
|
|
140
|
+
};
|
|
141
|
+
server.watcher.on("change", changed);
|
|
142
|
+
server.httpServer?.once("close", () => {
|
|
143
|
+
servers.delete(server);
|
|
144
|
+
server.watcher.off("change", changed);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return template;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
//#endregion
|
|
152
|
+
export { registerAuthoringCatalog };
|
|
153
|
+
//# sourceMappingURL=authoring-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authoring-catalog.js","names":[],"sources":["../../../../src/internal/authoring-catalog.ts"],"sourcesContent":["import { formAuthoring } from '@internal/document/Forms'\nimport type { FormCapabilities } from '@internal/protocol/Forms'\nimport { validateCollectionBody } from '@internal/document/AuthoringSchema'\nimport type { DocumentConfiguration } from '@internal/protocol/Configuration'\nimport type { Nuxt } from '@nuxt/schema'\nimport type { Component } from '@nuxt/schema'\nimport { AuthoringCatalog, ComponentDefinition } from '@internal/protocol/Configuration'\nimport { addTemplate, addVitePlugin, updateTemplates } from '@nuxt/kit'\nimport { Crypto, Effect, Encoding, FileSystem, Path, Schema } from 'effect'\nimport { createCheckerByJson } from 'vue-component-meta'\nimport { resolveBlockAuthoring } from './authoring-metadata'\n\ntype AuthoringServer = {\n ws: {\n send: (\n payload:\n | { type: 'update'; updates: [] }\n | { type: 'error'; err: { message: string; stack: string; plugin: string } },\n ) => void\n }\n watcher: { add: (paths: string[]) => void }\n}\n\n/** A complete, validated snapshot shared by Vite and Nitro through ordinary imports. */\nexport const registerAuthoringCatalog = Effect.fn('CmsAuthoring.register')(function* (\n nuxt: Nuxt,\n documents: DocumentConfiguration,\n forms: Readonly<Record<string, FormCapabilities>> = {},\n) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const crypto = yield* Crypto.Crypto\n const run = Effect.runPromiseWith(yield* Effect.context<never>())\n let contents = 'export default { revision: \"empty\", components: [] }\\n'\n let blockContents = 'export default {}\\n'\n let discovered: Component[] = []\n let dependencies = new Set<string>()\n let queue = Promise.resolve()\n const servers = new Set<AuthoringServer>()\n let initialized = false\n let rejected = false\n\n const template = addTemplate({\n filename: 'cms/authoring.mjs',\n write: true,\n getContents: () => contents,\n })\n\n const blocks = addTemplate({ filename: 'cms/blocks.mjs', write: true, getContents: () => blockContents })\n nuxt.options.alias['#cms/blocks'] = blocks.dst\n nuxt.options.alias['#cms/authoring'] = template.dst\n nuxt.hook('nitro:config', (config) => {\n config.externals ||= {}\n config.externals.inline ||= []\n config.externals.inline.push(template.dst)\n })\n\n const compile = Effect.fn('CmsAuthoring.compile')(function* () {\n const checker = createCheckerByJson(\n nuxt.options.rootDir,\n {\n compilerOptions: {\n strict: true,\n target: 'ESNext',\n module: 'ESNext',\n moduleResolution: 'Bundler',\n baseUrl: nuxt.options.rootDir,\n paths: Object.fromEntries(\n Object.entries(nuxt.options.alias).map(([key, value]) => [`${key}/*`, [`${value}/*`]]),\n ),\n },\n files: [],\n },\n { schema: true },\n )\n\n const definitions: ComponentDefinition[] = []\n const registrations: { name: string; filePath: string }[] = []\n\n try {\n for (const component of discovered) {\n if (!component.filePath.endsWith('.vue') || component.filePath.includes('/node_modules/')) continue\n\n if (!(yield* fs.exists(component.filePath))) continue\n const source = yield* fs.readFileString(component.filePath)\n const form = forms[component.kebabName]\n\n const definition = form\n ? ComponentDefinition.make({ name: component.kebabName, authoring: formAuthoring(form) })\n : yield* resolveBlockAuthoring(checker, component.filePath, component.kebabName, source)\n\n if (!definition) continue\n\n if (definitions.some((existing) => existing.name === definition.name))\n throw new Error(`Duplicate authoring block ${definition.name}.`)\n definitions.push(definition)\n component.global = false\n registrations.push({ name: definition.name, filePath: component.filePath })\n }\n\n for (const name of Object.keys(forms))\n if (!definitions.some((definition) => definition.name === name))\n throw new Error(`Missing Vue form renderer ${name}.`)\n const components = definitions.sort((left, right) => left.name.localeCompare(right.name))\n\n for (const collection of documents.types) {\n const issues = validateCollectionBody(components, collection.body)\n\n if (issues.length) throw new Error(`${collection.label}: ${issues.join(' ')}`)\n }\n\n const encoded = yield* Schema.encodeEffect(Schema.fromJsonString(Schema.Array(ComponentDefinition)))(components)\n const revision = Encoding.encodeHex(yield* crypto.digest('SHA-256', new TextEncoder().encode(encoded)))\n const catalog = yield* Schema.encodeEffect(Schema.fromJsonString(AuthoringCatalog))({ revision, components })\n const next = `export default ${catalog}\\n`\n registrations.sort((left, right) => left.name.localeCompare(right.name))\n\n const nextBlocks = [\n ...registrations.map((component, index) => `import Block${index} from ${JSON.stringify(component.filePath)}`),\n `export default { ${registrations.map((component, index) => `${JSON.stringify(component.name)}: Block${index}`).join(', ')} }`,\n ].join('\\n')\n\n const changed = next !== contents || nextBlocks !== blockContents\n blockContents = nextBlocks\n\n if (rejected) {\n rejected = false\n\n for (const server of servers) server.ws.send({ type: 'update', updates: [] })\n }\n\n if (!changed) return false\n contents = next\n\n return true\n } finally {\n const discoveredDependencies = new Set(\n checker\n .getProgram()\n ?.getSourceFiles()\n .map((file) => path.resolve(file.fileName))\n .filter((file) => !file.includes('/node_modules/')),\n )\n\n // Keep failed candidate dependencies too, so fixing an imported type can recover the catalog.\n dependencies = new Set([...dependencies, ...discoveredDependencies])\n\n for (const server of servers) server.watcher.add([...dependencies])\n }\n })\n\n function rebuild() {\n const current = queue.then(() => run(compile()))\n queue = current.then(\n () => undefined,\n () => undefined,\n )\n\n return current\n }\n\n function report(error: Error) {\n rejected = true\n\n for (const server of servers)\n server.ws.send({\n type: 'error',\n err: { message: error.message, stack: error.stack ?? '', plugin: 'cms-authoring' },\n })\n\n return run(Effect.logError('Authoring update rejected; keeping the last valid catalog.', error))\n }\n\n function update() {\n return rebuild()\n .then((changed) =>\n changed\n ? updateTemplates({ filter: (candidate) => [template.dst, blocks.dst].includes(candidate.dst) })\n : undefined,\n )\n .catch((error) => report(error instanceof Error ? error : new Error(String(error))))\n }\n\n nuxt.hook('components:extend', (components) => {\n discovered = components\n // This hook runs inside template generation. Its caller writes the new snapshot.\n const candidate = rebuild().then(() => undefined)\n\n if (initialized)\n return candidate.catch((error) => report(error instanceof Error ? error : new Error(String(error))))\n initialized = true\n\n return candidate\n })\n addVitePlugin({\n name: 'cms-authoring-catalog',\n configureServer(server) {\n servers.add(server)\n server.watcher.add([...dependencies])\n\n const changed = (filename: string) => {\n const absolute = path.resolve(filename)\n\n if (dependencies.has(absolute) || discovered.some((component) => component.filePath === absolute)) void update()\n }\n\n server.watcher.on('change', changed)\n server.httpServer?.once('close', () => {\n servers.delete(server)\n server.watcher.off('change', changed)\n })\n },\n })\n\n return template\n})\n"],"mappings":"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"}
|
|
@@ -2,7 +2,6 @@ import { MagicString, babelParse, parse, walkIdentifiers } from "@vue/compiler-s
|
|
|
2
2
|
|
|
3
3
|
//#region src/internal/authoring-compiler.ts
|
|
4
4
|
const authoringImport = "@phtngyn/cms/Authoring";
|
|
5
|
-
/** Reads static authoring data and erases the macro before Vue compiles the SFC. */
|
|
6
5
|
function compileCmsBlock(source, filename) {
|
|
7
6
|
if (!source.includes(authoringImport)) return void 0;
|
|
8
7
|
const { descriptor, errors } = parse(source, { filename });
|
|
@@ -98,7 +97,8 @@ function readAuthoringDefault(source, filename) {
|
|
|
98
97
|
plugins: ["typescript"]
|
|
99
98
|
}).program.body[0];
|
|
100
99
|
if (expression?.type !== "ExpressionStatement") throw new Error(`${filename}: expected a literal Vue default.`);
|
|
101
|
-
|
|
100
|
+
const value = expression.expression;
|
|
101
|
+
return staticValue(value.type === "ArrowFunctionExpression" && value.body.type !== "BlockStatement" ? value.body : value, filename);
|
|
102
102
|
}
|
|
103
103
|
function cmsAuthoringPlugin() {
|
|
104
104
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authoring-compiler.js","names":[],"sources":["../../../../src/internal/authoring-compiler.ts"],"sourcesContent":["import { babelParse, MagicString, parse, walkIdentifiers } from '@vue/compiler-sfc'\nimport type { Schema } from 'effect'\n\nconst authoringImport = '@phtngyn/cms/Authoring'\ntype Statement = ReturnType<typeof babelParse>['program']['body'][number]\ntype Expression = Extract<Statement, { type: 'ExpressionStatement' }>['expression']\ntype Call = Extract<Expression, { type: 'CallExpression' }>\n\n/** Reads static authoring data and erases the macro before Vue compiles the SFC. */\nexport function compileCmsBlock(source: string, filename: string) {\n if (!source.includes(authoringImport)) return undefined\n const { descriptor, errors } = parse(source, { filename })\n if (errors.length) throw new Error(`${filename}: ${errors.join('\\n')}`)\n if (descriptor.script?.content.includes(authoringImport)) {\n const script = babelParse(descriptor.script.content, { sourceType: 'module', plugins: ['typescript'] })\n if (\n script.program.body.some(\n (statement) =>\n statement.type === 'ImportDeclaration' &&\n statement.source.value === authoringImport &&\n statement.importKind !== 'type' &&\n statement.specifiers.some(\n (specifier) => specifier.type !== 'ImportSpecifier' || specifier.importKind !== 'type',\n ),\n )\n )\n throw new Error(`${filename}: defineCmsBlock requires <script setup lang=\"ts\">.`)\n }\n const setup = descriptor.scriptSetup\n if (!setup) return undefined\n const ast = babelParse(setup.content, { sourceType: 'module', plugins: ['typescript'] })\n const imports = ast.program.body.filter(\n (statement) =>\n statement.type === 'ImportDeclaration' &&\n statement.source.value === authoringImport &&\n statement.importKind !== 'type',\n )\n if (!imports.length) return undefined\n const imported = imports[0]\n if (imported?.type !== 'ImportDeclaration') return undefined\n const specifier = imported.specifiers[0]\n if (\n imports.length !== 1 ||\n imported.specifiers.length !== 1 ||\n specifier?.type !== 'ImportSpecifier' ||\n specifier.importKind === 'type' ||\n specifier.imported.type !== 'Identifier' ||\n specifier.imported.name !== 'defineCmsBlock'\n ) {\n throw new Error(`${filename}: use a dedicated named import for defineCmsBlock.`)\n }\n if (setup.lang !== 'ts' || setup.attrs.generic) {\n throw new Error(`${filename}: defineCmsBlock requires a non-generic <script setup lang=\"ts\">.`)\n }\n const calls = ast.program.body.filter(\n (statement) =>\n statement.type === 'ExpressionStatement' &&\n statement.expression.type === 'CallExpression' &&\n statement.expression.callee.type === 'Identifier' &&\n statement.expression.callee.name === specifier.local.name,\n )\n const statement = calls[0]\n if (\n calls.length !== 1 ||\n statement?.type !== 'ExpressionStatement' ||\n statement.expression.type !== 'CallExpression'\n ) {\n throw new Error(`${filename}: defineCmsBlock must be called exactly once as a top-level statement.`)\n }\n const call = statement.expression\n const argument = call.arguments[0]\n if (call.arguments.length !== 1 || argument?.type !== 'ObjectExpression') {\n throw new Error(`${filename}: defineCmsBlock requires one object literal.`)\n }\n const types = call.typeParameters?.type === 'TSTypeParameterInstantiation' ? call.typeParameters.params : []\n if (types.length !== 2)\n throw new Error(`${filename}: share named Props and Slots types with defineProps and defineSlots.`)\n const names = types.map((type) => {\n if (type.type !== 'TSTypeReference' || type.typeName.type !== 'Identifier' || type.typeParameters) {\n throw new Error(`${filename}: defineCmsBlock type arguments must be named, non-generic types.`)\n }\n return type.typeName.name\n })\n const vueCalls = new Map<string, Call>()\n walkIdentifiers(ast.program, (identifier, parent, _stack, isReference) => {\n if (!isReference) return\n if (identifier.name === specifier.local.name && identifier !== call.callee) {\n throw new Error(`${filename}: defineCmsBlock may only be used in its top-level declaration.`)\n }\n if (\n (identifier.name === 'defineProps' || identifier.name === 'defineSlots') &&\n parent?.type === 'CallExpression' &&\n parent.callee === identifier\n ) {\n vueCalls.set(identifier.name, parent)\n }\n })\n for (const [index, macro] of ['defineProps', 'defineSlots'].entries()) {\n const vueCall = vueCalls.get(macro)\n const parameters = vueCall?.typeParameters\n const type = parameters?.type === 'TSTypeParameterInstantiation' ? parameters.params[0] : undefined\n if (\n type?.type !== 'TSTypeReference' ||\n type.typeName.type !== 'Identifier' ||\n type.typeParameters !== undefined ||\n type.typeName.name !== names[index]\n ) {\n throw new Error(`${filename}: ${macro} must use the same ${names[index]} type as defineCmsBlock.`)\n }\n }\n const declaration = staticValue(argument, filename)\n const code = new MagicString(source)\n for (const node of [imported, statement]) {\n if (node.start === null || node.start === undefined || node.end === null || node.end === undefined) {\n throw new Error(`${filename}: missing authoring declaration source location.`)\n }\n code.remove(setup.loc.start.offset + node.start, setup.loc.start.offset + node.end)\n }\n return {\n declaration,\n code: code.toString(),\n map: code.generateMap({ source: filename, includeContent: true, hires: true }),\n }\n}\n\nfunction staticValue(expression: Parameters<typeof walkIdentifiers>[0], filename: string): Schema.Json {\n switch (expression.type) {\n case 'StringLiteral':\n case 'BooleanLiteral':\n return expression.value\n case 'NumericLiteral':\n if (Number.isFinite(expression.value)) return expression.value\n break\n case 'NullLiteral':\n return null\n case 'UnaryExpression':\n if (\n expression.operator === '-' &&\n expression.argument.type === 'NumericLiteral' &&\n Number.isFinite(expression.argument.value)\n ) {\n return -expression.argument.value\n }\n break\n case 'ArrayExpression':\n return expression.elements.map((element) => {\n if (!element || element.type === 'SpreadElement')\n throw new Error(`${filename}: authoring arrays require literal entries.`)\n return staticValue(element, filename)\n })\n case 'ObjectExpression': {\n const entries = expression.properties.map((property): [string, Schema.Json] => {\n if (\n property.type !== 'ObjectProperty' ||\n property.computed ||\n property.shorthand ||\n (property.key.type !== 'Identifier' && property.key.type !== 'StringLiteral')\n ) {\n throw new Error(\n `${filename}: authoring objects require literal named properties; spreads and methods are unsupported.`,\n )\n }\n return [\n property.key.type === 'Identifier' ? property.key.name : property.key.value,\n staticValue(property.value, filename),\n ]\n })\n if (new Set(entries.map(([key]) => key)).size !== entries.length)\n throw new Error(`${filename}: duplicate authoring property.`)\n return Object.fromEntries(entries)\n }\n }\n throw new Error(`${filename}: authoring metadata must contain only JSON literals; ${expression.type} is unsupported.`)\n}\n\n/** Reads Vue's printed default expression without evaluating application code. */\nexport function readAuthoringDefault(source: string, filename: string): Schema.Json {\n const expression = babelParse(`(${source})`, { sourceType: 'module', plugins: ['typescript'] }).program.body[0]\n if (expression?.type !== 'ExpressionStatement') throw new Error(`${filename}: expected a literal Vue default.`)\n return staticValue(expression.expression, filename)\n}\n\nexport function cmsAuthoringPlugin() {\n return {\n name: 'cms:authoring',\n enforce: 'pre' as const,\n transform(source: string, id: string) {\n if (!id.endsWith('.vue')) return undefined\n const compiled = compileCmsBlock(source, id)\n return compiled ? { code: compiled.code, map: compiled.map } : undefined\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"authoring-compiler.js","names":[],"sources":["../../../../src/internal/authoring-compiler.ts"],"sourcesContent":["import { babelParse, MagicString, parse, walkIdentifiers } from '@vue/compiler-sfc'\nimport type { Schema } from 'effect'\n\nconst authoringImport = '@phtngyn/cms/Authoring'\n\ntype Statement = ReturnType<typeof babelParse>['program']['body'][number]\n\ntype Expression = Extract<Statement, { type: 'ExpressionStatement' }>['expression']\n\ntype Call = Extract<Expression, { type: 'CallExpression' }>\n\nexport function compileCmsBlock(source: string, filename: string) {\n if (!source.includes(authoringImport)) return undefined\n const { descriptor, errors } = parse(source, { filename })\n\n if (errors.length) throw new Error(`${filename}: ${errors.join('\\n')}`)\n\n if (descriptor.script?.content.includes(authoringImport)) {\n const script = babelParse(descriptor.script.content, { sourceType: 'module', plugins: ['typescript'] })\n\n if (\n script.program.body.some(\n (statement) =>\n statement.type === 'ImportDeclaration' &&\n statement.source.value === authoringImport &&\n statement.importKind !== 'type' &&\n statement.specifiers.some(\n (specifier) => specifier.type !== 'ImportSpecifier' || specifier.importKind !== 'type',\n ),\n )\n )\n throw new Error(`${filename}: defineCmsBlock requires <script setup lang=\"ts\">.`)\n }\n\n const setup = descriptor.scriptSetup\n\n if (!setup) return undefined\n const ast = babelParse(setup.content, { sourceType: 'module', plugins: ['typescript'] })\n\n const imports = ast.program.body.filter(\n (statement) =>\n statement.type === 'ImportDeclaration' &&\n statement.source.value === authoringImport &&\n statement.importKind !== 'type',\n )\n\n if (!imports.length) return undefined\n const imported = imports[0]\n\n if (imported?.type !== 'ImportDeclaration') return undefined\n const specifier = imported.specifiers[0]\n\n if (\n imports.length !== 1 ||\n imported.specifiers.length !== 1 ||\n specifier?.type !== 'ImportSpecifier' ||\n specifier.importKind === 'type' ||\n specifier.imported.type !== 'Identifier' ||\n specifier.imported.name !== 'defineCmsBlock'\n ) {\n throw new Error(`${filename}: use a dedicated named import for defineCmsBlock.`)\n }\n\n if (setup.lang !== 'ts' || setup.attrs.generic) {\n throw new Error(`${filename}: defineCmsBlock requires a non-generic <script setup lang=\"ts\">.`)\n }\n\n const calls = ast.program.body.filter(\n (statement) =>\n statement.type === 'ExpressionStatement' &&\n statement.expression.type === 'CallExpression' &&\n statement.expression.callee.type === 'Identifier' &&\n statement.expression.callee.name === specifier.local.name,\n )\n\n const statement = calls[0]\n\n if (\n calls.length !== 1 ||\n statement?.type !== 'ExpressionStatement' ||\n statement.expression.type !== 'CallExpression'\n ) {\n throw new Error(`${filename}: defineCmsBlock must be called exactly once as a top-level statement.`)\n }\n\n const call = statement.expression\n const argument = call.arguments[0]\n\n if (call.arguments.length !== 1 || argument?.type !== 'ObjectExpression') {\n throw new Error(`${filename}: defineCmsBlock requires one object literal.`)\n }\n\n const types = call.typeParameters?.type === 'TSTypeParameterInstantiation' ? call.typeParameters.params : []\n\n if (types.length !== 2)\n throw new Error(`${filename}: share named Props and Slots types with defineProps and defineSlots.`)\n\n const names = types.map((type) => {\n if (type.type !== 'TSTypeReference' || type.typeName.type !== 'Identifier' || type.typeParameters) {\n throw new Error(`${filename}: defineCmsBlock type arguments must be named, non-generic types.`)\n }\n\n return type.typeName.name\n })\n\n const vueCalls = new Map<string, Call>()\n walkIdentifiers(ast.program, (identifier, parent, _stack, isReference) => {\n if (!isReference) return\n\n if (identifier.name === specifier.local.name && identifier !== call.callee) {\n throw new Error(`${filename}: defineCmsBlock may only be used in its top-level declaration.`)\n }\n\n if (\n (identifier.name === 'defineProps' || identifier.name === 'defineSlots') &&\n parent?.type === 'CallExpression' &&\n parent.callee === identifier\n ) {\n vueCalls.set(identifier.name, parent)\n }\n })\n\n for (const [index, macro] of ['defineProps', 'defineSlots'].entries()) {\n const vueCall = vueCalls.get(macro)\n const parameters = vueCall?.typeParameters\n const type = parameters?.type === 'TSTypeParameterInstantiation' ? parameters.params[0] : undefined\n\n if (\n type?.type !== 'TSTypeReference' ||\n type.typeName.type !== 'Identifier' ||\n type.typeParameters !== undefined ||\n type.typeName.name !== names[index]\n ) {\n throw new Error(`${filename}: ${macro} must use the same ${names[index]} type as defineCmsBlock.`)\n }\n }\n\n const declaration = staticValue(argument, filename)\n const code = new MagicString(source)\n\n for (const node of [imported, statement]) {\n if (node.start === null || node.start === undefined || node.end === null || node.end === undefined) {\n throw new Error(`${filename}: missing authoring declaration source location.`)\n }\n\n code.remove(setup.loc.start.offset + node.start, setup.loc.start.offset + node.end)\n }\n\n return {\n declaration,\n code: code.toString(),\n map: code.generateMap({ source: filename, includeContent: true, hires: true }),\n }\n}\n\nfunction staticValue(expression: Parameters<typeof walkIdentifiers>[0], filename: string): Schema.Json {\n switch (expression.type) {\n case 'StringLiteral':\n case 'BooleanLiteral':\n return expression.value\n case 'NumericLiteral':\n if (Number.isFinite(expression.value)) return expression.value\n break\n case 'NullLiteral':\n return null\n case 'UnaryExpression':\n if (\n expression.operator === '-' &&\n expression.argument.type === 'NumericLiteral' &&\n Number.isFinite(expression.argument.value)\n ) {\n return -expression.argument.value\n }\n\n break\n case 'ArrayExpression':\n return expression.elements.map((element) => {\n if (!element || element.type === 'SpreadElement')\n throw new Error(`${filename}: authoring arrays require literal entries.`)\n\n return staticValue(element, filename)\n })\n case 'ObjectExpression': {\n const entries = expression.properties.map((property): [string, Schema.Json] => {\n if (\n property.type !== 'ObjectProperty' ||\n property.computed ||\n property.shorthand ||\n (property.key.type !== 'Identifier' && property.key.type !== 'StringLiteral')\n ) {\n throw new Error(\n `${filename}: authoring objects require literal named properties; spreads and methods are unsupported.`,\n )\n }\n\n return [\n property.key.type === 'Identifier' ? property.key.name : property.key.value,\n staticValue(property.value, filename),\n ]\n })\n\n if (new Set(entries.map(([key]) => key)).size !== entries.length)\n throw new Error(`${filename}: duplicate authoring property.`)\n\n return Object.fromEntries(entries)\n }\n }\n\n throw new Error(`${filename}: authoring metadata must contain only JSON literals; ${expression.type} is unsupported.`)\n}\n\n/** Reads Vue's printed default expression without evaluating application code. */\nexport function readAuthoringDefault(source: string, filename: string): Schema.Json {\n const expression = babelParse(`(${source})`, { sourceType: 'module', plugins: ['typescript'] }).program.body[0]\n\n if (expression?.type !== 'ExpressionStatement') throw new Error(`${filename}: expected a literal Vue default.`)\n const value = expression.expression\n\n return staticValue(\n value.type === 'ArrowFunctionExpression' && value.body.type !== 'BlockStatement' ? value.body : value,\n filename,\n )\n}\n\nexport function cmsAuthoringPlugin() {\n return {\n name: 'cms:authoring',\n enforce: 'pre' as const,\n transform(source: string, id: string) {\n if (!id.endsWith('.vue')) return undefined\n const compiled = compileCmsBlock(source, id)\n\n return compiled ? { code: compiled.code, map: compiled.map } : undefined\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as parseAuthoringContent, t as createAuthoringSchema } from "../AuthoringSchema-BQ5kbhVB.js";
|
|
1
|
+
import { a as BlockAuthoring, c as parseAuthoringContent, n as ComponentDefinition, s as createAuthoringSchema } from "../Configuration-CA_jvQKG.js";
|
|
3
2
|
import { compileCmsBlock, readAuthoringDefault } from "./authoring-compiler.js";
|
|
4
|
-
import { Effect, Schema } from "effect";
|
|
3
|
+
import { Effect, Result, Schema } from "effect";
|
|
5
4
|
import ts from "typescript";
|
|
6
5
|
|
|
7
6
|
//#region src/internal/authoring-metadata.ts
|
|
8
7
|
const Presentation = Schema.Struct({
|
|
8
|
+
fields: Schema.optionalKey(Schema.Record(Schema.String, Schema.suspend(() => Presentation))),
|
|
9
|
+
blocks: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
|
|
10
|
+
groups: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
|
|
9
11
|
label: Schema.NonEmptyString,
|
|
10
12
|
help: Schema.optionalKey(Schema.String),
|
|
11
13
|
input: Schema.String,
|
|
@@ -40,28 +42,9 @@ const resolveBlockAuthoring = Effect.fn("NuxtAuthoring.resolve")(function* (chec
|
|
|
40
42
|
const props = Object.entries(declared.props).map(([key, presentation]) => {
|
|
41
43
|
const prop = metadata.props.find((candidate) => candidate.name === key && !candidate.global);
|
|
42
44
|
if (!prop) throw new Error(`${filename}: unknown Vue prop ${key}.`);
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"text",
|
|
47
|
-
"textarea",
|
|
48
|
-
"select"
|
|
49
|
-
].includes(input) || resolved.type === "number" && input !== "number" || resolved.type === "boolean" && input !== "toggle" || input === "select" && !resolved.choices.length) throw new Error(`${filename}: ${key} has an incompatible authoring input ${input}.`);
|
|
50
|
-
for (const key of Object.keys(presentation.optionLabels ?? {})) if (!resolved.choices.includes(key)) throw new Error(`${filename}: ${key} is not a Vue union choice.`);
|
|
51
|
-
return Schema.decodeUnknownSync(BlockAuthoring.fields.props.value)({
|
|
52
|
-
name: key,
|
|
53
|
-
label: presentation.label,
|
|
54
|
-
...presentation.help === void 0 ? void 0 : { help: presentation.help },
|
|
55
|
-
type: resolved.type,
|
|
56
|
-
input,
|
|
57
|
-
required: prop.required,
|
|
58
|
-
nullable: resolved.nullable,
|
|
59
|
-
...prop.default === void 0 ? void 0 : { defaultValue: readAuthoringDefault(prop.default, `${filename} prop ${key}`) },
|
|
60
|
-
options: resolved.choices.map((value) => ({
|
|
61
|
-
value,
|
|
62
|
-
label: presentation.optionLabels?.[value] ?? value
|
|
63
|
-
}))
|
|
64
|
-
});
|
|
45
|
+
const typeChecker = checker.getProgram()?.getTypeChecker();
|
|
46
|
+
if (!typeChecker) throw new Error(`${filename}: Vue type information is unavailable.`);
|
|
47
|
+
return resolveProp(prop.getTypeObject(), key, prop.required, presentation, typeChecker, filename, prop.default);
|
|
65
48
|
});
|
|
66
49
|
for (const prop of metadata.props) if (!prop.global && prop.required && prop.default === void 0 && !Object.hasOwn(declared.props, prop.name)) throw new Error(`${filename}: required prop ${prop.name} needs an authoring declaration and initial value.`);
|
|
67
50
|
const slotTypes = vueSlots(checker, filename);
|
|
@@ -83,6 +66,8 @@ const resolveBlockAuthoring = Effect.fn("NuxtAuthoring.resolve")(function* (chec
|
|
|
83
66
|
input: presentation.input,
|
|
84
67
|
required: slot.required,
|
|
85
68
|
features: presentation.features ?? [],
|
|
69
|
+
...presentation.blocks === void 0 ? void 0 : { blocks: presentation.blocks },
|
|
70
|
+
...presentation.groups === void 0 ? void 0 : { groups: presentation.groups },
|
|
86
71
|
...initial === void 0 ? void 0 : { initial }
|
|
87
72
|
}));
|
|
88
73
|
if (slot.required && !seed) throw new Error(`${filename}: required slot ${key} needs initial content.`);
|
|
@@ -102,37 +87,71 @@ const resolveBlockAuthoring = Effect.fn("NuxtAuthoring.resolve")(function* (chec
|
|
|
102
87
|
createAuthoringSchema(name, authoring);
|
|
103
88
|
return ComponentDefinition.make({
|
|
104
89
|
name,
|
|
105
|
-
authoring
|
|
106
|
-
props: props.map(({ name, required, type }) => ({
|
|
107
|
-
name,
|
|
108
|
-
required,
|
|
109
|
-
type
|
|
110
|
-
})),
|
|
111
|
-
slots: slots.map(({ name }) => ({ name }))
|
|
90
|
+
authoring
|
|
112
91
|
});
|
|
113
92
|
});
|
|
114
|
-
function
|
|
115
|
-
|
|
93
|
+
function resolveProp(raw, name, required, presentation, checker, filename, defaultSource, depth = 0) {
|
|
94
|
+
if (depth > 12) throw new Error(`${filename}: ${name} exceeds the supported property nesting depth.`);
|
|
116
95
|
const union = raw.isUnion() ? raw.types : [raw];
|
|
117
96
|
const members = union.filter((type) => !(type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)));
|
|
118
97
|
const nullable = union.some((type) => Boolean(type.flags & ts.TypeFlags.Null));
|
|
119
98
|
const choices = members.flatMap((type) => type.isStringLiteral() ? [type.value] : []).sort();
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (members.length
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
99
|
+
const valueType = checker.getNonNullableType(raw);
|
|
100
|
+
let type;
|
|
101
|
+
let fields;
|
|
102
|
+
if (members.length && members.every((type) => Boolean(type.flags & ts.TypeFlags.StringLike))) type = "string";
|
|
103
|
+
else if (members.length === 1 && members[0]?.flags === ts.TypeFlags.Number) type = "number";
|
|
104
|
+
else if (members.length && members.every((type) => Boolean(type.flags & ts.TypeFlags.BooleanLike))) type = "boolean";
|
|
105
|
+
else if (presentation.input === "object" || presentation.input === "list") {
|
|
106
|
+
type = presentation.input === "list" ? "array" : "object";
|
|
107
|
+
const item = type === "array" ? checker.getIndexTypeOfType(valueType, ts.IndexKind.Number) : valueType;
|
|
108
|
+
if (!item || !(item.flags & ts.TypeFlags.Object) || item.getCallSignatures().length) throw new Error(`${filename}: ${name} needs an object${type === "array" ? " list" : ""} type.`);
|
|
109
|
+
if (type === "array") {
|
|
110
|
+
const id = item.getProperty("id");
|
|
111
|
+
const declaration = id?.valueDeclaration ?? id?.declarations?.[0];
|
|
112
|
+
if (!id || !declaration || id.flags & ts.SymbolFlags.Optional || checker.getTypeOfSymbolAtLocation(id, declaration).flags !== ts.TypeFlags.String) throw new Error(`${filename}: ${name} list items need a required string id for stable editing.`);
|
|
113
|
+
}
|
|
114
|
+
fields = Object.entries(presentation.fields ?? {}).map(([key, field]) => {
|
|
115
|
+
if (type === "array" && key === "id") throw new Error(`${filename}: list item ids are managed by Studio.`);
|
|
116
|
+
const property = item.getProperty(key);
|
|
117
|
+
const declaration = property?.valueDeclaration ?? property?.declarations?.[0];
|
|
118
|
+
if (!property || !declaration) throw new Error(`${filename}: unknown field ${name}.${key}.`);
|
|
119
|
+
return resolveProp(checker.getTypeOfSymbolAtLocation(property, declaration), key, !(property.flags & ts.SymbolFlags.Optional), field, checker, filename, void 0, depth + 1);
|
|
120
|
+
});
|
|
121
|
+
for (const property of item.getProperties()) {
|
|
122
|
+
if (type === "array" && property.name === "id") continue;
|
|
123
|
+
if (!(property.flags & ts.SymbolFlags.Optional) && !fields.some((field) => field.name === property.name)) throw new Error(`${filename}: required field ${name}.${property.name} needs an authoring declaration.`);
|
|
124
|
+
}
|
|
125
|
+
} else throw new Error(`${filename}: unsupported editable prop ${name}.`);
|
|
126
|
+
if (!{
|
|
127
|
+
string: [
|
|
128
|
+
"text",
|
|
129
|
+
"textarea",
|
|
130
|
+
"select",
|
|
131
|
+
"image",
|
|
132
|
+
"link"
|
|
133
|
+
],
|
|
134
|
+
number: ["number"],
|
|
135
|
+
boolean: ["toggle"],
|
|
136
|
+
object: ["object"],
|
|
137
|
+
array: ["list"]
|
|
138
|
+
}[type].includes(presentation.input) || presentation.input === "select" && !choices.length) throw new Error(`${filename}: ${name} has an incompatible authoring input ${presentation.input}.`);
|
|
139
|
+
for (const key of Object.keys(presentation.optionLabels ?? {})) if (!choices.includes(key)) throw new Error(`${filename}: ${key} is not a Vue union choice.`);
|
|
140
|
+
return Result.getOrThrow(Schema.decodeUnknownResult(BlockAuthoring.fields.props.value)({
|
|
141
|
+
name,
|
|
142
|
+
label: presentation.label,
|
|
143
|
+
type,
|
|
144
|
+
input: presentation.input,
|
|
145
|
+
required,
|
|
132
146
|
nullable,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
...presentation.help === void 0 ? void 0 : { help: presentation.help },
|
|
148
|
+
...fields === void 0 ? void 0 : { fields },
|
|
149
|
+
...defaultSource === void 0 ? void 0 : { defaultValue: readAuthoringDefault(defaultSource, `${filename} prop ${name}`) },
|
|
150
|
+
options: choices.map((value) => ({
|
|
151
|
+
value,
|
|
152
|
+
label: presentation.optionLabels?.[value] ?? value
|
|
153
|
+
}))
|
|
154
|
+
}));
|
|
136
155
|
}
|
|
137
156
|
function vueSlots(checker, filename) {
|
|
138
157
|
const program = checker.getProgram();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authoring-metadata.js","names":[],"sources":["../../../../src/internal/authoring-metadata.ts"],"sourcesContent":["import type { ComponentMetaChecker, PropertyMeta } from 'vue-component-meta'\nimport type { BlockProp, BlockSlot } from '@internal/protocol/Authoring'\nimport { createAuthoringSchema, parseAuthoringContent } from '@internal/document/AuthoringSchema'\nimport { BlockAuthoring } from '@internal/protocol/Authoring'\nimport { ComponentDefinition } from '@internal/protocol/Configuration'\nimport { Effect, Schema } from 'effect'\nimport ts from 'typescript'\nimport { compileCmsBlock, readAuthoringDefault } from './authoring-compiler'\n\nconst Presentation = Schema.Struct({\n label: Schema.NonEmptyString,\n help: Schema.optionalKey(Schema.String),\n input: Schema.String,\n optionLabels: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),\n features: Schema.optionalKey(Schema.Array(Schema.Literals(['bold', 'italic', 'link', 'bullet-list', 'code']))),\n})\nconst Declaration = Schema.Struct({\n label: Schema.NonEmptyString,\n description: Schema.optionalKey(Schema.String),\n group: Schema.optionalKey(Schema.String),\n icon: Schema.optionalKey(Schema.String),\n props: Schema.Record(Schema.String, Presentation),\n slots: Schema.Record(Schema.String, Presentation),\n initial: Schema.optionalKey(\n Schema.Struct({\n props: Schema.optionalKey(Schema.JsonObject),\n slots: Schema.optionalKey(\n Schema.Record(\n Schema.String,\n Schema.Union([Schema.Struct({ text: Schema.String }), Schema.Struct({ markdown: Schema.String })]),\n ),\n ),\n }),\n ),\n})\n\n/** Resolves one opted-in SFC. No framework metadata or executable defaults cross this seam. */\nexport const resolveBlockAuthoring = Effect.fn('NuxtAuthoring.resolve')(function* (\n checker: ComponentMetaChecker,\n filename: string,\n name: string,\n source: string,\n) {\n const compiled = compileCmsBlock(source, filename)\n if (!compiled) return undefined\n const declared = yield* Schema.decodeUnknownEffect(Declaration)(compiled.declaration, { onExcessProperty: 'error' })\n checker.updateFile(filename, source)\n const metadata = checker.getComponentMeta(filename)\n const props = Object.entries(declared.props).map(([key, presentation]): BlockProp => {\n const prop = metadata.props.find((candidate) => candidate.name === key && !candidate.global)\n if (!prop) throw new Error(`${filename}: unknown Vue prop ${key}.`)\n const resolved = scalarProp(prop, filename)\n const input = presentation.input\n if (\n (resolved.type === 'string' && !['text', 'textarea', 'select'].includes(input)) ||\n (resolved.type === 'number' && input !== 'number') ||\n (resolved.type === 'boolean' && input !== 'toggle') ||\n (input === 'select' && !resolved.choices.length)\n )\n throw new Error(`${filename}: ${key} has an incompatible authoring input ${input}.`)\n for (const key of Object.keys(presentation.optionLabels ?? {}))\n if (!resolved.choices.includes(key)) throw new Error(`${filename}: ${key} is not a Vue union choice.`)\n return Schema.decodeUnknownSync(BlockAuthoring.fields.props.value)({\n name: key,\n label: presentation.label,\n ...(presentation.help === undefined ? undefined : { help: presentation.help }),\n type: resolved.type,\n input,\n required: prop.required,\n nullable: resolved.nullable,\n ...(prop.default === undefined\n ? undefined\n : { defaultValue: readAuthoringDefault(prop.default, `${filename} prop ${key}`) }),\n options: resolved.choices.map((value) => ({ value, label: presentation.optionLabels?.[value] ?? value })),\n })\n })\n for (const prop of metadata.props) {\n if (!prop.global && prop.required && prop.default === undefined && !Object.hasOwn(declared.props, prop.name))\n throw new Error(`${filename}: required prop ${prop.name} needs an authoring declaration and initial value.`)\n }\n const slotTypes = vueSlots(checker, filename)\n const slots: BlockSlot[] = []\n for (const [key, presentation] of Object.entries(declared.slots)) {\n const slot = slotTypes.get(key)\n if (!slot) throw new Error(`${filename}: unknown Vue slot ${key}.`)\n if (slot.scoped) throw new Error(`${filename}: scoped slot ${key} cannot be authored.`)\n const seed = declared.initial?.slots?.[key]\n const initial = seed\n ? 'text' in seed\n ? [['p', {}, seed.text]]\n : yield* parseAuthoringContent(seed.markdown)\n : undefined\n slots.push(\n yield* Schema.decodeUnknownEffect(BlockAuthoring.fields.slots.value)({\n name: key,\n label: presentation.label,\n ...(presentation.help === undefined ? undefined : { help: presentation.help }),\n input: presentation.input,\n required: slot.required,\n features: presentation.features ?? [],\n ...(initial === undefined ? undefined : { initial }),\n }),\n )\n if (slot.required && !seed) throw new Error(`${filename}: required slot ${key} needs initial content.`)\n }\n for (const [key, slot] of slotTypes)\n if (slot.required && !Object.hasOwn(declared.slots, key))\n throw new Error(`${filename}: required slot ${key} must be declared.`)\n for (const key of Object.keys(declared.initial?.slots ?? {}))\n if (!slotTypes.has(key) || !Object.hasOwn(declared.slots, key))\n throw new Error(`${filename}: initial content refers to undeclared slot ${key}.`)\n const authoring = BlockAuthoring.make({\n version: 1,\n label: declared.label,\n props,\n slots,\n initialProps: declared.initial?.props ?? {},\n ...(declared.description === undefined ? undefined : { description: declared.description }),\n ...(declared.group === undefined ? undefined : { group: declared.group }),\n ...(declared.icon === undefined ? undefined : { icon: declared.icon }),\n })\n createAuthoringSchema(name, authoring)\n return ComponentDefinition.make({\n name,\n authoring,\n props: props.map(({ name, required, type }) => ({ name, required, type })),\n slots: slots.map(({ name }) => ({ name })),\n })\n})\n\nfunction scalarProp(prop: PropertyMeta, filename: string) {\n const raw = prop.getTypeObject()\n const union = raw.isUnion() ? raw.types : [raw]\n const members = union.filter((type) => !(type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)))\n const nullable = union.some((type) => Boolean(type.flags & ts.TypeFlags.Null))\n const choices = members.flatMap((type) => (type.isStringLiteral() ? [type.value] : [])).sort()\n if (members.length && members.every((type) => Boolean(type.flags & ts.TypeFlags.StringLike)))\n return { type: 'string' as const, nullable, choices }\n if (members.length === 1 && members[0]?.flags === ts.TypeFlags.Number)\n return { type: 'number' as const, nullable, choices }\n if (members.length === 2 && members.every((type) => Boolean(type.flags & ts.TypeFlags.BooleanLiteral)))\n return { type: 'boolean' as const, nullable, choices }\n throw new Error(`${filename}: editable prop ${prop.name} must be a string, literal string union, number, or boolean.`)\n}\n\nfunction vueSlots(checker: ComponentMetaChecker, filename: string) {\n const program = checker.getProgram()\n const file = program?.getSourceFile(filename)\n if (!program || !file) throw new Error(`${filename}: Vue type information is unavailable.`)\n const typeChecker = program.getTypeChecker()\n const result = new Map<string, { required: boolean; scoped: boolean }>()\n function visit(node: ts.Node): void {\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'defineSlots') {\n const type = node.typeArguments?.[0]\n if (type)\n for (const property of typeChecker.getTypeAtLocation(type).getProperties()) {\n const declaration = property.valueDeclaration ?? property.declarations?.[0]\n const signature = declaration\n ? typeChecker\n .getNonNullableType(typeChecker.getTypeOfSymbolAtLocation(property, declaration))\n .getCallSignatures()\n : []\n result.set(property.name, {\n required: !(property.flags & ts.SymbolFlags.Optional),\n scoped: signature.length !== 1 || signature.some((signature) => signature.parameters.length > 0),\n })\n }\n }\n ts.forEachChild(node, visit)\n }\n visit(file)\n return result\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"authoring-metadata.js","names":[],"sources":["../../../../src/internal/authoring-metadata.ts"],"sourcesContent":["import type { ComponentMetaChecker } from 'vue-component-meta'\nimport type { BlockProp, BlockSlot } from '@internal/protocol/Authoring'\nimport { createAuthoringSchema, parseAuthoringContent } from '@internal/document/AuthoringSchema'\nimport { BlockAuthoring } from '@internal/protocol/Authoring'\nimport { ComponentDefinition } from '@internal/protocol/Configuration'\nimport { Result, Effect, Schema } from 'effect'\nimport ts from 'typescript'\nimport { compileCmsBlock, readAuthoringDefault } from './authoring-compiler'\n\ninterface Presentation {\n readonly label: string\n readonly help?: string\n readonly input: string\n readonly optionLabels?: Readonly<Record<string, string>>\n readonly features?: ReadonlyArray<'bold' | 'italic' | 'link' | 'bullet-list' | 'code'>\n readonly blocks?: ReadonlyArray<string>\n readonly groups?: ReadonlyArray<string>\n readonly fields?: Readonly<Record<string, Presentation>>\n}\n\nconst Presentation: Schema.Codec<Presentation> = Schema.Struct({\n fields: Schema.optionalKey(\n Schema.Record(\n Schema.String,\n Schema.suspend(() => Presentation),\n ),\n ),\n blocks: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),\n groups: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),\n label: Schema.NonEmptyString,\n help: Schema.optionalKey(Schema.String),\n input: Schema.String,\n optionLabels: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),\n features: Schema.optionalKey(Schema.Array(Schema.Literals(['bold', 'italic', 'link', 'bullet-list', 'code']))),\n})\n\nconst Declaration = Schema.Struct({\n label: Schema.NonEmptyString,\n description: Schema.optionalKey(Schema.String),\n group: Schema.optionalKey(Schema.String),\n icon: Schema.optionalKey(Schema.String),\n props: Schema.Record(Schema.String, Presentation),\n slots: Schema.Record(Schema.String, Presentation),\n initial: Schema.optionalKey(\n Schema.Struct({\n props: Schema.optionalKey(Schema.JsonObject),\n slots: Schema.optionalKey(\n Schema.Record(\n Schema.String,\n Schema.Union([Schema.Struct({ text: Schema.String }), Schema.Struct({ markdown: Schema.String })]),\n ),\n ),\n }),\n ),\n})\n\n/** Resolves one opted-in SFC. No framework metadata or executable defaults cross this seam. */\nexport const resolveBlockAuthoring = Effect.fn('NuxtAuthoring.resolve')(function* (\n checker: ComponentMetaChecker,\n filename: string,\n name: string,\n source: string,\n) {\n const compiled = compileCmsBlock(source, filename)\n\n if (!compiled) return undefined\n const declared = yield* Schema.decodeUnknownEffect(Declaration)(compiled.declaration, { onExcessProperty: 'error' })\n checker.updateFile(filename, source)\n const metadata = checker.getComponentMeta(filename)\n\n const props = Object.entries(declared.props).map(([key, presentation]): BlockProp => {\n const prop = metadata.props.find((candidate) => candidate.name === key && !candidate.global)\n\n if (!prop) throw new Error(`${filename}: unknown Vue prop ${key}.`)\n const typeChecker = checker.getProgram()?.getTypeChecker()\n\n if (!typeChecker) throw new Error(`${filename}: Vue type information is unavailable.`)\n\n return resolveProp(prop.getTypeObject(), key, prop.required, presentation, typeChecker, filename, prop.default)\n })\n\n for (const prop of metadata.props) {\n if (!prop.global && prop.required && prop.default === undefined && !Object.hasOwn(declared.props, prop.name))\n throw new Error(`${filename}: required prop ${prop.name} needs an authoring declaration and initial value.`)\n }\n\n const slotTypes = vueSlots(checker, filename)\n const slots: BlockSlot[] = []\n\n for (const [key, presentation] of Object.entries(declared.slots)) {\n const slot = slotTypes.get(key)\n\n if (!slot) throw new Error(`${filename}: unknown Vue slot ${key}.`)\n\n if (slot.scoped) throw new Error(`${filename}: scoped slot ${key} cannot be authored.`)\n const seed = declared.initial?.slots?.[key]\n\n const initial = seed\n ? 'text' in seed\n ? [['p', {}, seed.text]]\n : yield* parseAuthoringContent(seed.markdown)\n : undefined\n\n slots.push(\n yield* Schema.decodeUnknownEffect(BlockAuthoring.fields.slots.value)({\n name: key,\n label: presentation.label,\n ...(presentation.help === undefined ? undefined : { help: presentation.help }),\n input: presentation.input,\n required: slot.required,\n features: presentation.features ?? [],\n ...(presentation.blocks === undefined ? undefined : { blocks: presentation.blocks }),\n ...(presentation.groups === undefined ? undefined : { groups: presentation.groups }),\n ...(initial === undefined ? undefined : { initial }),\n }),\n )\n\n if (slot.required && !seed) throw new Error(`${filename}: required slot ${key} needs initial content.`)\n }\n\n for (const [key, slot] of slotTypes)\n if (slot.required && !Object.hasOwn(declared.slots, key))\n throw new Error(`${filename}: required slot ${key} must be declared.`)\n\n for (const key of Object.keys(declared.initial?.slots ?? {}))\n if (!slotTypes.has(key) || !Object.hasOwn(declared.slots, key))\n throw new Error(`${filename}: initial content refers to undeclared slot ${key}.`)\n\n const authoring = BlockAuthoring.make({\n version: 1,\n label: declared.label,\n props,\n slots,\n initialProps: declared.initial?.props ?? {},\n ...(declared.description === undefined ? undefined : { description: declared.description }),\n ...(declared.group === undefined ? undefined : { group: declared.group }),\n ...(declared.icon === undefined ? undefined : { icon: declared.icon }),\n })\n\n createAuthoringSchema(name, authoring)\n\n return ComponentDefinition.make({\n name,\n authoring,\n })\n})\n\nfunction resolveProp(\n raw: ts.Type,\n name: string,\n required: boolean,\n presentation: Presentation,\n checker: ts.TypeChecker,\n filename: string,\n defaultSource?: string,\n depth = 0,\n): BlockProp {\n if (depth > 12) throw new Error(`${filename}: ${name} exceeds the supported property nesting depth.`)\n const union = raw.isUnion() ? raw.types : [raw]\n const members = union.filter((type) => !(type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)))\n const nullable = union.some((type) => Boolean(type.flags & ts.TypeFlags.Null))\n const choices = members.flatMap((type) => (type.isStringLiteral() ? [type.value] : [])).sort()\n const valueType = checker.getNonNullableType(raw)\n let type: BlockProp['type']\n let fields: BlockProp[] | undefined\n\n if (members.length && members.every((type) => Boolean(type.flags & ts.TypeFlags.StringLike))) type = 'string'\n else if (members.length === 1 && members[0]?.flags === ts.TypeFlags.Number) type = 'number'\n else if (members.length && members.every((type) => Boolean(type.flags & ts.TypeFlags.BooleanLike))) type = 'boolean'\n else if (presentation.input === 'object' || presentation.input === 'list') {\n type = presentation.input === 'list' ? 'array' : 'object'\n const item = type === 'array' ? checker.getIndexTypeOfType(valueType, ts.IndexKind.Number) : valueType\n\n if (!item || !(item.flags & ts.TypeFlags.Object) || item.getCallSignatures().length)\n throw new Error(`${filename}: ${name} needs an object${type === 'array' ? ' list' : ''} type.`)\n\n if (type === 'array') {\n const id = item.getProperty('id')\n const declaration = id?.valueDeclaration ?? id?.declarations?.[0]\n\n if (\n !id ||\n !declaration ||\n id.flags & ts.SymbolFlags.Optional ||\n checker.getTypeOfSymbolAtLocation(id, declaration).flags !== ts.TypeFlags.String\n )\n throw new Error(`${filename}: ${name} list items need a required string id for stable editing.`)\n }\n\n fields = Object.entries(presentation.fields ?? {}).map(([key, field]) => {\n if (type === 'array' && key === 'id') throw new Error(`${filename}: list item ids are managed by Studio.`)\n const property = item.getProperty(key)\n const declaration = property?.valueDeclaration ?? property?.declarations?.[0]\n\n if (!property || !declaration) throw new Error(`${filename}: unknown field ${name}.${key}.`)\n\n return resolveProp(\n checker.getTypeOfSymbolAtLocation(property, declaration),\n key,\n !(property.flags & ts.SymbolFlags.Optional),\n field,\n checker,\n filename,\n undefined,\n depth + 1,\n )\n })\n\n for (const property of item.getProperties()) {\n if (type === 'array' && property.name === 'id') continue\n\n if (!(property.flags & ts.SymbolFlags.Optional) && !fields.some((field) => field.name === property.name))\n throw new Error(`${filename}: required field ${name}.${property.name} needs an authoring declaration.`)\n }\n } else throw new Error(`${filename}: unsupported editable prop ${name}.`)\n\n const inputs = {\n string: ['text', 'textarea', 'select', 'image', 'link'],\n number: ['number'],\n boolean: ['toggle'],\n object: ['object'],\n array: ['list'],\n }\n\n if (!inputs[type].includes(presentation.input) || (presentation.input === 'select' && !choices.length))\n throw new Error(`${filename}: ${name} has an incompatible authoring input ${presentation.input}.`)\n\n for (const key of Object.keys(presentation.optionLabels ?? {}))\n if (!choices.includes(key)) throw new Error(`${filename}: ${key} is not a Vue union choice.`)\n\n return Result.getOrThrow(\n Schema.decodeUnknownResult(BlockAuthoring.fields.props.value)({\n name,\n label: presentation.label,\n type,\n input: presentation.input,\n required,\n nullable,\n ...(presentation.help === undefined ? undefined : { help: presentation.help }),\n ...(fields === undefined ? undefined : { fields }),\n ...(defaultSource === undefined\n ? undefined\n : { defaultValue: readAuthoringDefault(defaultSource, `${filename} prop ${name}`) }),\n options: choices.map((value) => ({ value, label: presentation.optionLabels?.[value] ?? value })),\n }),\n )\n}\n\nfunction vueSlots(checker: ComponentMetaChecker, filename: string) {\n const program = checker.getProgram()\n const file = program?.getSourceFile(filename)\n\n if (!program || !file) throw new Error(`${filename}: Vue type information is unavailable.`)\n const typeChecker = program.getTypeChecker()\n const result = new Map<string, { required: boolean; scoped: boolean }>()\n\n function visit(node: ts.Node): void {\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'defineSlots') {\n const type = node.typeArguments?.[0]\n\n if (type)\n for (const property of typeChecker.getTypeAtLocation(type).getProperties()) {\n const declaration = property.valueDeclaration ?? property.declarations?.[0]\n\n const signature = declaration\n ? typeChecker\n .getNonNullableType(typeChecker.getTypeOfSymbolAtLocation(property, declaration))\n .getCallSignatures()\n : []\n\n result.set(property.name, {\n required: !(property.flags & ts.SymbolFlags.Optional),\n scoped: signature.length !== 1 || signature.some((signature) => signature.parameters.length > 0),\n })\n }\n }\n\n ts.forEachChild(node, visit)\n }\n\n visit(file)\n\n return result\n}\n"],"mappings":"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"}
|
|
@@ -13,7 +13,6 @@ var CmsConfigurationLoadFailed = class extends Schema.TaggedError()("CmsConfigur
|
|
|
13
13
|
cause: Schema.Defect(),
|
|
14
14
|
operation: Schema.String
|
|
15
15
|
}) {};
|
|
16
|
-
/** Loads the conventional executable CMS configuration without routing it through Nuxt options. */
|
|
17
16
|
const loadCmsConfiguration = Effect.fn("CmsConfiguration.load")(function* (rootDir) {
|
|
18
17
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
19
18
|
const paths = yield* Path.Path;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-config.js","names":[],"sources":["../../../../src/internal/cms-config.ts"],"sourcesContent":["import type { CmsConfiguration } from '../Configuration'\nimport { Effect, FileSystem, Path, Schema } from 'effect'\nimport { createJiti } from 'jiti'\n\nconst CMS_CONFIG_NAMES = ['cms.config.ts', 'cms.config.mts', 'cms.config.js', 'cms.config.mjs']\nconst jiti = createJiti(import.meta.url)\n\nclass CmsConfigurationLoadFailed extends Schema.TaggedError<CmsConfigurationLoadFailed>()(\n 'CmsConfigurationLoadFailed',\n { cause: Schema.Defect(), operation: Schema.String },\n) {}\n\
|
|
1
|
+
{"version":3,"file":"cms-config.js","names":[],"sources":["../../../../src/internal/cms-config.ts"],"sourcesContent":["import type { CmsConfiguration } from '../Configuration'\nimport { Effect, FileSystem, Path, Schema } from 'effect'\nimport { createJiti } from 'jiti'\n\nconst CMS_CONFIG_NAMES = ['cms.config.ts', 'cms.config.mts', 'cms.config.js', 'cms.config.mjs']\n\nconst jiti = createJiti(import.meta.url)\n\nclass CmsConfigurationLoadFailed extends Schema.TaggedError<CmsConfigurationLoadFailed>()(\n 'CmsConfigurationLoadFailed',\n { cause: Schema.Defect(), operation: Schema.String },\n) {}\n\nexport const loadCmsConfiguration = Effect.fn('CmsConfiguration.load')(function* (rootDir: string) {\n const fileSystem = yield* FileSystem.FileSystem\n const paths = yield* Path.Path\n let configurationPath: string | undefined\n\n for (const name of CMS_CONFIG_NAMES) {\n const candidate = paths.join(rootDir, name)\n\n if (yield* fileSystem.exists(candidate)) {\n configurationPath = candidate\n break\n }\n }\n\n if (!configurationPath) {\n return yield* new CmsConfigurationLoadFailed({\n cause: new Error(`CMS configuration is missing. Create ${paths.join(rootDir, 'cms.config.ts')}.`),\n operation: 'CmsConfiguration.locate',\n })\n }\n\n const configuration = yield* Effect.tryPromise({\n try: () => jiti.import<CmsConfiguration>(configurationPath, { default: true }),\n catch: (cause) => new CmsConfigurationLoadFailed({ cause, operation: 'CmsConfiguration.import' }),\n })\n\n return { configuration, path: configurationPath }\n})\n"],"mappings":";;;;AAIA,MAAM,mBAAmB;CAAC;CAAiB;CAAkB;CAAiB;AAAgB;AAE9F,MAAM,OAAO,WAAW,YAAY,GAAG;AAEvC,IAAM,6BAAN,cAAyC,OAAO,YAAwC,CAAC,CACvF,8BACA;CAAE,OAAO,OAAO,OAAO;CAAG,WAAW,OAAO;AAAO,CACrD,CAAC,CAAC,CAAC;AAEH,MAAa,uBAAuB,OAAO,GAAG,uBAAuB,CAAC,CAAC,WAAW,SAAiB;CACjG,MAAM,aAAa,OAAO,WAAW;CACrC,MAAM,QAAQ,OAAO,KAAK;CAC1B,IAAI;CAEJ,KAAK,MAAM,QAAQ,kBAAkB;EACnC,MAAM,YAAY,MAAM,KAAK,SAAS,IAAI;EAE1C,IAAI,OAAO,WAAW,OAAO,SAAS,GAAG;GACvC,oBAAoB;GACpB;EACF;CACF;CAEA,IAAI,CAAC,mBACH,OAAO,OAAO,IAAI,2BAA2B;EAC3C,uBAAO,IAAI,MAAM,wCAAwC,MAAM,KAAK,SAAS,eAAe,EAAE,EAAE;EAChG,WAAW;CACb,CAAC;CAQH,OAAO;EAAE,sBALoB,OAAO,WAAW;GAC7C,WAAW,KAAK,OAAyB,mBAAmB,EAAE,SAAS,KAAK,CAAC;GAC7E,QAAQ,UAAU,IAAI,2BAA2B;IAAE;IAAO,WAAW;GAA0B,CAAC;EAClG,CAAC;EAEuB,MAAM;CAAkB;AAClD,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { validateFormCapabilities } from "./form-capabilities.js";
|
|
2
|
+
import { V as SeoConfiguration, u as DocumentImportSource } from "../models-DwYioyNV.js";
|
|
3
|
+
import { j as StudioBootstrap, t as maxS3SingleCopyBytes, u as makeDefaultSeoOptions } from "../S3MediaObjects-JKMAWyVB.js";
|
|
4
|
+
import { compileCmsI18n } from "./i18n.js";
|
|
5
|
+
import { i as PreviewOrigin } from "../models-BWm1pTAf.js";
|
|
5
6
|
import { compileCmsDocumentModel } from "./document-model.js";
|
|
6
|
-
import { Result, Schema } from "effect";
|
|
7
|
+
import { Predicate, Result, Schema } from "effect";
|
|
7
8
|
|
|
8
9
|
//#region src/internal/configuration.ts
|
|
9
10
|
const DEFAULT_CLEANUP_INTERVAL_MILLIS = 864e5;
|
|
@@ -19,7 +20,10 @@ const InstallationConfiguration = Schema.Struct({
|
|
|
19
20
|
databaseUrl: Schema.NonEmptyString,
|
|
20
21
|
initializationToken: Schema.NonEmptyString.check(Schema.isMinLength(32)),
|
|
21
22
|
media: Schema.optionalKey(Schema.Union([Schema.Literals([false]), Schema.Struct({
|
|
22
|
-
maxUploadBytes: Schema.optionalKey(Schema.Int.check(Schema.
|
|
23
|
+
maxUploadBytes: Schema.optionalKey(Schema.Int.check(Schema.isBetween({
|
|
24
|
+
minimum: 1,
|
|
25
|
+
maximum: maxS3SingleCopyBytes
|
|
26
|
+
}))),
|
|
23
27
|
publicBaseUrl: Schema.NonEmptyString,
|
|
24
28
|
storage: Schema.Struct({
|
|
25
29
|
bucket: Schema.NonEmptyString,
|
|
@@ -34,39 +38,47 @@ const InstallationConfiguration = Schema.Struct({
|
|
|
34
38
|
region: Schema.NonEmptyString
|
|
35
39
|
})
|
|
36
40
|
})])),
|
|
41
|
+
seo: Schema.optionalKey(SeoConfiguration),
|
|
37
42
|
secret: Schema.NonEmptyString.check(Schema.isMinLength(32))
|
|
38
43
|
});
|
|
39
|
-
/** Compiles every authored, executable concern once through the owning document interface. */
|
|
40
44
|
function compileCmsConfiguration(configuration, initialSources = []) {
|
|
41
45
|
const decodedConfiguration = Schema.decodeResult(InstallationConfiguration)(configuration);
|
|
42
46
|
if (Result.isFailure(decodedConfiguration)) throw new Error(`Invalid CMS configuration: ${String(decodedConfiguration.failure)}`);
|
|
43
|
-
const
|
|
47
|
+
for (const [name, form] of Object.entries(configuration.forms ?? {})) {
|
|
48
|
+
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(name)) throw new Error(`Form ${name} needs a kebab-case Vue component name.`);
|
|
49
|
+
validateFormCapabilities(form.definition);
|
|
50
|
+
if (!Predicate.isFunction(form.onSubmit)) throw new Error(`Form ${name} needs a server submission handler.`);
|
|
51
|
+
}
|
|
52
|
+
const i18n = compileCmsI18n(configuration.i18n);
|
|
53
|
+
const documents = compileCmsDocumentModel(configuration.documents, configuration.i18n);
|
|
44
54
|
const decodedSources = Schema.decodeResult(Schema.Array(DocumentImportSource))(initialSources);
|
|
45
55
|
if (Result.isFailure(decodedSources)) throw new Error(`Invalid CMS initial sources: ${String(decodedSources.failure)}`);
|
|
46
56
|
return {
|
|
57
|
+
i18n,
|
|
47
58
|
documentModel: documents.executable,
|
|
48
59
|
documents: documents.authoring,
|
|
49
60
|
initialSources: decodedSources.success,
|
|
50
61
|
installation: decodedConfiguration.success
|
|
51
62
|
};
|
|
52
63
|
}
|
|
53
|
-
|
|
54
|
-
function resolveCmsInstallation(authored, componentMetadata, initialSources = [], authoring = []) {
|
|
64
|
+
function resolveCmsInstallation(authored, authoring, initialSources = []) {
|
|
55
65
|
const compiled = compileCmsConfiguration(authored, initialSources);
|
|
56
66
|
const configuration = compiled.installation;
|
|
57
67
|
const cleanup = configuration.cleanup === false ? void 0 : configuration.cleanup;
|
|
58
68
|
const media = configuration.media === false ? void 0 : configuration.media;
|
|
59
|
-
const bootstrap = Schema.
|
|
69
|
+
const bootstrap = Result.getOrThrow(Schema.decodeResult(StudioBootstrap)({
|
|
60
70
|
basePath: "/",
|
|
61
|
-
components: [...cmsComponentDefinitions(Object.fromEntries(Object.entries(componentMetadata).filter(([, component]) => !authoring.some((definition) => definition.name === component.kebabName)))), ...authoring].sort((left, right) => left.name.localeCompare(right.name)),
|
|
62
71
|
documents: compiled.documents,
|
|
63
72
|
media: {
|
|
64
73
|
enabled: media !== void 0,
|
|
65
74
|
maxUploadBytes: media?.maxUploadBytes ?? DEFAULT_MEDIA_MAX_UPLOAD_BYTES
|
|
66
75
|
},
|
|
76
|
+
seo: configuration.seo ?? makeDefaultSeoOptions(configuration.baseUrl),
|
|
67
77
|
version: "1"
|
|
68
|
-
});
|
|
78
|
+
}));
|
|
69
79
|
return {
|
|
80
|
+
formHandlers: Object.fromEntries(Object.entries(authored.forms ?? {}).map(([name, form]) => [name, form.onSubmit])),
|
|
81
|
+
authoring,
|
|
70
82
|
baseUrl: configuration.baseUrl,
|
|
71
83
|
bootstrap,
|
|
72
84
|
cleanup: {
|