Skip to content

Commit 7d4cdc8

Browse files
committed
feat(nuxt-json): locale metadata, query utils refactor, error codes and removal of deprecated client composable
1 parent 66d8be6 commit 7d4cdc8

File tree

8 files changed

+231
-114
lines changed

8 files changed

+231
-114
lines changed

packages/nuxt-json/CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @contentrain/nuxt-json
22

3+
## 1.1.0
4+
5+
- Add Nuxt 4 compatibility metadata
6+
- Update development dependencies to Nuxt 4.2.1 and tooling requirements
7+
- Skip asset build when the content directory is not configured to avoid runtime errors
8+
39
## 1.0.4
410

511
- Clean unnececary logs
@@ -22,6 +28,12 @@
2228

2329
## 1.0.0
2430

25-
### Major Changes
31+
## 1.2.0
32+
33+
- Locale dizisi build aşamasında metadata'ya ekleniyor (`model.locales`) – lokalizasyon mevcut dillerin tespiti.
34+
- Sunucu sorgu endpointinde filtre ve sıralama mantığı ortak utility'e taşındı (`runtime/utils/query.ts`).
35+
- `useContentrainClient` composable kaldırıldı; `useContentrainModels` doğrudan API çağrıları ve kendi cache yapısı ile sadeleştirildi.
36+
- Yeni hata kodları eklendi: `INVALID_URL_ERROR`, `INVALID_DATA_ERROR`, `INVALID_RELATION`, `RELATION_NOT_FOUND`.
37+
- Nuxt 4 uyumluluk refactor'ü sonrası iç mimari temizlik.
2638

2739
- Relase Nuxt Json query builder module.

packages/nuxt-json/package.json

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentrain/nuxt-json",
3-
"version": "1.0.4",
3+
"version": "1.2.0",
44
"description": "Contentrain JSON Module for Nuxt",
55
"repository": {
66
"type": "git",
@@ -21,6 +21,14 @@
2121
"files": [
2222
"dist"
2323
],
24+
"peerDependencies": {
25+
"nuxt": "^3.0.0 || ^4.0.0"
26+
},
27+
"peerDependenciesMeta": {
28+
"nuxt": {
29+
"optional": false
30+
}
31+
},
2432
"scripts": {
2533
"prepack": "nuxt-module-build build",
2634
"dev": "nuxi dev playground",
@@ -34,25 +42,26 @@
3442
"lint:fix": "eslint . --fix"
3543
},
3644
"dependencies": {
37-
"@nuxt/kit": "^3.15.4",
45+
"@nuxt/kit": "^3.0.0 || ^4.0.0",
3846
"defu": "^6.1.4",
3947
"unstorage": "^1.15.0"
4048
},
4149
"devDependencies": {
4250
"@nuxt/devtools": "^2.1.0",
4351
"@nuxt/eslint-config": "^1.1.0",
4452
"@nuxt/module-builder": "^0.8.4",
45-
"@nuxt/schema": "^3.15.4",
53+
"@nuxt/schema": "^3.0.0 || ^4.0.0",
4654
"@nuxt/test-utils": "^3.17.0",
4755
"@types/node": "^22.13.5",
4856
"changelogen": "^0.5.7",
4957
"defu": "^6.1.4",
5058
"eslint": "^9.21.0",
5159
"h3": "^1.15.1",
5260
"nitropack": "^2.10.4",
53-
"nuxt": "^3.15.4",
61+
"nuxt": "^4.2.1",
5462
"typescript": "~5.7.3",
5563
"vitest": "^3.0.7",
56-
"vue-tsc": "^2.2.4"
64+
"vue-tsc": "~2.2.10",
65+
"vue": "^3.5.13"
5766
}
5867
}

