nextjs-cms 0.7.1 → 0.7.3
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/LICENSE +21 -21
- package/README.md +290 -290
- package/dist/api/index.d.ts +3 -3
- package/dist/api/lib/serverActions.d.ts +3 -3
- package/dist/api/root.d.ts +6 -6
- package/dist/api/routers/hasItemsSection.d.ts +2 -2
- package/dist/api/routers/simpleSection.d.ts +1 -1
- package/dist/cli/lib/db-config.js +10 -10
- package/dist/core/config/config-loader.d.ts +23 -3
- package/dist/core/config/config-loader.d.ts.map +1 -1
- package/dist/core/config/config-loader.js +32 -6
- package/dist/core/config/loader-with-jiti.d.ts.map +1 -1
- package/dist/core/config/loader-with-jiti.js +19 -15
- package/dist/core/db/table-checker/MysqlTable.js +8 -8
- package/dist/core/factories/section-factory-with-esbuild.d.ts.map +1 -1
- package/dist/core/factories/section-factory-with-esbuild.js +23 -9
- package/dist/core/factories/section-factory-with-jiti.d.ts.map +1 -1
- package/dist/core/factories/section-factory-with-jiti.js +51 -33
- package/dist/core/factories/section-name-validation.d.ts +10 -0
- package/dist/core/factories/section-name-validation.d.ts.map +1 -0
- package/dist/core/factories/section-name-validation.js +31 -0
- package/dist/core/fields/photo.d.ts +127 -85
- package/dist/core/fields/photo.d.ts.map +1 -1
- package/dist/core/fields/photo.js +83 -72
- package/dist/core/fields/richText.d.ts +9 -9
- package/dist/core/fields/select.d.ts +1 -1
- package/dist/core/sections/category.d.ts +42 -36
- package/dist/core/sections/category.d.ts.map +1 -1
- package/dist/core/sections/category.js +6 -1
- package/dist/core/sections/hasItems.d.ts +156 -54
- package/dist/core/sections/hasItems.d.ts.map +1 -1
- package/dist/core/sections/hasItems.js +6 -11
- package/dist/core/sections/section.d.ts +33 -20
- package/dist/core/sections/section.d.ts.map +1 -1
- package/dist/core/sections/section.js +37 -3
- package/dist/core/sections/simple.d.ts +14 -8
- package/dist/core/sections/simple.d.ts.map +1 -1
- package/dist/core/sections/simple.js +6 -1
- package/dist/core/submit/submit.js +1 -1
- package/dist/translations/client.d.ts.map +1 -1
- package/dist/translations/server.d.ts.map +1 -1
- package/dist/validators/photo.js +1 -1
- package/package.json +2 -1
- package/dist/translations/dictionaries/ar.d.ts +0 -433
- package/dist/translations/dictionaries/ar.d.ts.map +0 -1
- package/dist/translations/dictionaries/ar.js +0 -444
- package/dist/translations/dictionaries/en.d.ts +0 -433
- package/dist/translations/dictionaries/en.d.ts.map +0 -1
- package/dist/translations/dictionaries/en.js +0 -444
|
@@ -66,7 +66,7 @@ export declare const createSimpleSectionPage: (session: Session, sectionName: st
|
|
|
66
66
|
thumbnail?: {
|
|
67
67
|
width: number;
|
|
68
68
|
height: number;
|
|
69
|
-
|
|
69
|
+
fit: "cover" | "contain";
|
|
70
70
|
quality: number;
|
|
71
71
|
};
|
|
72
72
|
} | undefined;
|
|
@@ -113,7 +113,7 @@ export declare const createEditPage: (session: Session, sectionName: string, sec
|
|
|
113
113
|
thumbnail?: {
|
|
114
114
|
width: number;
|
|
115
115
|
height: number;
|
|
116
|
-
|
|
116
|
+
fit: "cover" | "contain";
|
|
117
117
|
quality: number;
|
|
118
118
|
};
|
|
119
119
|
} | undefined;
|
|
@@ -173,7 +173,7 @@ export declare const createNewPage: (session: Session, sectionName: string) => P
|
|
|
173
173
|
thumbnail?: {
|
|
174
174
|
width: number;
|
|
175
175
|
height: number;
|
|
176
|
-
|
|
176
|
+
fit: "cover" | "contain";
|
|
177
177
|
quality: number;
|
|
178
178
|
};
|
|
179
179
|
} | undefined;
|
package/dist/api/root.d.ts
CHANGED
|
@@ -297,7 +297,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
297
297
|
thumbnail?: {
|
|
298
298
|
width: number;
|
|
299
299
|
height: number;
|
|
300
|
-
|
|
300
|
+
fit: "cover" | "contain";
|
|
301
301
|
quality: number;
|
|
302
302
|
};
|
|
303
303
|
} | undefined;
|
|
@@ -346,7 +346,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
346
346
|
thumbnail?: {
|
|
347
347
|
width: number;
|
|
348
348
|
height: number;
|
|
349
|
-
|
|
349
|
+
fit: "cover" | "contain";
|
|
350
350
|
quality: number;
|
|
351
351
|
};
|
|
352
352
|
} | undefined;
|
|
@@ -443,7 +443,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
443
443
|
thumbnail?: {
|
|
444
444
|
width: number;
|
|
445
445
|
height: number;
|
|
446
|
-
|
|
446
|
+
fit: "cover" | "contain";
|
|
447
447
|
quality: number;
|
|
448
448
|
};
|
|
449
449
|
} | undefined;
|
|
@@ -1170,7 +1170,7 @@ export declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
|
|
|
1170
1170
|
thumbnail?: {
|
|
1171
1171
|
width: number;
|
|
1172
1172
|
height: number;
|
|
1173
|
-
|
|
1173
|
+
fit: "cover" | "contain";
|
|
1174
1174
|
quality: number;
|
|
1175
1175
|
};
|
|
1176
1176
|
} | undefined;
|
|
@@ -1219,7 +1219,7 @@ export declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
|
|
|
1219
1219
|
thumbnail?: {
|
|
1220
1220
|
width: number;
|
|
1221
1221
|
height: number;
|
|
1222
|
-
|
|
1222
|
+
fit: "cover" | "contain";
|
|
1223
1223
|
quality: number;
|
|
1224
1224
|
};
|
|
1225
1225
|
} | undefined;
|
|
@@ -1316,7 +1316,7 @@ export declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
|
|
|
1316
1316
|
thumbnail?: {
|
|
1317
1317
|
width: number;
|
|
1318
1318
|
height: number;
|
|
1319
|
-
|
|
1319
|
+
fit: "cover" | "contain";
|
|
1320
1320
|
quality: number;
|
|
1321
1321
|
};
|
|
1322
1322
|
} | undefined;
|
|
@@ -82,7 +82,7 @@ export declare const hasItemsSectionRouter: import("@trpc/server").TRPCBuiltRout
|
|
|
82
82
|
thumbnail?: {
|
|
83
83
|
width: number;
|
|
84
84
|
height: number;
|
|
85
|
-
|
|
85
|
+
fit: "cover" | "contain";
|
|
86
86
|
quality: number;
|
|
87
87
|
};
|
|
88
88
|
} | undefined;
|
|
@@ -131,7 +131,7 @@ export declare const hasItemsSectionRouter: import("@trpc/server").TRPCBuiltRout
|
|
|
131
131
|
thumbnail?: {
|
|
132
132
|
width: number;
|
|
133
133
|
height: number;
|
|
134
|
-
|
|
134
|
+
fit: "cover" | "contain";
|
|
135
135
|
quality: number;
|
|
136
136
|
};
|
|
137
137
|
} | undefined;
|
|
@@ -140,16 +140,16 @@ export async function dbConfigSetup(options) {
|
|
|
140
140
|
/**
|
|
141
141
|
* Prepare new environment variables block
|
|
142
142
|
*/
|
|
143
|
-
const newEnvBlock = `
|
|
144
|
-
|
|
145
|
-
####
|
|
146
|
-
# generated by the init script
|
|
147
|
-
####
|
|
148
|
-
DB_HOST=${dbHost}
|
|
149
|
-
DB_PORT=${dbPort}
|
|
150
|
-
DB_NAME=${dbName}
|
|
151
|
-
DB_USER=${dbUser}
|
|
152
|
-
DB_PASSWORD='${dbPassword}'
|
|
143
|
+
const newEnvBlock = `
|
|
144
|
+
|
|
145
|
+
####
|
|
146
|
+
# generated by the init script
|
|
147
|
+
####
|
|
148
|
+
DB_HOST=${dbHost}
|
|
149
|
+
DB_PORT=${dbPort}
|
|
150
|
+
DB_NAME=${dbName}
|
|
151
|
+
DB_USER=${dbUser}
|
|
152
|
+
DB_PASSWORD='${dbPassword}'
|
|
153
153
|
`;
|
|
154
154
|
/**
|
|
155
155
|
* Append the new credentials to the .env file
|
|
@@ -30,10 +30,23 @@ declare const cmsConfigSchema: z.ZodObject<{
|
|
|
30
30
|
b: z.ZodNumber;
|
|
31
31
|
alpha: z.ZodNumber;
|
|
32
32
|
}, z.core.$strip>>;
|
|
33
|
+
image: z.ZodOptional<z.ZodObject<{
|
|
34
|
+
width: z.ZodNumber;
|
|
35
|
+
height: z.ZodNumber;
|
|
36
|
+
fit: z.ZodEnum<{
|
|
37
|
+
cover: "cover";
|
|
38
|
+
contain: "contain";
|
|
39
|
+
}>;
|
|
40
|
+
strict: z.ZodBoolean;
|
|
41
|
+
quality: z.ZodNumber;
|
|
42
|
+
}, z.core.$strip>>;
|
|
33
43
|
thumbnail: z.ZodOptional<z.ZodObject<{
|
|
34
44
|
width: z.ZodNumber;
|
|
35
45
|
height: z.ZodNumber;
|
|
36
|
-
|
|
46
|
+
fit: z.ZodEnum<{
|
|
47
|
+
cover: "cover";
|
|
48
|
+
contain: "contain";
|
|
49
|
+
}>;
|
|
37
50
|
quality: z.ZodOptional<z.ZodNumber>;
|
|
38
51
|
}, z.core.$strip>>;
|
|
39
52
|
watermark: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -112,11 +125,18 @@ export interface ComputedCMSConfig {
|
|
|
112
125
|
b: number;
|
|
113
126
|
alpha: number;
|
|
114
127
|
};
|
|
128
|
+
image: {
|
|
129
|
+
width: number;
|
|
130
|
+
height: number;
|
|
131
|
+
fit: 'cover' | 'contain';
|
|
132
|
+
strict?: boolean;
|
|
133
|
+
quality?: number;
|
|
134
|
+
};
|
|
115
135
|
thumbnail: {
|
|
116
136
|
width: number;
|
|
117
137
|
height: number;
|
|
118
|
-
|
|
119
|
-
quality
|
|
138
|
+
fit?: 'cover' | 'contain';
|
|
139
|
+
quality?: number;
|
|
120
140
|
};
|
|
121
141
|
watermark: boolean;
|
|
122
142
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/core/config/config-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAGxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AAEzE,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,iBAAkB,CAAA;AAErD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAA;AAGrE,QAAA,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/core/config/config-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -91,8 +91,8 @@ const cmsConfigSchema = z.object({
|
|
|
91
91
|
images: z
|
|
92
92
|
.object({
|
|
93
93
|
/**
|
|
94
|
-
* Global background color for image
|
|
95
|
-
* Applied to both
|
|
94
|
+
* Global background color for image padding (used with fit: 'contain').
|
|
95
|
+
* Applied to both images and thumbnails as a fallback.
|
|
96
96
|
*/
|
|
97
97
|
background: z
|
|
98
98
|
.object({
|
|
@@ -103,13 +103,25 @@ const cmsConfigSchema = z.object({
|
|
|
103
103
|
})
|
|
104
104
|
.optional(),
|
|
105
105
|
/**
|
|
106
|
-
* Global
|
|
106
|
+
* Global image defaults (used when individual fields don't specify size)
|
|
107
|
+
*/
|
|
108
|
+
image: z
|
|
109
|
+
.object({
|
|
110
|
+
width: z.number(),
|
|
111
|
+
height: z.number(),
|
|
112
|
+
fit: z.enum(['cover', 'contain']),
|
|
113
|
+
strict: z.boolean(),
|
|
114
|
+
quality: z.number().min(1).max(100),
|
|
115
|
+
})
|
|
116
|
+
.optional(),
|
|
117
|
+
/**
|
|
118
|
+
* Global thumbnail defaults (used when individual fields don't specify thumbnail)
|
|
107
119
|
*/
|
|
108
120
|
thumbnail: z
|
|
109
121
|
.object({
|
|
110
122
|
width: z.number(),
|
|
111
123
|
height: z.number(),
|
|
112
|
-
|
|
124
|
+
fit: z.enum(['cover', 'contain']),
|
|
113
125
|
quality: z.number().optional(),
|
|
114
126
|
})
|
|
115
127
|
.optional(),
|
|
@@ -278,10 +290,17 @@ function mergeConfig(defaults, userConfig) {
|
|
|
278
290
|
},
|
|
279
291
|
images: {
|
|
280
292
|
background: userConfig.media?.images?.background ?? defaults.media.images.background,
|
|
293
|
+
image: {
|
|
294
|
+
width: userConfig.media?.images?.image?.width ?? defaults.media.images.image.width,
|
|
295
|
+
height: userConfig.media?.images?.image?.height ?? defaults.media.images.image.height,
|
|
296
|
+
fit: userConfig.media?.images?.image?.fit ?? defaults.media.images.image.fit,
|
|
297
|
+
strict: userConfig.media?.images?.image?.strict ?? defaults.media.images.image.strict,
|
|
298
|
+
quality: userConfig.media?.images?.image?.quality ?? defaults.media.images.image.quality,
|
|
299
|
+
},
|
|
281
300
|
thumbnail: {
|
|
282
301
|
width: userConfig.media?.images?.thumbnail?.width ?? defaults.media.images.thumbnail.width,
|
|
283
302
|
height: userConfig.media?.images?.thumbnail?.height ?? defaults.media.images.thumbnail.height,
|
|
284
|
-
|
|
303
|
+
fit: userConfig.media?.images?.thumbnail?.fit ?? defaults.media.images.thumbnail.fit,
|
|
285
304
|
quality: userConfig.media?.images?.thumbnail?.quality ?? defaults.media.images.thumbnail.quality,
|
|
286
305
|
},
|
|
287
306
|
watermark: userConfig.media?.images?.watermark ?? defaults.media.images.watermark,
|
|
@@ -330,10 +349,17 @@ const defaultConfig = {
|
|
|
330
349
|
},
|
|
331
350
|
images: {
|
|
332
351
|
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
352
|
+
image: {
|
|
353
|
+
width: 1200,
|
|
354
|
+
height: 630,
|
|
355
|
+
fit: 'cover',
|
|
356
|
+
strict: false,
|
|
357
|
+
quality: 80,
|
|
358
|
+
},
|
|
333
359
|
thumbnail: {
|
|
334
360
|
width: 200,
|
|
335
361
|
height: 200,
|
|
336
|
-
|
|
362
|
+
fit: 'contain',
|
|
337
363
|
quality: 80,
|
|
338
364
|
},
|
|
339
365
|
watermark: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader-with-jiti.d.ts","sourceRoot":"","sources":["../../../src/core/config/loader-with-jiti.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loader-with-jiti.d.ts","sourceRoot":"","sources":["../../../src/core/config/loader-with-jiti.ts"],"names":[],"mappings":"AAiDA,eAAO,MAAM,sBAAsB,cAAmC,CAAA;AAsHtE;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,QAAa,OAAO,CAAC,OAAO,CA4BxD,CAAA"}
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
// - Keeps your Node CJS resolve patch for tests (js specifier -> ts fallback)
|
|
9
9
|
import { existsSync } from 'fs';
|
|
10
10
|
import { resolve, join } from 'path';
|
|
11
|
-
import chokidar from 'chokidar';
|
|
12
11
|
import chalk from 'chalk';
|
|
13
12
|
import fs from 'fs';
|
|
14
13
|
let jitiInstance = null;
|
|
@@ -103,20 +102,25 @@ const startConfigWatcher = () => {
|
|
|
103
102
|
configWatcherState.version++;
|
|
104
103
|
await bumpHotMarker();
|
|
105
104
|
};
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
// Dynamically import chokidar to avoid bundling fsevents (native module) in Turbopack
|
|
106
|
+
import('chokidar').then((chokidar) => {
|
|
107
|
+
if (configWatcherState.watcher)
|
|
108
|
+
return;
|
|
109
|
+
configWatcherState.watcher = chokidar.default
|
|
110
|
+
.watch('cms.config.*', {
|
|
111
|
+
cwd,
|
|
112
|
+
ignoreInitial: true,
|
|
113
|
+
atomic: true,
|
|
114
|
+
awaitWriteFinish: {
|
|
115
|
+
stabilityThreshold: 100,
|
|
116
|
+
pollInterval: 20,
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
.setMaxListeners(3)
|
|
120
|
+
.on('add', async (path) => await invalidateForRelPath(path))
|
|
121
|
+
.on('change', async (path) => await invalidateForRelPath(path))
|
|
122
|
+
.on('unlink', async (path) => await invalidateForRelPath(path));
|
|
123
|
+
});
|
|
120
124
|
};
|
|
121
125
|
const findConfigFile = () => {
|
|
122
126
|
const cwd = process.cwd();
|
|
@@ -22,10 +22,10 @@ export class MysqlTableChecker extends DbTableChecker {
|
|
|
22
22
|
return _tables;
|
|
23
23
|
}
|
|
24
24
|
static async getColumns(tableName) {
|
|
25
|
-
const statement = sql `
|
|
26
|
-
SELECT COLUMN_NAME
|
|
27
|
-
FROM information_schema.COLUMNS c
|
|
28
|
-
inner join information_schema.TABLES t ON t.TABLE_NAME = c.TABLE_NAME
|
|
25
|
+
const statement = sql `
|
|
26
|
+
SELECT COLUMN_NAME
|
|
27
|
+
FROM information_schema.COLUMNS c
|
|
28
|
+
inner join information_schema.TABLES t ON t.TABLE_NAME = c.TABLE_NAME
|
|
29
29
|
WHERE t.TABLE_NAME = ${tableName}`;
|
|
30
30
|
const _cols = [];
|
|
31
31
|
const _res = await db.execute(statement);
|
|
@@ -82,10 +82,10 @@ export class MysqlTableChecker extends DbTableChecker {
|
|
|
82
82
|
fullTextKeys.push({ name: key, columns: _fullTextKeyMap[key] });
|
|
83
83
|
}
|
|
84
84
|
// Foreign keys should be retrieved from information_schema
|
|
85
|
-
const [foreignKeys] = await db.execute(sql `
|
|
86
|
-
SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
|
|
87
|
-
FROM information_schema.KEY_COLUMN_USAGE
|
|
88
|
-
WHERE TABLE_NAME = ${table} AND CONSTRAINT_NAME != 'PRIMARY' AND REFERENCED_TABLE_NAME IS NOT NULL;
|
|
85
|
+
const [foreignKeys] = await db.execute(sql `
|
|
86
|
+
SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
|
|
87
|
+
FROM information_schema.KEY_COLUMN_USAGE
|
|
88
|
+
WHERE TABLE_NAME = ${table} AND CONSTRAINT_NAME != 'PRIMARY' AND REFERENCED_TABLE_NAME IS NOT NULL;
|
|
89
89
|
`);
|
|
90
90
|
return { primaryKeys, uniqueKeys, indexKeys, foreignKeys, fullTextKeys };
|
|
91
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-factory-with-esbuild.d.ts","sourceRoot":"","sources":["../../../src/core/factories/section-factory-with-esbuild.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtF,OAAO,KAAK,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC7G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"section-factory-with-esbuild.d.ts","sourceRoot":"","sources":["../../../src/core/factories/section-factory-with-esbuild.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,7 @@ import { db } from '../../db/client.js';
|
|
|
11
11
|
import { AdminPrivilegesTable } from '../../db/schema.js';
|
|
12
12
|
import { getCMSConfig, getConfigImportVersion } from '../config/index.js';
|
|
13
13
|
import crypto from 'crypto';
|
|
14
|
+
import { collectDuplicateSectionNameReport } from './section-name-validation.js';
|
|
14
15
|
let cachedCmsConfig = null;
|
|
15
16
|
let cachedCmsConfigVersion = -1;
|
|
16
17
|
const getCmsConfigCached = async (currentVersion = getConfigImportVersion()) => {
|
|
@@ -387,6 +388,7 @@ export class SectionFactory {
|
|
|
387
388
|
this.log('Loading all sections from disk...', this.isDev ? '(dev mode)' : '(production mode)');
|
|
388
389
|
}
|
|
389
390
|
const sections = [];
|
|
391
|
+
const sectionNameSources = [];
|
|
390
392
|
try {
|
|
391
393
|
const sectionFiles = await glob('**/*.section.ts', {
|
|
392
394
|
cwd: cmsConfig.sections.path,
|
|
@@ -420,6 +422,7 @@ export class SectionFactory {
|
|
|
420
422
|
continue;
|
|
421
423
|
}
|
|
422
424
|
sections.push(defaultExport);
|
|
425
|
+
sectionNameSources.push({ file, name: String(defaultExport.name).trim() });
|
|
423
426
|
}
|
|
424
427
|
catch (importErr) {
|
|
425
428
|
/**
|
|
@@ -433,15 +436,15 @@ export class SectionFactory {
|
|
|
433
436
|
importErr.message.includes('Cannot find module') &&
|
|
434
437
|
importErr.message.includes('.section')) {
|
|
435
438
|
this.sectionProcessingErrors[file] ??= [];
|
|
436
|
-
this.sectionProcessingErrors[file].push(`❌ Invalid section import detected.
|
|
437
|
-
|
|
438
|
-
Sections MUST use extensionless relative imports:
|
|
439
|
-
|
|
440
|
-
✅ import exampleSection from './example.section'
|
|
441
|
-
❌ import exampleSection from './example.section.ts'
|
|
442
|
-
❌ import exampleSection from './example.section.js'
|
|
443
|
-
|
|
444
|
-
This file is bundled with esbuild, so Node never resolves the import directly.
|
|
439
|
+
this.sectionProcessingErrors[file].push(`❌ Invalid section import detected.
|
|
440
|
+
|
|
441
|
+
Sections MUST use extensionless relative imports:
|
|
442
|
+
|
|
443
|
+
✅ import exampleSection from './example.section'
|
|
444
|
+
❌ import exampleSection from './example.section.ts'
|
|
445
|
+
❌ import exampleSection from './example.section.js'
|
|
446
|
+
|
|
447
|
+
This file is bundled with esbuild, so Node never resolves the import directly.
|
|
445
448
|
If you added an extension manually, remove it.`);
|
|
446
449
|
this.errorCount++;
|
|
447
450
|
continue;
|
|
@@ -487,6 +490,17 @@ If you added an extension manually, remove it.`);
|
|
|
487
490
|
catch (err) {
|
|
488
491
|
console.error('Error finding section files:', err);
|
|
489
492
|
}
|
|
493
|
+
const duplicateReport = collectDuplicateSectionNameReport(sectionNameSources);
|
|
494
|
+
const duplicateSectionNames = [...duplicateReport.duplicateDisplayNames].sort((a, b) => a.localeCompare(b));
|
|
495
|
+
if (duplicateSectionNames.length > 0) {
|
|
496
|
+
for (const [file, errors] of Object.entries(duplicateReport.errorsByFile)) {
|
|
497
|
+
this.sectionProcessingErrors[file] ??= [];
|
|
498
|
+
this.sectionProcessingErrors[file].push(...errors);
|
|
499
|
+
this.errorCount += errors.length;
|
|
500
|
+
}
|
|
501
|
+
console.error(chalk.red.bold(`[Sections Validation]: Duplicate section names detected: ${duplicateSectionNames.join(', ')}.`));
|
|
502
|
+
return [];
|
|
503
|
+
}
|
|
490
504
|
this.allSectionsLoaded = true;
|
|
491
505
|
/**
|
|
492
506
|
* If `strict` mode is enabled,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-factory-with-jiti.d.ts","sourceRoot":"","sources":["../../../src/core/factories/section-factory-with-jiti.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"section-factory-with-jiti.d.ts","sourceRoot":"","sources":["../../../src/core/factories/section-factory-with-jiti.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,12 +3,12 @@ import { cloneDeep } from 'lodash-es';
|
|
|
3
3
|
import { resolve } from 'path';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { eq } from 'drizzle-orm';
|
|
6
|
-
import chokidar from 'chokidar';
|
|
7
6
|
import fs from 'fs';
|
|
8
7
|
import { createRequire } from 'module';
|
|
9
8
|
import { db } from '../../db/client.js';
|
|
10
9
|
import { AdminPrivilegesTable } from '../../db/schema.js';
|
|
11
10
|
import { getCMSConfig, getConfigImportVersion } from '../config/index.js';
|
|
11
|
+
import { collectDuplicateSectionNameReport } from './section-name-validation.js';
|
|
12
12
|
const hotMarkerFile = resolve(process.cwd(), 'components/form/helpers/_section-hot-reload.js');
|
|
13
13
|
const sectionWatcherState = (() => {
|
|
14
14
|
const key = Symbol.for('nextjs-cms.sectionWatcher');
|
|
@@ -338,6 +338,7 @@ export class SectionFactory {
|
|
|
338
338
|
this.log('Loading all sections from disk...', this.isDev ? '(dev mode)' : '(production mode)');
|
|
339
339
|
}
|
|
340
340
|
const sections = [];
|
|
341
|
+
const sectionNameSources = [];
|
|
341
342
|
try {
|
|
342
343
|
const sectionFiles = await glob('**/*.section.ts', {
|
|
343
344
|
cwd: cmsConfig.sections.path,
|
|
@@ -371,6 +372,7 @@ export class SectionFactory {
|
|
|
371
372
|
continue;
|
|
372
373
|
}
|
|
373
374
|
sections.push(defaultExport);
|
|
375
|
+
sectionNameSources.push({ file, name: String(defaultExport.name).trim() });
|
|
374
376
|
}
|
|
375
377
|
catch (importErr) {
|
|
376
378
|
/**
|
|
@@ -384,15 +386,15 @@ export class SectionFactory {
|
|
|
384
386
|
importErr.message.includes('Cannot find module') &&
|
|
385
387
|
importErr.message.includes('.section')) {
|
|
386
388
|
this.sectionProcessingErrors[file] ??= [];
|
|
387
|
-
this.sectionProcessingErrors[file].push(`❌ Invalid section import detected.
|
|
388
|
-
|
|
389
|
-
Sections MUST use extensionless relative imports:
|
|
390
|
-
|
|
391
|
-
✅ import exampleSection from './example.section'
|
|
392
|
-
❌ import exampleSection from './example.section.ts'
|
|
393
|
-
❌ import exampleSection from './example.section.js'
|
|
394
|
-
|
|
395
|
-
This file is bundled with jiti, so Node never resolves the import directly.
|
|
389
|
+
this.sectionProcessingErrors[file].push(`❌ Invalid section import detected.
|
|
390
|
+
|
|
391
|
+
Sections MUST use extensionless relative imports:
|
|
392
|
+
|
|
393
|
+
✅ import exampleSection from './example.section'
|
|
394
|
+
❌ import exampleSection from './example.section.ts'
|
|
395
|
+
❌ import exampleSection from './example.section.js'
|
|
396
|
+
|
|
397
|
+
This file is bundled with jiti, so Node never resolves the import directly.
|
|
396
398
|
If you added an extension manually, remove it.`);
|
|
397
399
|
this.errorCount++;
|
|
398
400
|
continue;
|
|
@@ -438,6 +440,17 @@ If you added an extension manually, remove it.`);
|
|
|
438
440
|
catch (err) {
|
|
439
441
|
console.error('Error finding section files:', err);
|
|
440
442
|
}
|
|
443
|
+
const duplicateReport = collectDuplicateSectionNameReport(sectionNameSources);
|
|
444
|
+
const duplicateSectionNames = [...duplicateReport.duplicateDisplayNames].sort((a, b) => a.localeCompare(b));
|
|
445
|
+
if (duplicateSectionNames.length > 0) {
|
|
446
|
+
for (const [file, errors] of Object.entries(duplicateReport.errorsByFile)) {
|
|
447
|
+
this.sectionProcessingErrors[file] ??= [];
|
|
448
|
+
this.sectionProcessingErrors[file].push(...errors);
|
|
449
|
+
this.errorCount += errors.length;
|
|
450
|
+
}
|
|
451
|
+
console.error(chalk.red.bold(`[Sections Validation]: Duplicate section names detected: ${duplicateSectionNames.join(', ')}.`));
|
|
452
|
+
return [];
|
|
453
|
+
}
|
|
441
454
|
this.allSectionsLoaded = true;
|
|
442
455
|
/**
|
|
443
456
|
* If `strict` mode is enabled,
|
|
@@ -490,30 +503,35 @@ If you added an extension manually, remove it.`);
|
|
|
490
503
|
// Bump the hot marker to trigger Next.js Fast Refresh
|
|
491
504
|
this.bumpHotMarker();
|
|
492
505
|
};
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
506
|
+
// Dynamically import chokidar to avoid bundling fsevents (native module) in Turbopack
|
|
507
|
+
import('chokidar').then((chokidar) => {
|
|
508
|
+
if (sectionWatcherState.watcher)
|
|
509
|
+
return;
|
|
510
|
+
sectionWatcherState.watcher = chokidar.default
|
|
511
|
+
.watch('**/*.section.ts', {
|
|
512
|
+
cwd: cmsConfig.sections.path,
|
|
513
|
+
ignoreInitial: true,
|
|
514
|
+
atomic: true,
|
|
515
|
+
awaitWriteFinish: {
|
|
516
|
+
stabilityThreshold: 100,
|
|
517
|
+
pollInterval: 20,
|
|
518
|
+
},
|
|
519
|
+
})
|
|
520
|
+
.setMaxListeners(3)
|
|
521
|
+
.on('add', (path) => {
|
|
522
|
+
this.log('Section file added:', path);
|
|
523
|
+
invalidateForRelPath(path);
|
|
524
|
+
})
|
|
525
|
+
.on('change', (path) => {
|
|
526
|
+
this.log('Section file changed:', path);
|
|
527
|
+
invalidateForRelPath(path);
|
|
528
|
+
})
|
|
529
|
+
.on('unlink', (path) => {
|
|
530
|
+
this.log('Section file removed:', path);
|
|
531
|
+
invalidateForRelPath(path);
|
|
532
|
+
});
|
|
533
|
+
this.log('Starting section watcher in dev mode...');
|
|
515
534
|
});
|
|
516
|
-
this.log('Starting section watcher in dev mode...');
|
|
517
535
|
}
|
|
518
536
|
/**
|
|
519
537
|
* Clear jiti's cache for a specific file path.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type SectionNameSource = {
|
|
2
|
+
file: string;
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
export type DuplicateSectionNameReport = {
|
|
6
|
+
duplicateDisplayNames: string[];
|
|
7
|
+
errorsByFile: Record<string, string[]>;
|
|
8
|
+
};
|
|
9
|
+
export declare function collectDuplicateSectionNameReport(sources: SectionNameSource[]): DuplicateSectionNameReport;
|
|
10
|
+
//# sourceMappingURL=section-name-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section-name-validation.d.ts","sourceRoot":"","sources":["../../../src/core/factories/section-name-validation.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACrC,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;CACzC,CAAA;AAID,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,0BAA0B,CAqC1G"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const normalizeSectionName = (name) => name.trim().toLowerCase();
|
|
2
|
+
export function collectDuplicateSectionNameReport(sources) {
|
|
3
|
+
const byNormalizedName = new Map();
|
|
4
|
+
for (const source of sources) {
|
|
5
|
+
const normalizedName = normalizeSectionName(source.name);
|
|
6
|
+
if (!byNormalizedName.has(normalizedName)) {
|
|
7
|
+
byNormalizedName.set(normalizedName, []);
|
|
8
|
+
}
|
|
9
|
+
byNormalizedName.get(normalizedName)?.push(source);
|
|
10
|
+
}
|
|
11
|
+
const duplicateDisplayNameSet = new Set();
|
|
12
|
+
const errorsByFile = {};
|
|
13
|
+
for (const group of byNormalizedName.values()) {
|
|
14
|
+
if (group.length < 2)
|
|
15
|
+
continue;
|
|
16
|
+
for (const entry of group) {
|
|
17
|
+
duplicateDisplayNameSet.add(entry.name);
|
|
18
|
+
const conflicts = group
|
|
19
|
+
.filter((other) => other.file !== entry.file)
|
|
20
|
+
.map((other) => `"${other.name}" in "${other.file}"`)
|
|
21
|
+
.join(', ');
|
|
22
|
+
const errors = errorsByFile[entry.file] ?? [];
|
|
23
|
+
errors.push(`Duplicate section name "${entry.name}" detected. Conflicts with: ${conflicts}. Section names must be globally unique (case-insensitive).`);
|
|
24
|
+
errorsByFile[entry.file] = errors;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
duplicateDisplayNames: [...duplicateDisplayNameSet],
|
|
29
|
+
errorsByFile,
|
|
30
|
+
};
|
|
31
|
+
}
|