@xleddyl/nuxt-cms 0.1.40 → 0.1.41
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/module.json +1 -1
- package/dist/module.mjs +13 -8
- package/dist/runtime/app/composables/cms-query-disabled.d.ts +7 -2
- package/dist/runtime/app/composables/cms-query-disabled.js +7 -3
- package/dist/runtime/app/composables/cms-query.d.ts +7 -2
- package/dist/runtime/app/composables/cms-query.js +8 -3
- package/dist/runtime/server/utils/graphql.js +7 -2
- package/dist/runtime/shared/graphql-sdl.d.ts +2 -3
- package/dist/runtime/shared/graphql-sdl.js +43 -24
- package/dist/runtime/shared/index.d.ts +3 -0
- package/dist/runtime/shared/index.js +9 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -9,8 +9,8 @@ import svgLoader from 'vite-svg-loader';
|
|
|
9
9
|
import { introspectionFromSchema, buildSchema } from 'graphql';
|
|
10
10
|
import { minifyIntrospection, outputIntrospectionFile } from 'gql.tada/internal';
|
|
11
11
|
import { createJiti } from 'jiti';
|
|
12
|
-
import { typeName, blockTypeName,
|
|
13
|
-
import { isMultiSelect, fieldConditions, isTranslatableField, isTranslatableMediaField, isRequiredField, isPrivateField, DEFAULT_MEDIA_MAX_FILE_SIZE } from '../dist/runtime/shared/index.js';
|
|
12
|
+
import { typeName, blockTypeName, blocksFieldTypeName, renderGraphqlSdl } from '../dist/runtime/shared/graphql-sdl.js';
|
|
13
|
+
import { isMultiSelect, fieldConditions, isTranslatableField, isTranslatableMediaField, isRequiredField, isPrivateField, mediaTypeFilter, DEFAULT_MEDIA_MAX_FILE_SIZE } from '../dist/runtime/shared/index.js';
|
|
14
14
|
import { scanMediaDirectory, readMediaFileMeta } from '../dist/runtime/server/utils/media-sync.js';
|
|
15
15
|
import { createHash } from 'node:crypto';
|
|
16
16
|
import { readFile } from 'node:fs/promises';
|
|
@@ -450,6 +450,10 @@ ${[mediaTableExpr(dialect), ...tables, ...joins].join(
|
|
|
450
450
|
`;
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
+
function mediaTsType(field) {
|
|
454
|
+
const types = mediaTypeFilter(field.mediaType);
|
|
455
|
+
return types ? `CmsMedia<${types.map((type) => `'${type}'`).join(" | ")}>` : "CmsMedia";
|
|
456
|
+
}
|
|
453
457
|
function scalarTsType(field) {
|
|
454
458
|
switch (field.type) {
|
|
455
459
|
case "number":
|
|
@@ -470,13 +474,13 @@ function fieldTsType(config, entryName, key, field) {
|
|
|
470
474
|
if (field.cardinality === "many-to-many") return `${target}[]`;
|
|
471
475
|
return isRequiredField(field) && !config[field.to]?.drafts ? target : `${target} | null`;
|
|
472
476
|
}
|
|
473
|
-
if (field.type === "media") return
|
|
477
|
+
if (field.type === "media") return `${mediaTsType(field)} | null`;
|
|
474
478
|
if (field.type === "select" && field.multiple) {
|
|
475
479
|
const union = field.options.map((o) => JSON.stringify(o)).join(" | ");
|
|
476
480
|
return `(${union})[]`;
|
|
477
481
|
}
|
|
478
482
|
if (field.type === "blocks") {
|
|
479
|
-
const union =
|
|
483
|
+
const union = blocksFieldTypeName(entryName, key);
|
|
480
484
|
return isRequiredField(field) ? `${union}[]` : `${union}[] | null`;
|
|
481
485
|
}
|
|
482
486
|
if (isTranslatableField(field)) return isRequiredField(field) ? "string" : "string | null";
|
|
@@ -492,7 +496,7 @@ function blockTypesTs(entryName, key, field) {
|
|
|
492
496
|
const lines = [` __typename?: '${name}'`, ` type: '${blockName}'`];
|
|
493
497
|
for (const [blockFieldKey, blockField] of Object.entries(block.fields)) {
|
|
494
498
|
if (blockField.type === "media") {
|
|
495
|
-
lines.push(` ${blockFieldKey}:
|
|
499
|
+
lines.push(` ${blockFieldKey}: ${mediaTsType(blockField)} | null`);
|
|
496
500
|
continue;
|
|
497
501
|
}
|
|
498
502
|
const base = scalarTsType(blockField);
|
|
@@ -503,7 +507,7 @@ ${lines.join("\n")}
|
|
|
503
507
|
}`);
|
|
504
508
|
}
|
|
505
509
|
if (members.length)
|
|
506
|
-
defs.push(`export type ${
|
|
510
|
+
defs.push(`export type ${blocksFieldTypeName(entryName, key)} = ${members.join(" | ")}`);
|
|
507
511
|
return defs;
|
|
508
512
|
}
|
|
509
513
|
function entryTs(config, name, entry) {
|
|
@@ -520,10 +524,11 @@ ${lines.join("\n")}
|
|
|
520
524
|
}
|
|
521
525
|
function renderTypesFile(config) {
|
|
522
526
|
const parts = [
|
|
523
|
-
`export
|
|
527
|
+
`export type CmsMediaType = 'image' | 'video' | 'file'`,
|
|
528
|
+
`export interface CmsMedia<T extends CmsMediaType = CmsMediaType> {
|
|
524
529
|
key: string
|
|
525
530
|
url: string | null
|
|
526
|
-
type:
|
|
531
|
+
type: T
|
|
527
532
|
alt: string | null
|
|
528
533
|
folder: string | null
|
|
529
534
|
mime: string | null
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type { AsyncData } from 'nuxt/app';
|
|
1
|
+
import type { AsyncData, AsyncDataOptions } from 'nuxt/app';
|
|
2
2
|
type CmsDisabledResult = Record<string, any>;
|
|
3
3
|
type CmsDisabledVariables = Record<string, any>;
|
|
4
|
+
export interface CmsAsyncDataOptions<ResT, DefaultT> extends Pick<AsyncDataOptions<ResT>, 'server' | 'lazy' | 'immediate' | 'deep' | 'dedupe' | 'watch'> {
|
|
5
|
+
key?: string;
|
|
6
|
+
default?: () => DefaultT;
|
|
7
|
+
}
|
|
8
|
+
export declare function cmsQueryKey(query: string, variables?: unknown): string;
|
|
4
9
|
export declare function $cmsQuery<const Q extends string>(query: Q, variables?: CmsDisabledVariables): Promise<CmsDisabledResult>;
|
|
5
|
-
export declare function useCms<const Q extends string>(query: Q, variables?: CmsDisabledVariables): AsyncData<CmsDisabledResult | undefined, Error | undefined>;
|
|
10
|
+
export declare function useCms<const Q extends string, DefaultT = undefined>(query: Q, variables?: CmsDisabledVariables, options?: CmsAsyncDataOptions<CmsDisabledResult, DefaultT>): AsyncData<CmsDisabledResult | DefaultT | undefined, Error | undefined>;
|
|
6
11
|
export {};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { useAsyncData } from "#imports";
|
|
2
|
+
export function cmsQueryKey(query, variables) {
|
|
3
|
+
return `cms-gql:${query}:${JSON.stringify(variables ?? {})}`;
|
|
4
|
+
}
|
|
2
5
|
export async function $cmsQuery(query, variables) {
|
|
3
6
|
return {};
|
|
4
7
|
}
|
|
5
|
-
export function useCms(query, variables) {
|
|
8
|
+
export function useCms(query, variables, options = {}) {
|
|
9
|
+
const { key, default: defaultValue } = options;
|
|
6
10
|
return useAsyncData(
|
|
7
|
-
|
|
8
|
-
async () => null
|
|
11
|
+
key ?? cmsQueryKey(query, variables),
|
|
12
|
+
async () => defaultValue ? defaultValue() : null
|
|
9
13
|
);
|
|
10
14
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { CmsResult, CmsVariables } from '#cms-graphql';
|
|
2
|
-
import type { AsyncData } from 'nuxt/app';
|
|
2
|
+
import type { AsyncData, AsyncDataOptions } from 'nuxt/app';
|
|
3
|
+
export interface CmsAsyncDataOptions<ResT, DefaultT> extends Pick<AsyncDataOptions<ResT>, 'server' | 'lazy' | 'immediate' | 'deep' | 'dedupe' | 'watch'> {
|
|
4
|
+
key?: string;
|
|
5
|
+
default?: () => DefaultT;
|
|
6
|
+
}
|
|
7
|
+
export declare function cmsQueryKey(query: string, variables?: unknown): string;
|
|
3
8
|
export declare function $cmsQuery<const Q extends string>(query: Q, variables?: CmsVariables<Q>): Promise<CmsResult<Q>>;
|
|
4
|
-
export declare function useCms<const Q extends string>(query: Q, variables?: CmsVariables<Q>): AsyncData<CmsResult<Q> | undefined, Error | undefined>;
|
|
9
|
+
export declare function useCms<const Q extends string, DefaultT = undefined>(query: Q, variables?: CmsVariables<Q>, options?: CmsAsyncDataOptions<CmsResult<Q>, DefaultT>): AsyncData<CmsResult<Q> | DefaultT | undefined, Error | undefined>;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { useAsyncData } from "#imports";
|
|
2
2
|
const ENDPOINT = "/api/cms/graphql";
|
|
3
|
+
export function cmsQueryKey(query, variables) {
|
|
4
|
+
return `cms-gql:${query}:${JSON.stringify(variables ?? {})}`;
|
|
5
|
+
}
|
|
3
6
|
export async function $cmsQuery(query, variables) {
|
|
4
7
|
const res = await $fetch(ENDPOINT, {
|
|
5
8
|
method: "POST",
|
|
@@ -10,9 +13,11 @@ export async function $cmsQuery(query, variables) {
|
|
|
10
13
|
}
|
|
11
14
|
return res.data;
|
|
12
15
|
}
|
|
13
|
-
export function useCms(query, variables) {
|
|
16
|
+
export function useCms(query, variables, options = {}) {
|
|
17
|
+
const { key, ...asyncDataOptions } = options;
|
|
14
18
|
return useAsyncData(
|
|
15
|
-
|
|
16
|
-
() => $cmsQuery(query, variables)
|
|
19
|
+
key ?? cmsQueryKey(query, variables),
|
|
20
|
+
() => $cmsQuery(query, variables),
|
|
21
|
+
asyncDataOptions
|
|
17
22
|
);
|
|
18
23
|
}
|
|
@@ -30,7 +30,12 @@ import {
|
|
|
30
30
|
pickTranslatedMedia,
|
|
31
31
|
translatableFieldKeys
|
|
32
32
|
} from "../../shared/index.js";
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
blockTypeName,
|
|
35
|
+
blocksFieldTypeName,
|
|
36
|
+
renderGraphqlSdl,
|
|
37
|
+
typeName
|
|
38
|
+
} from "../../shared/graphql-sdl.js";
|
|
34
39
|
import { useMediaIndex } from "./media-index.js";
|
|
35
40
|
import { getContentI18n, resolveTable, tableColumns } from "./registry.js";
|
|
36
41
|
const MAX_LIMIT = 100;
|
|
@@ -285,7 +290,7 @@ function entryResolvers(config, name, entry) {
|
|
|
285
290
|
}
|
|
286
291
|
function blockResolvers(name, key, field) {
|
|
287
292
|
const resolvers = {};
|
|
288
|
-
resolvers[
|
|
293
|
+
resolvers[blocksFieldTypeName(name, key)] = {
|
|
289
294
|
__resolveType: (value) => blockTypeName(name, key, String(value.type))
|
|
290
295
|
};
|
|
291
296
|
for (const [blockName, block] of Object.entries(field.blocks ?? {})) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { CmsConfig } from './index.js';
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
export declare function blockTypeName(entryName: string, fieldKey: string, blockName: string): string;
|
|
2
|
+
import { blockTypeName, blocksFieldTypeName, typeName } from './index.js';
|
|
3
|
+
export { blockTypeName, blocksFieldTypeName, typeName };
|
|
5
4
|
export declare function renderGraphqlSdl(config: CmsConfig): string;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
blockTypeName,
|
|
3
|
+
blocksFieldTypeName,
|
|
4
|
+
isPrivateField,
|
|
5
|
+
isRequiredField,
|
|
6
|
+
isTranslatableField,
|
|
7
|
+
typeName
|
|
8
|
+
} from "./index.js";
|
|
9
|
+
export { blockTypeName, blocksFieldTypeName, typeName };
|
|
11
10
|
function scalarFor(field) {
|
|
12
11
|
switch (field.type) {
|
|
13
12
|
case "number":
|
|
@@ -58,7 +57,7 @@ function fieldSdl(config, entryName, key, field) {
|
|
|
58
57
|
if (field.type === "media") return ` ${key}: CmsMedia`;
|
|
59
58
|
if (field.type === "select" && field.multiple) return ` ${key}: [String!]!`;
|
|
60
59
|
if (field.type === "blocks")
|
|
61
|
-
return ` ${key}: [${
|
|
60
|
+
return ` ${key}: [${blocksFieldTypeName(entryName, key)}!]${isRequiredField(field) ? "!" : ""}`;
|
|
62
61
|
return ` ${key}: ${scalarFor(field)}${isRequiredField(field) ? "!" : ""}`;
|
|
63
62
|
}
|
|
64
63
|
function entrySdl(config, name, entry) {
|
|
@@ -73,25 +72,44 @@ function entrySdl(config, name, entry) {
|
|
|
73
72
|
${lines.join("\n")}
|
|
74
73
|
}`;
|
|
75
74
|
}
|
|
75
|
+
function blockFieldSdl(field) {
|
|
76
|
+
if (field.type === "media") return { base: "CmsMedia", nonNull: false };
|
|
77
|
+
return { base: scalarFor(field), nonNull: !!field.required };
|
|
78
|
+
}
|
|
79
|
+
function sharedBlockFieldsSdl(field) {
|
|
80
|
+
const blocks = Object.values(field.blocks ?? {});
|
|
81
|
+
const first = blocks[0];
|
|
82
|
+
if (!first) return [];
|
|
83
|
+
const lines = [];
|
|
84
|
+
for (const key of Object.keys(first.fields)) {
|
|
85
|
+
const rendered = blocks.map((block) => block.fields[key]);
|
|
86
|
+
if (rendered.some((blockField) => !blockField)) continue;
|
|
87
|
+
const types = rendered.map((blockField) => blockFieldSdl(blockField));
|
|
88
|
+
if (types.some((type) => type.base !== types[0].base)) continue;
|
|
89
|
+
const nonNull = types.every((type) => type.nonNull);
|
|
90
|
+
lines.push(` ${key}: ${types[0].base}${nonNull ? "!" : ""}`);
|
|
91
|
+
}
|
|
92
|
+
return lines;
|
|
93
|
+
}
|
|
76
94
|
function blocksSdl(name, key, field) {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
95
|
+
const blocks = Object.entries(field.blocks ?? {});
|
|
96
|
+
if (!blocks.length) return [];
|
|
97
|
+
const interfaceName = blocksFieldTypeName(name, key);
|
|
98
|
+
const shared = [" type: String!", ...sharedBlockFieldsSdl(field)];
|
|
99
|
+
const defs = [`interface ${interfaceName} {
|
|
100
|
+
${shared.join("\n")}
|
|
101
|
+
}`];
|
|
102
|
+
for (const [blockName, block] of blocks) {
|
|
82
103
|
const lines = [" type: String!"];
|
|
83
104
|
for (const [blockFieldKey, blockField] of Object.entries(block.fields)) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
lines.push(` ${blockFieldKey}: ${scalarFor(blockField)}${blockField.required ? "!" : ""}`);
|
|
105
|
+
const type = blockFieldSdl(blockField);
|
|
106
|
+
lines.push(` ${blockFieldKey}: ${type.base}${type.nonNull ? "!" : ""}`);
|
|
89
107
|
}
|
|
90
|
-
|
|
108
|
+
const gqlType = blockTypeName(name, key, blockName);
|
|
109
|
+
defs.push(`type ${gqlType} implements ${interfaceName} {
|
|
91
110
|
${lines.join("\n")}
|
|
92
111
|
}`);
|
|
93
112
|
}
|
|
94
|
-
if (members.length) defs.push(`union ${blockUnionName(name, key)} = ${members.join(" | ")}`);
|
|
95
113
|
return defs;
|
|
96
114
|
}
|
|
97
115
|
function filterSdl(name, entry) {
|
|
@@ -119,7 +137,8 @@ const COMMON_SDL = [
|
|
|
119
137
|
"input FloatFilter {\n eq: Float\n neq: Float\n gt: Float\n gte: Float\n lt: Float\n lte: Float\n in: [Float!]\n isNull: Boolean\n}",
|
|
120
138
|
"input StringFilter {\n eq: String\n neq: String\n gt: String\n gte: String\n lt: String\n lte: String\n like: String\n in: [String!]\n isNull: Boolean\n}",
|
|
121
139
|
"input BooleanFilter {\n eq: Boolean\n neq: Boolean\n isNull: Boolean\n}",
|
|
122
|
-
"
|
|
140
|
+
"enum CmsMediaType {\n image\n video\n file\n}",
|
|
141
|
+
"type CmsMedia {\n key: String!\n url: String\n type: CmsMediaType!\n alt: String\n folder: String\n mime: String\n size: Int\n width: Int\n height: Int\n}"
|
|
123
142
|
];
|
|
124
143
|
export function renderGraphqlSdl(config) {
|
|
125
144
|
const queryLines = [];
|
|
@@ -98,6 +98,9 @@ export interface CmsEntry {
|
|
|
98
98
|
table?: CmsTable;
|
|
99
99
|
}
|
|
100
100
|
export type CmsConfig = Record<string, CmsEntry>;
|
|
101
|
+
export declare function typeName(name: string): string;
|
|
102
|
+
export declare function blocksFieldTypeName(entryName: string, fieldKey: string): string;
|
|
103
|
+
export declare function blockTypeName(entryName: string, fieldKey: string, blockName: string): string;
|
|
101
104
|
interface FieldInputBase {
|
|
102
105
|
label: string;
|
|
103
106
|
required?: boolean;
|
|
@@ -184,6 +184,15 @@ export function localizeBlocks(field, value, locale, defaultLocale) {
|
|
|
184
184
|
if (!Array.isArray(value)) return value;
|
|
185
185
|
return value.map((item) => localizeBlock(field, item, locale, defaultLocale));
|
|
186
186
|
}
|
|
187
|
+
export function typeName(name) {
|
|
188
|
+
return name.replace(/(?:^|_)([a-z0-9])/gi, (_, c) => c.toUpperCase());
|
|
189
|
+
}
|
|
190
|
+
export function blocksFieldTypeName(entryName, fieldKey) {
|
|
191
|
+
return `${typeName(entryName)}${typeName(fieldKey)}Block`;
|
|
192
|
+
}
|
|
193
|
+
export function blockTypeName(entryName, fieldKey, blockName) {
|
|
194
|
+
return `${typeName(entryName)}${typeName(fieldKey)}${typeName(blockName)}`;
|
|
195
|
+
}
|
|
187
196
|
export function defineCmsConfig(config) {
|
|
188
197
|
return config;
|
|
189
198
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xleddyl/nuxt-cms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
4
4
|
"description": "Lightweight CMS that ships with your Nuxt app: runs on the Nitro server, content types defined in code, /cms admin panel, GraphQL API, SQLite or Postgres. No external CMS needed!",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Edoardo Alberti (https://github.com/xleddyl)",
|