@hywax/cms 0.0.2 → 0.0.4
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/.nuxt/cms/http-codes.ts +8 -0
- package/.nuxt/cms/index.ts +2 -1
- package/.nuxt/cms/uplora-image.ts +8 -0
- package/cli/commands/make/component.mjs +75 -0
- package/cli/commands/make/index.mjs +12 -0
- package/cli/index.mjs +15 -0
- package/cli/package.json +13 -0
- package/cli/templates.mjs +101 -0
- package/cli/utils.mjs +31 -0
- package/dist/module.d.mts +27 -3
- package/dist/module.json +1 -1
- package/dist/module.mjs +122 -31
- package/dist/runtime/components/ButtonClear.vue +1 -2
- package/dist/runtime/components/UploraImage.vue +108 -0
- package/dist/runtime/components/UploraImage.vue.d.ts +34 -0
- package/dist/runtime/components/prose/UploraImage.vue +1 -2
- package/dist/runtime/composables/useApi.d.ts +14 -0
- package/dist/runtime/composables/useApi.js +16 -0
- package/dist/runtime/composables/useSeoStats.d.ts +12 -0
- package/dist/runtime/composables/useSeoStats.js +44 -0
- package/dist/runtime/plugins/api.d.ts +6 -0
- package/dist/runtime/plugins/api.js +23 -0
- package/dist/runtime/server/errors/InternalHttpError.d.ts +4 -0
- package/dist/runtime/server/errors/InternalHttpError.js +6 -0
- package/dist/runtime/server/errors/TimeoutError.d.ts +2 -0
- package/dist/runtime/server/errors/TimeoutError.js +2 -0
- package/dist/runtime/server/errors/index.d.ts +2 -0
- package/dist/runtime/server/errors/index.js +2 -0
- package/dist/runtime/server/types/errors.d.ts +8 -0
- package/dist/runtime/server/types/errors.js +0 -0
- package/dist/runtime/server/types/index.d.ts +1 -0
- package/dist/runtime/server/types/index.js +1 -0
- package/dist/runtime/server/utils/errors.d.ts +8 -0
- package/dist/runtime/server/utils/errors.js +57 -0
- package/dist/runtime/server/utils/httpHandler.d.ts +10 -0
- package/dist/runtime/server/utils/httpHandler.js +15 -0
- package/dist/runtime/server/utils/pagination.d.ts +11 -0
- package/dist/runtime/server/utils/pagination.js +12 -0
- package/dist/runtime/server/utils/timeout.d.ts +7 -0
- package/dist/runtime/server/utils/timeout.js +9 -0
- package/dist/runtime/server/utils/validation.d.ts +3 -0
- package/dist/runtime/server/utils/validation.js +26 -0
- package/dist/runtime/types/image.d.ts +48 -0
- package/dist/runtime/types/image.js +0 -0
- package/dist/runtime/types/index.d.ts +4 -0
- package/dist/runtime/types/index.js +4 -0
- package/dist/runtime/types/query.d.ts +20 -0
- package/dist/runtime/types/query.js +0 -0
- package/dist/runtime/types/seo.d.ts +4 -0
- package/dist/runtime/types/seo.js +0 -0
- package/dist/runtime/types/utils.d.ts +4 -1
- package/dist/runtime/utils/avatar.d.ts +1 -0
- package/dist/runtime/utils/avatar.js +9 -0
- package/dist/runtime/utils/dictionaries.d.ts +4 -0
- package/dist/runtime/utils/dictionaries.js +6 -0
- package/dist/runtime/utils/image.d.ts +30 -0
- package/dist/runtime/utils/image.js +81 -0
- package/dist/runtime/utils/index.d.ts +4 -1
- package/dist/runtime/utils/index.js +4 -0
- package/dist/runtime/utils/slugify.d.ts +1 -0
- package/dist/runtime/utils/slugify.js +12 -0
- package/dist/types.d.mts +6 -2
- package/package.json +17 -3
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const HTTP_CODE_BAD_REQUEST = '400: Неверный запрос'
|
|
2
|
+
export const HTTP_CODE_UNAUTHORIZED = '401: Не авторизован'
|
|
3
|
+
export const HTTP_CODE_FORBIDDEN = '403: Доступ запрещен'
|
|
4
|
+
export const HTTP_CODE_NOT_FOUND = '404: Не найдено'
|
|
5
|
+
export const HTTP_CODE_REQUEST_TIMEOUT = '408: Время ожидания запроса истекло'
|
|
6
|
+
export const HTTP_CODE_INTERNAL_SERVER_ERROR = '500: Внутренняя ошибка сервера'
|
|
7
|
+
export const HTTP_CODE_BAD_GATEWAY = '502: Ошибка шлюза'
|
|
8
|
+
export const HTTP_CODE_SERVICE_UNAVAILABLE = '503: Сервис недоступен'
|
package/.nuxt/cms/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { default as buttonClear } from './button-clear'
|
|
1
|
+
export { default as buttonClear } from './button-clear'
|
|
2
|
+
export { default as uploraImage } from './uplora-image'
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { existsSync, promises as fsp } from 'node:fs'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
import { defineCommand } from 'citty'
|
|
4
|
+
import { consola } from 'consola'
|
|
5
|
+
import { resolve } from 'pathe'
|
|
6
|
+
import { camelCase, kebabCase, splitByCase, upperFirst } from 'scule'
|
|
7
|
+
import templates from '../../templates.mjs'
|
|
8
|
+
import { appendFile, sortFile } from '../../utils.mjs'
|
|
9
|
+
|
|
10
|
+
export default defineCommand({
|
|
11
|
+
meta: {
|
|
12
|
+
name: 'component',
|
|
13
|
+
description: 'Создать новый компонент',
|
|
14
|
+
},
|
|
15
|
+
args: {
|
|
16
|
+
name: {
|
|
17
|
+
type: 'positional',
|
|
18
|
+
required: true,
|
|
19
|
+
description: 'Имя компонента',
|
|
20
|
+
},
|
|
21
|
+
prose: {
|
|
22
|
+
type: 'boolean',
|
|
23
|
+
description: 'Создать компонент для контента',
|
|
24
|
+
},
|
|
25
|
+
template: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Сгенерировать только шаблон',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
async setup({ args }) {
|
|
31
|
+
const name = args.name
|
|
32
|
+
if (!name) {
|
|
33
|
+
consola.error('`name` argument is missing!')
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const path = resolve('.')
|
|
38
|
+
|
|
39
|
+
for (const template of Object.keys(templates)) {
|
|
40
|
+
if (args.template && template !== args.template) {
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { filename, contents } = templates[template](args)
|
|
45
|
+
if (!contents) {
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const filePath = resolve(path, filename)
|
|
50
|
+
|
|
51
|
+
if (existsSync(filePath)) {
|
|
52
|
+
consola.error(`🚨 ${filePath} уже существует!`)
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await fsp.writeFile(filePath, `${contents.trim()}\n`)
|
|
57
|
+
|
|
58
|
+
consola.success(`🪄 ${filePath} создан!`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (args.template) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const themePath = resolve(path, `src/theme/${args.prose ? 'prose/' : ''}index.ts`)
|
|
66
|
+
await appendFile(themePath, `export { default as ${camelCase(name)} } from './${kebabCase(name)}'`)
|
|
67
|
+
await sortFile(themePath)
|
|
68
|
+
|
|
69
|
+
if (!args.prose) {
|
|
70
|
+
const typesPath = resolve(path, 'src/runtime/types/index.ts')
|
|
71
|
+
await appendFile(typesPath, `export * from '../components/${splitByCase(name).map((p) => upperFirst(p)).join('')}.vue'`)
|
|
72
|
+
await sortFile(typesPath)
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
})
|
package/cli/index.mjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineCommand, runMain } from 'citty'
|
|
3
|
+
import make from './commands/make/index.mjs'
|
|
4
|
+
|
|
5
|
+
const main = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: 'cms',
|
|
8
|
+
description: 'Hywax CMS CLI',
|
|
9
|
+
},
|
|
10
|
+
subCommands: {
|
|
11
|
+
make,
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
runMain(main)
|
package/cli/package.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { camelCase, kebabCase, splitByCase, upperFirst } from 'scule'
|
|
2
|
+
|
|
3
|
+
function component({ name, prose }) {
|
|
4
|
+
const upperName = splitByCase(name).map((p) => upperFirst(p)).join('')
|
|
5
|
+
const camelName = camelCase(name)
|
|
6
|
+
const kebabName = kebabCase(name)
|
|
7
|
+
const path = 'cms'
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
filename: `src/runtime/components/${prose ? 'prose/' : ''}${upperName}.vue`,
|
|
11
|
+
contents: `
|
|
12
|
+
<template>
|
|
13
|
+
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
|
|
14
|
+
<slot />
|
|
15
|
+
</Primitive>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script lang="ts">
|
|
19
|
+
import type { AppConfig } from '@nuxt/schema'
|
|
20
|
+
import type { ComponentConfig } from '${prose ? '../../types' : '../types'}'
|
|
21
|
+
import theme from '#build/${path}/${prose ? 'prose/' : ''}${kebabName}'
|
|
22
|
+
import { computed, useAppConfig } from '#imports'
|
|
23
|
+
import { Primitive } from 'reka-ui'
|
|
24
|
+
import { tv } from '${prose ? '../../utils/tv' : '../utils/tv'}'
|
|
25
|
+
|
|
26
|
+
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}>
|
|
27
|
+
|
|
28
|
+
export interface ${upperName}Props {
|
|
29
|
+
as?: any
|
|
30
|
+
class?: any
|
|
31
|
+
ui?: ${upperName}['slots']
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ${upperName}Emits {
|
|
35
|
+
// 'update:modelValue': [string]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ${upperName}Slots {
|
|
39
|
+
default?: () => any
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<script setup lang="ts">
|
|
44
|
+
const props = defineProps<${upperName}Props>()
|
|
45
|
+
const emit = defineEmits<${upperName}Emits>()
|
|
46
|
+
defineSlots<${upperName}Slots>()
|
|
47
|
+
|
|
48
|
+
const appConfig = useAppConfig() as ${upperName}['AppConfig']
|
|
49
|
+
|
|
50
|
+
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.cms?.${camelName} || {}) })())
|
|
51
|
+
</script>
|
|
52
|
+
`,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function test({ name, prose }) {
|
|
57
|
+
const upperName = splitByCase(name).map((p) => upperFirst(p)).join('')
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
filename: `test/runtime/components/${upperName}.spec.ts`,
|
|
61
|
+
contents: prose
|
|
62
|
+
? undefined
|
|
63
|
+
: `
|
|
64
|
+
import type { ${upperName}Props, ${upperName}Slots } from '../../../src/runtime/components/${upperName}.vue'
|
|
65
|
+
import { describe, it, expect } from 'vitest'
|
|
66
|
+
import ${upperName} from '../../../src/runtime/components/${upperName}.vue'
|
|
67
|
+
import ComponentRender from '../../component-render'
|
|
68
|
+
|
|
69
|
+
describe('${upperName}', () => {
|
|
70
|
+
it.each([
|
|
71
|
+
// Props
|
|
72
|
+
['с алиасом', { props: { as: 'section' } }],
|
|
73
|
+
['с class', { props: { class: '' } }],
|
|
74
|
+
['с ui', { props: { ui: {} } }],
|
|
75
|
+
// Slots
|
|
76
|
+
['с дефолтным слотом', { slots: { default: () => 'Default slot' } }]
|
|
77
|
+
])('рендерит %s корректно', async (nameOrHtml: string, options: { props?: ${upperName}Props, slots?: Partial<${upperName}Slots> }) => {
|
|
78
|
+
const html = await ComponentRender(nameOrHtml, options, ${upperName})
|
|
79
|
+
expect(html).toMatchSnapshot()
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
`,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function theme({ name, prose }) {
|
|
87
|
+
const kebabName = kebabCase(name)
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
filename: `src/theme/${prose ? 'prose/' : ''}${kebabName}.ts`,
|
|
91
|
+
contents: `
|
|
92
|
+
export default {
|
|
93
|
+
slots: {
|
|
94
|
+
root: '',
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
`,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default { component, test, theme }
|
package/cli/utils.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { promises as fsp } from 'node:fs'
|
|
2
|
+
|
|
3
|
+
export async function sortFile(path) {
|
|
4
|
+
const file = await fsp.readFile(path, 'utf-8')
|
|
5
|
+
|
|
6
|
+
const lines = file.trim().split('\n').sort()
|
|
7
|
+
|
|
8
|
+
await fsp.writeFile(path, `${lines.join('\n')}\n`)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function appendFile(path, contents) {
|
|
12
|
+
const file = await fsp.readFile(path, 'utf-8')
|
|
13
|
+
|
|
14
|
+
if (!file.includes(contents)) {
|
|
15
|
+
await fsp.writeFile(path, `${file.trim()}\n${contents}\n`)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function normalizeLocale(locale) {
|
|
20
|
+
if (!locale) {
|
|
21
|
+
return ''
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (locale.includes('_')) {
|
|
25
|
+
return locale.split('_')
|
|
26
|
+
.map((part, index) => index === 0 ? part.toLowerCase() : part.toUpperCase())
|
|
27
|
+
.join('-')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return locale.toLowerCase()
|
|
31
|
+
}
|
package/dist/module.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
export * from '../dist/runtime/types/index.js';
|
|
3
3
|
|
|
4
|
-
interface
|
|
4
|
+
interface CMSOptions {
|
|
5
5
|
/**
|
|
6
6
|
* Name of the module
|
|
7
7
|
* @defaultValue 'cms'
|
|
@@ -12,8 +12,32 @@ interface ModuleOptions {
|
|
|
12
12
|
* @defaultValue 'C'
|
|
13
13
|
*/
|
|
14
14
|
prefix?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Fluxor URL
|
|
17
|
+
* @defaultValue 'https://fluxor.uplora.ru'
|
|
18
|
+
*/
|
|
19
|
+
fluxorUrl?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Prefix for the environment variables
|
|
22
|
+
* @defaultValue 'APP_'
|
|
23
|
+
*/
|
|
24
|
+
envPrefix?: string;
|
|
25
|
+
/**
|
|
26
|
+
* HTTP codes
|
|
27
|
+
* @defaultValue {}
|
|
28
|
+
*/
|
|
29
|
+
httpCodes?: Record<string, string>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
declare module '@nuxt/schema' {
|
|
33
|
+
interface ConfigSchema {
|
|
34
|
+
public?: {
|
|
35
|
+
fluxorUrl: string;
|
|
36
|
+
version: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
15
39
|
}
|
|
16
|
-
|
|
40
|
+
|
|
41
|
+
declare const _default: _nuxt_schema.NuxtModule<CMSOptions, CMSOptions, false>;
|
|
17
42
|
|
|
18
43
|
export { _default as default };
|
|
19
|
-
export type { ModuleOptions };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,10 +1,75 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createResolver, addComponentsDir, addImportsDir, addPlugin, addImports, addServerImports, addServerImportsDir, addTypeTemplate, addTemplate, addServerTemplate, defineNuxtModule, hasNuxtModule, installModule } from '@nuxt/kit';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
|
-
import 'node:url';
|
|
4
|
-
import {
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname } from 'pathe';
|
|
5
|
+
import { snakeCase, kebabCase } from 'scule';
|
|
5
6
|
|
|
6
7
|
const name = "@hywax/cms";
|
|
7
|
-
const version = "0.0.
|
|
8
|
+
const version = "0.0.4";
|
|
9
|
+
|
|
10
|
+
function createContext(options, nuxt) {
|
|
11
|
+
const { resolve } = createResolver(import.meta.url);
|
|
12
|
+
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
|
|
14
|
+
return {
|
|
15
|
+
options,
|
|
16
|
+
resolve,
|
|
17
|
+
distDir,
|
|
18
|
+
runtimeDir,
|
|
19
|
+
nuxt
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getDefaultCMSConfig() {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
const defaultModuleOptions = {
|
|
27
|
+
name: "cms",
|
|
28
|
+
prefix: "C",
|
|
29
|
+
fluxorUrl: "https://fluxor.uplora.ru",
|
|
30
|
+
envPrefix: "APP_",
|
|
31
|
+
httpCodes: {
|
|
32
|
+
badRequest: "400: \u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441",
|
|
33
|
+
unauthorized: "401: \u041D\u0435 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u043E\u0432\u0430\u043D",
|
|
34
|
+
forbidden: "403: \u0414\u043E\u0441\u0442\u0443\u043F \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D",
|
|
35
|
+
notFound: "404: \u041D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E",
|
|
36
|
+
requestTimeout: "408: \u0412\u0440\u0435\u043C\u044F \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F \u0437\u0430\u043F\u0440\u043E\u0441\u0430 \u0438\u0441\u0442\u0435\u043A\u043B\u043E",
|
|
37
|
+
internalServerError: "500: \u0412\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u044F\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430",
|
|
38
|
+
badGateway: "502: \u041E\u0448\u0438\u0431\u043A\u0430 \u0448\u043B\u044E\u0437\u0430",
|
|
39
|
+
serviceUnavailable: "503: \u0421\u0435\u0440\u0432\u0438\u0441 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D"
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function transformHttpCodes(httpCodes) {
|
|
44
|
+
return Object.entries(httpCodes).map(([code, value]) => ({
|
|
45
|
+
code: `HTTP_CODE_${snakeCase(code).toUpperCase()}`,
|
|
46
|
+
value
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function prepareAutoImports({ resolve, options, nuxt }) {
|
|
51
|
+
const httpCodesPath = resolve(nuxt.options.buildDir, "cms/http-codes.ts");
|
|
52
|
+
const httpCodesImports = transformHttpCodes(options.httpCodes).map(({ code }) => ({ name: code, from: httpCodesPath }));
|
|
53
|
+
addComponentsDir({
|
|
54
|
+
path: resolve("./runtime/components"),
|
|
55
|
+
pathPrefix: false,
|
|
56
|
+
prefix: options?.prefix,
|
|
57
|
+
ignore: ["prose/**"]
|
|
58
|
+
});
|
|
59
|
+
addComponentsDir({
|
|
60
|
+
path: resolve("./runtime/components/prose"),
|
|
61
|
+
prefix: "Prose",
|
|
62
|
+
pathPrefix: false,
|
|
63
|
+
global: true
|
|
64
|
+
});
|
|
65
|
+
addImportsDir(resolve("./runtime/composables"));
|
|
66
|
+
addPlugin(resolve("./runtime/plugins/api.ts"));
|
|
67
|
+
addImports(httpCodesImports);
|
|
68
|
+
addServerImports(httpCodesImports);
|
|
69
|
+
addServerImportsDir(resolve("./runtime/server/utils"));
|
|
70
|
+
nuxt.options.nitro.alias ||= {};
|
|
71
|
+
nuxt.options.nitro.alias["#cms/http-codes"] = httpCodesPath;
|
|
72
|
+
}
|
|
8
73
|
|
|
9
74
|
const buttonClear = {
|
|
10
75
|
slots: {
|
|
@@ -12,9 +77,19 @@ const buttonClear = {
|
|
|
12
77
|
}
|
|
13
78
|
};
|
|
14
79
|
|
|
80
|
+
const uploraImage$1 = {
|
|
81
|
+
slots: {
|
|
82
|
+
root: "relative grid grid-cols-[100%] grid-rows-[100%] overflow-hidden",
|
|
83
|
+
lqip: "bg-cover bg-no-repeat bg-center col-[1] row-[1]",
|
|
84
|
+
picture: "aspect-3/2 col-[1] row-[1]",
|
|
85
|
+
img: "w-full h-full block object-cover"
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
15
89
|
const theme = {
|
|
16
90
|
__proto__: null,
|
|
17
|
-
buttonClear: buttonClear
|
|
91
|
+
buttonClear: buttonClear,
|
|
92
|
+
uploraImage: uploraImage$1
|
|
18
93
|
};
|
|
19
94
|
|
|
20
95
|
const uploraImage = {
|
|
@@ -28,7 +103,7 @@ const themeProse = {
|
|
|
28
103
|
uploraImage: uploraImage
|
|
29
104
|
};
|
|
30
105
|
|
|
31
|
-
function
|
|
106
|
+
function getAppTemplates({ options }) {
|
|
32
107
|
const templates = [];
|
|
33
108
|
function writeThemeTemplate(theme2, path) {
|
|
34
109
|
for (const component in theme2) {
|
|
@@ -87,8 +162,11 @@ function getTemplates(options) {
|
|
|
87
162
|
filename: "types/cms.d.ts",
|
|
88
163
|
getContents: () => `import * as cms from '#build/cms'
|
|
89
164
|
import type { TVConfig } from '@nuxt/ui'
|
|
165
|
+
import type { RouteLocationRaw } from 'vue-router'
|
|
166
|
+
|
|
167
|
+
type AppConfigCMS = {
|
|
90
168
|
|
|
91
|
-
|
|
169
|
+
} & TVConfig<typeof cms>
|
|
92
170
|
|
|
93
171
|
declare module '@nuxt/schema' {
|
|
94
172
|
interface AppConfigInput {
|
|
@@ -102,17 +180,39 @@ declare module '@nuxt/schema' {
|
|
|
102
180
|
export {}
|
|
103
181
|
`
|
|
104
182
|
});
|
|
183
|
+
templates.push({
|
|
184
|
+
filename: "cms/http-codes.ts",
|
|
185
|
+
write: true,
|
|
186
|
+
getContents: () => {
|
|
187
|
+
const httpCodes = transformHttpCodes(options.httpCodes);
|
|
188
|
+
const content = httpCodes.map(({ code, value }) => `export const ${code} = '${value}'`).join("\n");
|
|
189
|
+
return `${content}
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
return templates;
|
|
194
|
+
}
|
|
195
|
+
function getServerTemplates(_context) {
|
|
196
|
+
const templates = [];
|
|
105
197
|
return templates;
|
|
106
198
|
}
|
|
107
|
-
function
|
|
108
|
-
const
|
|
109
|
-
|
|
199
|
+
function prepareTemplates(context) {
|
|
200
|
+
const appTemplates = getAppTemplates(context);
|
|
201
|
+
const serverTemplates = getServerTemplates();
|
|
202
|
+
for (const template of appTemplates) {
|
|
110
203
|
if (template.filename.endsWith(".d.ts")) {
|
|
111
204
|
addTypeTemplate(template);
|
|
112
205
|
} else {
|
|
113
206
|
addTemplate(template);
|
|
114
207
|
}
|
|
115
208
|
}
|
|
209
|
+
for (const template of serverTemplates) {
|
|
210
|
+
if (template.filename.endsWith(".d.ts")) {
|
|
211
|
+
addTypeTemplate(template, { nitro: true, nuxt: false });
|
|
212
|
+
} else {
|
|
213
|
+
addServerTemplate(template);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
116
216
|
}
|
|
117
217
|
|
|
118
218
|
const icons = {};
|
|
@@ -123,11 +223,15 @@ const module = defineNuxtModule({
|
|
|
123
223
|
version,
|
|
124
224
|
configKey: "cms"
|
|
125
225
|
},
|
|
126
|
-
defaults:
|
|
127
|
-
name: "cms",
|
|
128
|
-
prefix: "C"
|
|
129
|
-
},
|
|
226
|
+
defaults: defaultModuleOptions,
|
|
130
227
|
async setup(options, nuxt) {
|
|
228
|
+
const context = createContext(options, nuxt);
|
|
229
|
+
nuxt.options.runtimeConfig.public = defu(nuxt.options.runtimeConfig.public || {}, {
|
|
230
|
+
fluxorUrl: options.fluxorUrl,
|
|
231
|
+
version: ""
|
|
232
|
+
});
|
|
233
|
+
nuxt.options.runtimeConfig.nitro ||= {};
|
|
234
|
+
nuxt.options.runtimeConfig.nitro.envPrefix = options.envPrefix;
|
|
131
235
|
nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, {
|
|
132
236
|
icons
|
|
133
237
|
});
|
|
@@ -160,23 +264,10 @@ const module = defineNuxtModule({
|
|
|
160
264
|
if (!hasNuxtModule("nuxt-auth-utils")) {
|
|
161
265
|
await installModule("nuxt-auth-utils");
|
|
162
266
|
}
|
|
163
|
-
|
|
164
|
-
nuxt.options.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
path: resolve("./runtime/components"),
|
|
168
|
-
pathPrefix: false,
|
|
169
|
-
prefix: options?.prefix || "C",
|
|
170
|
-
ignore: ["prose/**"]
|
|
171
|
-
});
|
|
172
|
-
addComponentsDir({
|
|
173
|
-
path: resolve("./runtime/components/prose"),
|
|
174
|
-
prefix: "Prose",
|
|
175
|
-
pathPrefix: false,
|
|
176
|
-
global: true
|
|
177
|
-
});
|
|
178
|
-
addImportsDir(resolve("./runtime/composables"));
|
|
179
|
-
addTemplates(options);
|
|
267
|
+
nuxt.options.alias["#cms"] = context.resolve("./runtime");
|
|
268
|
+
nuxt.options.appConfig.cms = defu(nuxt.options.appConfig.cms || {}, getDefaultCMSConfig());
|
|
269
|
+
prepareAutoImports(context);
|
|
270
|
+
prepareTemplates(context);
|
|
180
271
|
}
|
|
181
272
|
});
|
|
182
273
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<figure :class="ui.root({ class: [props.ui?.root, props.class] })">
|
|
3
|
+
<div
|
|
4
|
+
v-if="lqip"
|
|
5
|
+
:class="ui.lqip({ class: props.ui?.lqip })"
|
|
6
|
+
:style="{
|
|
7
|
+
backgroundImage: `url(${lqip})`
|
|
8
|
+
}"
|
|
9
|
+
/>
|
|
10
|
+
<picture :class="ui.picture({ class: props.ui?.picture })">
|
|
11
|
+
<template v-if="sources.length">
|
|
12
|
+
<source
|
|
13
|
+
v-for="(source, key) in sources"
|
|
14
|
+
:key="key"
|
|
15
|
+
:srcset="source.srcset"
|
|
16
|
+
:type="source.type"
|
|
17
|
+
>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<img
|
|
21
|
+
ref="imageRef"
|
|
22
|
+
v-bind="imageAttrs"
|
|
23
|
+
:src="image.img"
|
|
24
|
+
:srcset="image?.srcset"
|
|
25
|
+
:alt="alt"
|
|
26
|
+
:loading="loading"
|
|
27
|
+
:class="ui.img({ class: props.ui?.img })"
|
|
28
|
+
>
|
|
29
|
+
</picture>
|
|
30
|
+
</figure>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script>
|
|
34
|
+
import theme from "#build/cms/uplora-image";
|
|
35
|
+
import { computed, onMounted, useAppConfig, useHead, useNuxtApp, useTemplateRef } from "#imports";
|
|
36
|
+
import { buildUploraImage } from "../utils";
|
|
37
|
+
import { tv } from "../utils/tv";
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<script setup>
|
|
41
|
+
const props = defineProps({
|
|
42
|
+
id: { type: String, required: true },
|
|
43
|
+
alt: { type: String, required: true },
|
|
44
|
+
formats: { type: Array, required: false },
|
|
45
|
+
sizes: { type: Array, required: false },
|
|
46
|
+
lqip: { type: String, required: false },
|
|
47
|
+
loading: { type: String, required: false, default: "lazy" },
|
|
48
|
+
preload: { type: [Boolean, Object], required: false },
|
|
49
|
+
nonce: { type: String, required: false },
|
|
50
|
+
imgAttrs: { type: Object, required: false },
|
|
51
|
+
class: { type: null, required: false },
|
|
52
|
+
ui: { type: null, required: false }
|
|
53
|
+
});
|
|
54
|
+
const emit = defineEmits(["load", "error"]);
|
|
55
|
+
const nuxtApp = useNuxtApp();
|
|
56
|
+
const initialLoad = nuxtApp.isHydrating;
|
|
57
|
+
const image = computed(() => buildUploraImage({
|
|
58
|
+
id: props.id,
|
|
59
|
+
formats: props.formats,
|
|
60
|
+
sizes: props.sizes
|
|
61
|
+
}));
|
|
62
|
+
const sources = computed(() => {
|
|
63
|
+
if (image.value.sources.length > 1) {
|
|
64
|
+
return image.value.sources.slice(1);
|
|
65
|
+
}
|
|
66
|
+
return [];
|
|
67
|
+
});
|
|
68
|
+
if (import.meta.server && props.preload) {
|
|
69
|
+
useHead({ link: () => {
|
|
70
|
+
if (!image.value.img) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const link = {
|
|
74
|
+
rel: "preload",
|
|
75
|
+
as: "image",
|
|
76
|
+
imagesrcset: image.value.srcset,
|
|
77
|
+
nonce: props.nonce,
|
|
78
|
+
...typeof props.preload !== "boolean" && props.preload?.fetchPriority ? { fetchpriority: props.preload.fetchPriority } : {}
|
|
79
|
+
};
|
|
80
|
+
return [link];
|
|
81
|
+
} });
|
|
82
|
+
}
|
|
83
|
+
const imageRef = useTemplateRef("imageRef");
|
|
84
|
+
const imageAttrs = computed(() => ({
|
|
85
|
+
...props.imgAttrs || {},
|
|
86
|
+
...import.meta.server ? { onerror: "this.setAttribute('data-error', 1)" } : {}
|
|
87
|
+
}));
|
|
88
|
+
onMounted(() => {
|
|
89
|
+
if (!imageRef.value) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (imageRef.value.complete && initialLoad) {
|
|
93
|
+
if (imageRef.value.getAttribute("data-error")) {
|
|
94
|
+
emit("error", new Event("error"));
|
|
95
|
+
} else {
|
|
96
|
+
emit("load", new Event("load"));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
imageRef.value.onload = (event) => {
|
|
100
|
+
emit("load", event);
|
|
101
|
+
};
|
|
102
|
+
imageRef.value.onerror = (event) => {
|
|
103
|
+
emit("error", event);
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
const appConfig = useAppConfig();
|
|
107
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.uploraImage || {} })());
|
|
108
|
+
</script>
|