packages/nuxt-json/src/module.ts

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,23 @@ async function buildContentrainAssets(
1616
buildDir: string,
1717
options: ContentrainOptions,
1818
) {
19+
if (!contentDir) {
20+
console.warn('[Contentrain Module] Content directory is not configured; skipping asset build');
21+
return [];
22+
}
23+
1924
await fs.mkdir(buildDir, { recursive: true });
2025

2126
try {
2227
const metadataPath = join(contentDir, 'models', 'metadata.json');
23-
const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf-8'));
24-
await fs.copyFile(
25-
metadataPath,
26-
join(buildDir, 'metadata.json'),
27-
);
28+
const metadataExists = await fs.stat(metadataPath).then(() => true).catch(() => false);
29+
30+
if (!metadataExists) {
31+
console.warn('[Contentrain Module] Metadata file not found; skipping asset build');
32+
return [];
33+
}
34+
35+
const metadata: ModelMetadata[] = JSON.parse(await fs.readFile(metadataPath, 'utf-8'));
2836
await Promise.all(metadata.map(async (model: ModelMetadata) => {
2937
const modelDir = join(buildDir, model.modelId);
3038
await fs.mkdir(modelDir, { recursive: true });
@@ -48,6 +56,9 @@ async function buildContentrainAssets(
4856
langFiles = [options.defaultLocale || 'en'];
4957
}
5058

59+
// locales alanını doldur
60+
model.locales = langFiles.length ? langFiles : [options.defaultLocale || 'en'];
61+
5162
await Promise.all(langFiles.map(async (lang) => {
5263
const sourcePath = join(contentDir, model.modelId, `${lang}.json`);
5364
const targetPath = join(modelDir, `${lang}.json`);
@@ -75,6 +86,9 @@ async function buildContentrainAssets(
7586
}
7687
}));
7788

89+
// Yazılmış (mutated) metadata'yı build dizinine kaydet
90+
await fs.writeFile(join(buildDir, 'metadata.json'), JSON.stringify(metadata, null, 2), 'utf-8');
91+
7892
const assetsPath = join(contentDir, 'assets.json');
7993
if (await fs.stat(assetsPath).catch(() => false)) {
8094
await fs.copyFile(assetsPath, join(buildDir, 'assets.json'));
@@ -124,7 +138,7 @@ export default defineNuxtModule<ContentrainOptions>({
124138
name: '@contentrain/nuxt-json',
125139
configKey: 'contentrain',
126140
compatibility: {
127-
nuxt: '^3.0.0',
141+
nuxt: '^3.0.0 || ^4.0.0',
128142
},
129143
},
130144

@@ -140,6 +154,7 @@ export default defineNuxtModule<ContentrainOptions>({
140154
setup(options, nuxt) {
141155
const resolver = createResolver(import.meta.url);
142156
const resolve = resolver.resolve.bind(resolver);
157+
const hasContentDir = Boolean(options.path && options.path.trim().length > 0);
143158

144159
console.info('[Contentrain Module] Initializing module', {
145160
path: options.path,
@@ -175,22 +190,29 @@ export default defineNuxtModule<ContentrainOptions>({
175190
nuxt.hooks.hook('nitro:config', async (nitroConfig) => {
176191
console.info('[Contentrain Module] Configuring Nitro');
177192

178-
const buildDir = join(options.path, '.contentrain-build');
179-
await buildContentrainAssets(options.path, buildDir, options);
193+
let metadata: ModelMetadata[] = [];
194+
let buildDir: string | undefined;
195+
196+
if (hasContentDir) {
197+
buildDir = join(options.path, '.contentrain-build');
198+
metadata = await buildContentrainAssets(options.path, buildDir, options);
199+
}
180200

181201
nitroConfig.storage = nitroConfig.storage || {};
182202
nitroConfig.storage.data = {
183203
driver: options.storage?.driver || 'memory',
184204
base: options.storage?.base || '.contentrain',
185205
};
186206

187-
nitroConfig.serverAssets = nitroConfig.serverAssets || [];
188-
nitroConfig.serverAssets.push({
189-
baseName: 'contentrain',
190-
dir: buildDir,
191-
});
207+
if (buildDir) {
208+
nitroConfig.serverAssets = nitroConfig.serverAssets || [];
209+
nitroConfig.serverAssets.push({
210+
baseName: 'contentrain',
211+
dir: buildDir,
212+
});
213+
}
192214

193-
if (options.path) {
215+
if (hasContentDir) {
194216
const publicAssetsDir = join(options.path, 'public');
195217
nitroConfig.publicAssets = nitroConfig.publicAssets || [];
196218
nitroConfig.publicAssets.push({
@@ -202,21 +224,21 @@ export default defineNuxtModule<ContentrainOptions>({
202224
nitroConfig.prerender = nitroConfig.prerender || {};
203225
nitroConfig.prerender.routes = nitroConfig.prerender.routes || [];
204226

205-
try {
206-
const metadataPath = join(options.path, 'models', 'metadata.json');
207-
const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf-8'));
227+
if (hasContentDir && metadata.length) {
208228
for (const model of metadata) {
209229
const route = `/_contentrain/api/query?modelId=${model.modelId}`;
210230
nitroConfig.prerender.routes.push(route);
211231
}
212232
}
213-
catch (error) {
214-
console.warn('[Contentrain Module] Error during prerender configuration:', error);
233+
else if (!hasContentDir) {
234+
console.warn('[Contentrain Module] Skipping prerender configuration because content path is missing');
215235
}
216236

217237
nitroConfig.externals = nitroConfig.externals || {};
218238
nitroConfig.externals.inline = nitroConfig.externals.inline || [];
219-
nitroConfig.externals.inline.push(options.path);
239+
if (hasContentDir) {
240+
nitroConfig.externals.inline.push(options.path);
241+
}
220242

221243
nitroConfig.imports = {
222244
...nitroConfig.imports,
@@ -255,6 +277,12 @@ export default defineNuxtModule<ContentrainOptions>({
255277
addTypeTemplate({
256278
filename: 'types/contentrain.d.ts',
257279
getContents: async () => {
280+
if (!hasContentDir) {
281+
return `// Contentrain types are unavailable because the content directory is not configured.
282+
export {};
283+
`;
284+
}
285+
258286
try {
259287
const typeDefinitions = await typeGenerator.generateTypes();
260288
console.info('[Contentrain Module] Type definitions generated successfully');

packages/nuxt-json/src/runtime/composables/useContentrainModels.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import type { ModelData } from '../../types';
1+
import type { ApiResponse, ModelData } from '../../types';
22
import { computed, onMounted, ref } from 'vue';
33
import { ContentrainError, createError, ERROR_CODES } from '../server/utils/errors';
4-
import { useContentrainClient } from './useContentrainClient';
54

65
export interface UseContentrainModelsOptions {
76
autoLoad?: boolean
@@ -16,15 +15,15 @@ export function useContentrainModels(options: UseContentrainModelsOptions = {})
1615
retryDelay = 1000,
1716
} = options;
1817

19-
const client = useContentrainClient();
2018
const pending = ref(false);
2119
const error = ref<Error | null>(null);
2220
const retries = ref(0);
21+
const _models = ref<ModelData[]>([]);
22+
const _loaded = ref(false);
2323

24-
// Client'dan modelleri al
25-
const models = computed(() => client.models.value);
26-
const isLoaded = computed(() => client.isLoaded.value);
27-
const isLoading = computed(() => client.isLoading.value || pending.value);
24+
const models = computed(() => _models.value);
25+
const isLoaded = computed(() => _loaded.value);
26+
const isLoading = computed(() => pending.value);
2827

2928
/**
3029
* Tüm modelleri yükle
@@ -38,7 +37,18 @@ export function useContentrainModels(options: UseContentrainModelsOptions = {})
3837
error.value = null;
3938

4039
try {
41-
await client.loadModels();
40+
const res = await fetch('/_contentrain/api/models');
41+
const response = await res.json() as ApiResponse<ModelData[]>;
42+
if (!response.success || !response.data) {
43+
const payload = response.error;
44+
throw createError(
45+
(payload?.code as typeof ERROR_CODES[keyof typeof ERROR_CODES]) || ERROR_CODES.MODEL_LOAD_ERROR,
46+
payload,
47+
payload?.message,
48+
);
49+
}
50+
_models.value = response.data;
51+
_loaded.value = true;
4252
retries.value = 0;
4353
return models.value;
4454
}
@@ -71,10 +81,28 @@ export function useContentrainModels(options: UseContentrainModelsOptions = {})
7181
error.value = null;
7282

7383
try {
74-
const result = await client.loadModel(modelId);
84+
// Önce cache'de var mı?
85+
const cached = _models.value.find(m => m.metadata.modelId === modelId);
86+
if (cached) {
87+
return cached;
88+
}
89+
90+
const res = await fetch(`/_contentrain/api/models/${modelId}`);
91+
const response = await res.json() as ApiResponse<ModelData | null>;
92+
if (!response.success) {
93+
const payload = response.error;
94+
throw createError(
95+
(payload?.code as typeof ERROR_CODES[keyof typeof ERROR_CODES]) || ERROR_CODES.MODEL_LOAD_ERROR,
96+
payload,
97+
payload?.message,
98+
);
99+
}
100+
const result = response.data;
75101
if (!result) {
76102
throw createError(ERROR_CODES.MODEL_NOT_FOUND, { modelId });
77103
}
104+
_models.value = [..._models.value, result];
105+
retries.value = 0;
78106
return result;
79107
}
80108
catch (err) {
@@ -98,14 +126,14 @@ export function useContentrainModels(options: UseContentrainModelsOptions = {})
98126
* Model var mı kontrol et
99127
*/
100128
function hasModel(modelId: string): boolean {
101-
return models.value.some(model => model.metadata.modelId === modelId);
129+
return models.value.some((model: ModelData) => model.metadata.modelId === modelId);
102130
}
103131

104132
/**
105133
* Model getir
106134
*/
107135
function getModel(modelId: string): ModelData | undefined {
108-
return models.value.find(model => model.metadata.modelId === modelId);
136+
return models.value.find((model: ModelData) => model.metadata.modelId === modelId);
109137
}
110138

111139
// Otomatik yükleme

0 commit comments

Comments
 (0)