@leadcms/sdk 1.2.91-pre → 2.0.0-pre
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 +154 -7
- package/dist/cli/index.js +55 -62
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -18
- package/dist/index.js.map +1 -1
- package/dist/lib/cms.d.ts +24 -8
- package/dist/lib/cms.d.ts.map +1 -1
- package/dist/lib/cms.js +159 -90
- package/dist/lib/cms.js.map +1 -1
- package/dist/lib/config.d.ts +0 -8
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +13 -40
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/console-colors.d.ts +49 -0
- package/dist/lib/console-colors.d.ts.map +1 -0
- package/dist/lib/console-colors.js +121 -0
- package/dist/lib/console-colors.js.map +1 -0
- package/dist/lib/content-transformation.d.ts +83 -0
- package/dist/lib/content-transformation.d.ts.map +1 -0
- package/dist/lib/content-transformation.js +305 -0
- package/dist/lib/content-transformation.js.map +1 -0
- package/dist/lib/data-service.d.ts +97 -0
- package/dist/lib/data-service.d.ts.map +1 -0
- package/dist/lib/data-service.js +389 -0
- package/dist/lib/data-service.js.map +1 -0
- package/dist/scripts/fetch-leadcms-content.d.ts +19 -0
- package/dist/scripts/fetch-leadcms-content.d.ts.map +1 -0
- package/dist/scripts/fetch-leadcms-content.js +301 -0
- package/dist/scripts/fetch-leadcms-content.js.map +1 -0
- package/dist/scripts/generate-env-js.d.ts +2 -0
- package/dist/scripts/generate-env-js.d.ts.map +1 -0
- package/dist/scripts/generate-env-js.js +22 -0
- package/dist/scripts/generate-env-js.js.map +1 -0
- package/dist/scripts/leadcms-helpers.d.ts +25 -0
- package/dist/scripts/leadcms-helpers.d.ts.map +1 -0
- package/dist/scripts/leadcms-helpers.js +78 -0
- package/dist/scripts/leadcms-helpers.js.map +1 -0
- package/dist/scripts/push-leadcms-content.d.ts +50 -0
- package/dist/scripts/push-leadcms-content.d.ts.map +1 -0
- package/dist/scripts/push-leadcms-content.js +1063 -0
- package/dist/scripts/push-leadcms-content.js.map +1 -0
- package/dist/scripts/sse-watcher.d.ts +20 -0
- package/dist/scripts/sse-watcher.d.ts.map +1 -0
- package/dist/scripts/sse-watcher.js +268 -0
- package/dist/scripts/sse-watcher.js.map +1 -0
- package/dist/scripts/status-leadcms-content.d.ts +4 -0
- package/dist/scripts/status-leadcms-content.d.ts.map +1 -0
- package/dist/scripts/status-leadcms-content.js +36 -0
- package/dist/scripts/status-leadcms-content.js.map +1 -0
- package/package.json +22 -13
- package/dist/scripts/fetch-leadcms-content.mjs +0 -367
- package/dist/scripts/generate-env-js.mjs +0 -24
- package/dist/scripts/leadcms-helpers.mjs +0 -208
- package/dist/scripts/sse-watcher.mjs +0 -300
package/dist/lib/cms.js
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getLeadCMSConfig = getLeadCMSConfig;
|
|
7
|
-
exports.getAvailableLanguages = getAvailableLanguages;
|
|
8
|
-
exports.getContentDirForLocale = getContentDirForLocale;
|
|
9
|
-
exports.getAllContentSlugsForLocale = getAllContentSlugsForLocale;
|
|
10
|
-
exports.getCMSContentBySlugForLocaleWithDraftSupport = getCMSContentBySlugForLocaleWithDraftSupport;
|
|
11
|
-
exports.getCMSContentBySlugForLocale = getCMSContentBySlugForLocale;
|
|
12
|
-
exports.getContentTranslations = getContentTranslations;
|
|
13
|
-
exports.getAllContentRoutes = getAllContentRoutes;
|
|
14
|
-
exports.extractUserUidFromSlug = extractUserUidFromSlug;
|
|
15
|
-
exports.getAllContentSlugs = getAllContentSlugs;
|
|
16
|
-
exports.getCMSContentBySlug = getCMSContentBySlug;
|
|
17
|
-
exports.loadContentConfig = loadContentConfig;
|
|
18
|
-
exports.getHeaderConfig = getHeaderConfig;
|
|
19
|
-
exports.getFooterConfig = getFooterConfig;
|
|
20
|
-
exports.getLocaleFromPath = getLocaleFromPath;
|
|
21
|
-
exports.makeLocaleAwareLink = makeLocaleAwareLink;
|
|
22
|
-
exports.loadContentConfigStrict = loadContentConfigStrict;
|
|
23
|
-
const fs_1 = __importDefault(require("fs"));
|
|
24
|
-
const path_1 = __importDefault(require("path"));
|
|
25
|
-
const gray_matter_1 = __importDefault(require("gray-matter"));
|
|
26
|
-
const config_1 = require("./config");
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import matter from "gray-matter";
|
|
4
|
+
import { getConfig } from "./config.js";
|
|
27
5
|
// Default language - internal fallback only when configuration is not available
|
|
28
6
|
const DEFAULT_LANGUAGE = "en";
|
|
29
7
|
const contentCache = new Map();
|
|
30
8
|
const CONTENT_CACHE_TTL = 30000; // 30 seconds cache TTL for content files
|
|
9
|
+
/**
|
|
10
|
+
* Check if content is a draft based on publishedAt field
|
|
11
|
+
* Content is considered draft if:
|
|
12
|
+
* - publishedAt is null/undefined
|
|
13
|
+
* - publishedAt is a future date (after current time)
|
|
14
|
+
*
|
|
15
|
+
* @param content - The content to check
|
|
16
|
+
* @returns true if content is draft, false otherwise
|
|
17
|
+
*/
|
|
18
|
+
export function isContentDraft(content) {
|
|
19
|
+
if (!content.publishedAt) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
const publishedDate = content.publishedAt instanceof Date
|
|
23
|
+
? content.publishedAt
|
|
24
|
+
: new Date(content.publishedAt);
|
|
25
|
+
const publishedTime = publishedDate.getTime();
|
|
26
|
+
// If the date is invalid (NaN), treat as draft
|
|
27
|
+
if (isNaN(publishedTime)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
return publishedTime > now;
|
|
32
|
+
}
|
|
31
33
|
/**
|
|
32
34
|
* Get LeadCMS configuration with fallbacks
|
|
33
35
|
* This function attempts to load the LeadCMS configuration and falls back to environment variables
|
|
@@ -35,9 +37,9 @@ const CONTENT_CACHE_TTL = 30000; // 30 seconds cache TTL for content files
|
|
|
35
37
|
*
|
|
36
38
|
* @returns Complete LeadCMS configuration object with all required fields
|
|
37
39
|
*/
|
|
38
|
-
function getLeadCMSConfig() {
|
|
40
|
+
export function getLeadCMSConfig() {
|
|
39
41
|
try {
|
|
40
|
-
return
|
|
42
|
+
return getConfig();
|
|
41
43
|
}
|
|
42
44
|
catch (error) {
|
|
43
45
|
// If config loading fails, return minimal config with environment fallbacks
|
|
@@ -59,7 +61,7 @@ function getAvailableLanguagesFromDir(contentDir) {
|
|
|
59
61
|
const config = getLeadCMSConfig();
|
|
60
62
|
const defaultLanguage = config.defaultLanguage || DEFAULT_LANGUAGE;
|
|
61
63
|
try {
|
|
62
|
-
const entries =
|
|
64
|
+
const entries = fs.readdirSync(contentDir, { withFileTypes: true });
|
|
63
65
|
const languages = [defaultLanguage]; // Always include default language
|
|
64
66
|
for (const entry of entries) {
|
|
65
67
|
if (entry.isDirectory() && entry.name.length === 2) {
|
|
@@ -78,7 +80,7 @@ function getAvailableLanguagesFromDir(contentDir) {
|
|
|
78
80
|
/**
|
|
79
81
|
* Get all available languages from the content directory structure
|
|
80
82
|
*/
|
|
81
|
-
function getAvailableLanguages() {
|
|
83
|
+
export function getAvailableLanguages() {
|
|
82
84
|
const config = getLeadCMSConfig();
|
|
83
85
|
const defaultLanguage = config.defaultLanguage || DEFAULT_LANGUAGE;
|
|
84
86
|
const contentDir = config.contentDir;
|
|
@@ -90,14 +92,14 @@ function getAvailableLanguages() {
|
|
|
90
92
|
/**
|
|
91
93
|
* Get content directory for a specific locale
|
|
92
94
|
*/
|
|
93
|
-
function getContentDirForLocale(contentDir, locale) {
|
|
95
|
+
export function getContentDirForLocale(contentDir, locale) {
|
|
94
96
|
const config = getLeadCMSConfig();
|
|
95
97
|
const defaultLanguage = config.defaultLanguage || DEFAULT_LANGUAGE;
|
|
96
98
|
const actualLocale = locale || defaultLanguage;
|
|
97
99
|
if (actualLocale === defaultLanguage) {
|
|
98
100
|
return contentDir;
|
|
99
101
|
}
|
|
100
|
-
return
|
|
102
|
+
return path.join(contentDir, actualLocale);
|
|
101
103
|
}
|
|
102
104
|
/**
|
|
103
105
|
* Get all content slugs for a specific locale with draft filtering options (internal helper)
|
|
@@ -117,7 +119,7 @@ function getAllContentSlugsForLocaleFromDir(contentDir, locale, contentTypes, in
|
|
|
117
119
|
slugs = getAllContentSlugsFromDir(localeContentDir, contentTypes);
|
|
118
120
|
}
|
|
119
121
|
// Apply draft filtering logic
|
|
120
|
-
return applyDraftFiltering(slugs, includeDrafts, draftUserUid);
|
|
122
|
+
return applyDraftFiltering(slugs, includeDrafts, draftUserUid, contentDir, locale);
|
|
121
123
|
}
|
|
122
124
|
/**
|
|
123
125
|
* Get all content slugs for a specific locale with draft filtering options
|
|
@@ -126,7 +128,7 @@ function getAllContentSlugsForLocaleFromDir(contentDir, locale, contentTypes, in
|
|
|
126
128
|
* @param includeDrafts - Whether to include draft content (default: null = false)
|
|
127
129
|
* @param draftUserUid - Specific user UID for draft content (only relevant if includeDrafts is true)
|
|
128
130
|
*/
|
|
129
|
-
function getAllContentSlugsForLocale(locale, contentTypes, includeDrafts, draftUserUid) {
|
|
131
|
+
export function getAllContentSlugsForLocale(locale, contentTypes, includeDrafts, draftUserUid) {
|
|
130
132
|
const config = getLeadCMSConfig();
|
|
131
133
|
const contentDir = config.contentDir;
|
|
132
134
|
if (!contentDir) {
|
|
@@ -140,19 +142,31 @@ function getAllContentSlugsForLocale(locale, contentTypes, includeDrafts, draftU
|
|
|
140
142
|
* @param slugs - Array of all slugs
|
|
141
143
|
* @param includeDrafts - Whether to include draft content (null/false = filter out drafts)
|
|
142
144
|
* @param draftUserUid - Specific user UID for draft content (only relevant if includeDrafts is true)
|
|
145
|
+
* @param contentDir - Content directory to load content from for publishedAt checking
|
|
146
|
+
* @param locale - Locale for content loading
|
|
143
147
|
*/
|
|
144
|
-
function applyDraftFiltering(slugs, includeDrafts, draftUserUid) {
|
|
148
|
+
function applyDraftFiltering(slugs, includeDrafts, draftUserUid, contentDir, locale) {
|
|
145
149
|
// If includeDrafts is false or null, filter out all draft content
|
|
146
150
|
if (!includeDrafts) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
let filteredSlugs = filterOutDraftSlugs(slugs);
|
|
152
|
+
// Also filter based on publishedAt if contentDir is available
|
|
153
|
+
if (contentDir) {
|
|
154
|
+
filteredSlugs = filteredSlugs.filter(slug => {
|
|
155
|
+
const localeContentDir = getContentDirForLocale(contentDir, locale);
|
|
156
|
+
const content = getCMSContentBySlugFromDir(slug, localeContentDir);
|
|
157
|
+
return content && !isContentDraft(content);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return filteredSlugs;
|
|
152
161
|
}
|
|
153
162
|
// If includeDrafts is true and draftUserUid is specified,
|
|
154
163
|
// return base content with user's drafts overriding the originals
|
|
155
|
-
|
|
164
|
+
// User-specific drafts are always included regardless of publishedAt
|
|
165
|
+
if (draftUserUid) {
|
|
166
|
+
return getBaseContentWithUserDraftOverrides(slugs, draftUserUid);
|
|
167
|
+
}
|
|
168
|
+
// If includeDrafts is true but no specific user UID, return all slugs as-is
|
|
169
|
+
return slugs;
|
|
156
170
|
}
|
|
157
171
|
/**
|
|
158
172
|
* Filter out draft slugs (those that have a corresponding base slug)
|
|
@@ -169,20 +183,20 @@ function filterOutDraftSlugs(slugs) {
|
|
|
169
183
|
}
|
|
170
184
|
/**
|
|
171
185
|
* Get base content slugs with user-specific draft overrides
|
|
172
|
-
* Returns
|
|
186
|
+
* Returns base slugs, but when content is fetched, user's version will be preferred
|
|
187
|
+
* Also includes user-only drafts (drafts that don't have a base version)
|
|
173
188
|
*/
|
|
174
189
|
function getBaseContentWithUserDraftOverrides(slugs, draftUserUid) {
|
|
175
190
|
// First, get all base slugs (non-draft)
|
|
176
191
|
const baseSlugs = filterOutDraftSlugs(slugs);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
else {
|
|
192
|
+
const result = [...baseSlugs]; // Start with all base slugs
|
|
193
|
+
// Find user-only drafts (drafts that don't have a corresponding base slug)
|
|
194
|
+
const userDraftPattern = new RegExp(`-${draftUserUid}$`);
|
|
195
|
+
const userDrafts = slugs.filter(slug => userDraftPattern.test(slug));
|
|
196
|
+
for (const userDraft of userDrafts) {
|
|
197
|
+
const baseSlug = userDraft.replace(userDraftPattern, '');
|
|
198
|
+
// If this is a user-only draft (no base version exists), add the base slug
|
|
199
|
+
if (!baseSlugs.includes(baseSlug)) {
|
|
186
200
|
result.push(baseSlug);
|
|
187
201
|
}
|
|
188
202
|
}
|
|
@@ -195,7 +209,7 @@ function getAllContentSlugsExcludingLanguageDirs(contentDir, contentTypes, rootC
|
|
|
195
209
|
? getAvailableLanguagesFromDir(rootContentDir)
|
|
196
210
|
: [config.defaultLanguage];
|
|
197
211
|
function walk(dir, prefix = "") {
|
|
198
|
-
const entries =
|
|
212
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
199
213
|
const slugs = [];
|
|
200
214
|
for (const entry of entries) {
|
|
201
215
|
if (entry.isDirectory()) {
|
|
@@ -206,7 +220,7 @@ function getAllContentSlugsExcludingLanguageDirs(contentDir, contentTypes, rootC
|
|
|
206
220
|
entry.name !== defaultLanguage) {
|
|
207
221
|
continue;
|
|
208
222
|
}
|
|
209
|
-
const subSlugs = walk(
|
|
223
|
+
const subSlugs = walk(path.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
|
|
210
224
|
slugs.push(...subSlugs);
|
|
211
225
|
}
|
|
212
226
|
else if (entry.isFile() && (entry.name.endsWith(".mdx") || entry.name.endsWith(".json"))) {
|
|
@@ -214,7 +228,7 @@ function getAllContentSlugsExcludingLanguageDirs(contentDir, contentTypes, rootC
|
|
|
214
228
|
const slug = (prefix ? `${prefix}/` : "") + entry.name.replace(new RegExp(`\\${ext}$`), "");
|
|
215
229
|
// If content types filter is provided, check the file's type
|
|
216
230
|
if (contentTypes && contentTypes.length > 0) {
|
|
217
|
-
const filePath =
|
|
231
|
+
const filePath = path.join(dir, entry.name);
|
|
218
232
|
try {
|
|
219
233
|
const fileType = getFileTypeOptimized(filePath, ext);
|
|
220
234
|
// Only include if the type matches the filter
|
|
@@ -242,8 +256,9 @@ function getAllContentSlugsExcludingLanguageDirs(contentDir, contentTypes, rootC
|
|
|
242
256
|
* @param slug - Content slug
|
|
243
257
|
* @param locale - Locale code
|
|
244
258
|
* @param userUid - Optional user UID for draft content
|
|
259
|
+
* @param includeDrafts - Whether to include content that is draft based on publishedAt (default: false)
|
|
245
260
|
*/
|
|
246
|
-
function getCMSContentBySlugForLocaleWithDraftSupport(slug, locale, userUid) {
|
|
261
|
+
export function getCMSContentBySlugForLocaleWithDraftSupport(slug, locale, userUid, includeDrafts = false) {
|
|
247
262
|
const config = getLeadCMSConfig();
|
|
248
263
|
const contentDir = config.contentDir;
|
|
249
264
|
if (!contentDir) {
|
|
@@ -255,11 +270,17 @@ function getCMSContentBySlugForLocaleWithDraftSupport(slug, locale, userUid) {
|
|
|
255
270
|
const draftSlug = `${slug}-${userUid}`;
|
|
256
271
|
const draftContent = getCMSContentBySlugForLocaleFromDir(draftSlug, contentDir, locale);
|
|
257
272
|
if (draftContent) {
|
|
273
|
+
// Always return user-specific draft content when found, ignoring publishedAt logic
|
|
258
274
|
return draftContent;
|
|
259
275
|
}
|
|
260
276
|
}
|
|
261
277
|
// Fall back to regular content
|
|
262
|
-
|
|
278
|
+
const content = getCMSContentBySlugForLocaleFromDir(slug, contentDir, locale);
|
|
279
|
+
// Filter out drafts by default unless explicitly requested
|
|
280
|
+
if (content && !includeDrafts && isContentDraft(content)) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
return content;
|
|
263
284
|
}
|
|
264
285
|
/**
|
|
265
286
|
* Get content by slug for a specific locale (internal helper)
|
|
@@ -277,19 +298,26 @@ function getCMSContentBySlugForLocaleFromDir(slug, contentDir, locale) {
|
|
|
277
298
|
/**
|
|
278
299
|
* Get content by slug for a specific locale
|
|
279
300
|
*/
|
|
280
|
-
function getCMSContentBySlugForLocale(slug, locale) {
|
|
301
|
+
export function getCMSContentBySlugForLocale(slug, locale, includeDrafts = false) {
|
|
281
302
|
const config = getLeadCMSConfig();
|
|
282
303
|
const contentDir = config.contentDir;
|
|
283
304
|
if (!contentDir) {
|
|
284
305
|
console.warn('[LeadCMS] No contentDir configured. Please set up your LeadCMS configuration.');
|
|
285
306
|
return null;
|
|
286
307
|
}
|
|
287
|
-
|
|
308
|
+
const content = getCMSContentBySlugForLocaleFromDir(slug, contentDir, locale);
|
|
309
|
+
// Filter out drafts by default unless explicitly requested
|
|
310
|
+
if (content && !includeDrafts && isContentDraft(content)) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
return content;
|
|
288
314
|
}
|
|
289
315
|
/**
|
|
290
316
|
* Get all translations of a content item by translationKey
|
|
317
|
+
* @param translationKey - The translation key to search for
|
|
318
|
+
* @param includeDrafts - Whether to include draft content (default: false)
|
|
291
319
|
*/
|
|
292
|
-
function getContentTranslations(translationKey) {
|
|
320
|
+
export function getContentTranslations(translationKey, includeDrafts = false) {
|
|
293
321
|
const config = getLeadCMSConfig();
|
|
294
322
|
const contentDir = config.contentDir;
|
|
295
323
|
if (!contentDir) {
|
|
@@ -302,10 +330,14 @@ function getContentTranslations(translationKey) {
|
|
|
302
330
|
const localeContentDir = getContentDirForLocale(contentDir, locale);
|
|
303
331
|
// Search for content with matching translationKey
|
|
304
332
|
try {
|
|
305
|
-
const slugs =
|
|
333
|
+
const slugs = getAllContentSlugsForLocaleFromDir(contentDir, locale, undefined, includeDrafts);
|
|
306
334
|
for (const slug of slugs) {
|
|
307
335
|
const content = getCMSContentBySlugFromDir(slug, localeContentDir);
|
|
308
336
|
if (content && content.translationKey === translationKey) {
|
|
337
|
+
// Filter out drafts by default unless explicitly requested
|
|
338
|
+
if (!includeDrafts && isContentDraft(content)) {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
309
341
|
content.language = content.language || locale;
|
|
310
342
|
translations.push({ locale, content });
|
|
311
343
|
break; // Found the translation for this locale
|
|
@@ -322,8 +354,11 @@ function getContentTranslations(translationKey) {
|
|
|
322
354
|
/**
|
|
323
355
|
* Get all content routes for all locales in a framework-agnostic format
|
|
324
356
|
* Returns an array of route objects with locale and slug information
|
|
357
|
+
* @param contentTypes - Optional array of content types to filter
|
|
358
|
+
* @param includeDrafts - Whether to include draft content (default: false)
|
|
359
|
+
* @param draftUserUid - Specific user UID for draft content (only relevant if includeDrafts is true)
|
|
325
360
|
*/
|
|
326
|
-
function getAllContentRoutes(contentTypes, includeDrafts, draftUserUid) {
|
|
361
|
+
export function getAllContentRoutes(contentTypes, includeDrafts = false, draftUserUid) {
|
|
327
362
|
const config = getLeadCMSConfig();
|
|
328
363
|
const contentDir = config.contentDir;
|
|
329
364
|
const defaultLanguage = config.defaultLanguage;
|
|
@@ -355,28 +390,49 @@ function getAllContentRoutes(contentTypes, includeDrafts, draftUserUid) {
|
|
|
355
390
|
* @param slug - The slug to check for userUid
|
|
356
391
|
* @returns userUid if found, null otherwise
|
|
357
392
|
*/
|
|
358
|
-
function extractUserUidFromSlug(slug) {
|
|
393
|
+
export function extractUserUidFromSlug(slug) {
|
|
359
394
|
// GUID pattern: 8-4-4-4-12 hexadecimal characters with a preceding dash
|
|
360
395
|
const draftSlugPattern = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
361
396
|
const match = slug.match(draftSlugPattern);
|
|
362
397
|
return match ? match[1] : null;
|
|
363
398
|
}
|
|
364
|
-
function getAllContentSlugs(contentTypes) {
|
|
399
|
+
export function getAllContentSlugs(contentTypes, includeDrafts = false) {
|
|
365
400
|
const config = getLeadCMSConfig();
|
|
366
401
|
const contentDir = config.contentDir;
|
|
367
402
|
if (!contentDir) {
|
|
368
403
|
console.warn('[LeadCMS] No contentDir configured. Please set up your LeadCMS configuration.');
|
|
369
404
|
return [];
|
|
370
405
|
}
|
|
371
|
-
|
|
406
|
+
const allSlugs = getAllContentSlugsFromDir(contentDir, contentTypes);
|
|
407
|
+
// Always filter out user-specific draft slugs (with GUID pattern) - these should never appear in general listings
|
|
408
|
+
let filteredSlugs = filterOutDraftSlugs(allSlugs);
|
|
409
|
+
// Filter out draft content based on publishedAt if includeDrafts is false
|
|
410
|
+
if (!includeDrafts) {
|
|
411
|
+
// Then filter based on publishedAt
|
|
412
|
+
return filteredSlugs.filter(slug => {
|
|
413
|
+
const content = getCMSContentBySlugFromDir(slug, contentDir);
|
|
414
|
+
return content && !isContentDraft(content);
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
return filteredSlugs;
|
|
372
418
|
}
|
|
373
419
|
function getAllContentSlugsFromDir(contentDir, contentTypes) {
|
|
374
420
|
function walk(dir, prefix = "") {
|
|
375
|
-
|
|
421
|
+
let entries;
|
|
422
|
+
try {
|
|
423
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
// If directory doesn't exist or can't be read, return empty array
|
|
427
|
+
if (error.code === 'ENOENT' || error.code === 'EACCES') {
|
|
428
|
+
return [];
|
|
429
|
+
}
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
376
432
|
const slugs = [];
|
|
377
433
|
for (const entry of entries) {
|
|
378
434
|
if (entry.isDirectory()) {
|
|
379
|
-
const subSlugs = walk(
|
|
435
|
+
const subSlugs = walk(path.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
|
|
380
436
|
slugs.push(...subSlugs);
|
|
381
437
|
}
|
|
382
438
|
else if (entry.isFile() && (entry.name.endsWith(".mdx") || entry.name.endsWith(".json"))) {
|
|
@@ -384,7 +440,7 @@ function getAllContentSlugsFromDir(contentDir, contentTypes) {
|
|
|
384
440
|
const slug = (prefix ? `${prefix}/` : "") + entry.name.replace(new RegExp(`\\${ext}$`), "");
|
|
385
441
|
// If content types filter is provided, check the file's type
|
|
386
442
|
if (contentTypes && contentTypes.length > 0) {
|
|
387
|
-
const filePath =
|
|
443
|
+
const filePath = path.join(dir, entry.name);
|
|
388
444
|
try {
|
|
389
445
|
const fileType = getFileTypeOptimized(filePath, ext);
|
|
390
446
|
// Only include if the type matches the filter
|
|
@@ -407,24 +463,33 @@ function getAllContentSlugsFromDir(contentDir, contentTypes) {
|
|
|
407
463
|
}
|
|
408
464
|
return walk(contentDir);
|
|
409
465
|
}
|
|
410
|
-
function getCMSContentBySlug(slug) {
|
|
466
|
+
export function getCMSContentBySlug(slug, includeDrafts = false) {
|
|
411
467
|
const config = getLeadCMSConfig();
|
|
412
468
|
const contentDir = config.contentDir;
|
|
413
469
|
if (!contentDir) {
|
|
414
470
|
console.warn('[LeadCMS] No contentDir configured. Please set up your LeadCMS configuration.');
|
|
415
471
|
return null;
|
|
416
472
|
}
|
|
417
|
-
|
|
473
|
+
const content = getCMSContentBySlugFromDir(slug, contentDir);
|
|
474
|
+
// Filter out drafts by default unless explicitly requested
|
|
475
|
+
if (content && !includeDrafts && isContentDraft(content)) {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
return content;
|
|
418
479
|
}
|
|
419
480
|
function getCMSContentBySlugFromDir(slug, contentDir) {
|
|
420
481
|
// Try both .mdx and .json extensions
|
|
421
|
-
const mdxPath =
|
|
422
|
-
const jsonPath =
|
|
482
|
+
const mdxPath = path.join(contentDir, `${slug}.mdx`);
|
|
483
|
+
const jsonPath = path.join(contentDir, `${slug}.json`);
|
|
423
484
|
try {
|
|
424
485
|
// Try MDX first - combine existence check with read operation
|
|
425
486
|
try {
|
|
426
|
-
const file =
|
|
427
|
-
const { data, content } = (
|
|
487
|
+
const file = fs.readFileSync(mdxPath, "utf8");
|
|
488
|
+
const { data, content } = matter(file);
|
|
489
|
+
// Convert publishedAt string to Date if present
|
|
490
|
+
if (data.publishedAt && typeof data.publishedAt === 'string') {
|
|
491
|
+
data.publishedAt = new Date(data.publishedAt);
|
|
492
|
+
}
|
|
428
493
|
return {
|
|
429
494
|
...data,
|
|
430
495
|
slug,
|
|
@@ -440,8 +505,12 @@ function getCMSContentBySlugFromDir(slug, contentDir) {
|
|
|
440
505
|
}
|
|
441
506
|
// Try JSON
|
|
442
507
|
try {
|
|
443
|
-
const file =
|
|
508
|
+
const file = fs.readFileSync(jsonPath, "utf8");
|
|
444
509
|
const data = JSON.parse(file);
|
|
510
|
+
// Convert publishedAt string to Date if present
|
|
511
|
+
if (data.publishedAt && typeof data.publishedAt === 'string') {
|
|
512
|
+
data.publishedAt = new Date(data.publishedAt);
|
|
513
|
+
}
|
|
445
514
|
return {
|
|
446
515
|
...data,
|
|
447
516
|
slug,
|
|
@@ -480,7 +549,7 @@ function getFileTypeOptimized(filePath, ext) {
|
|
|
480
549
|
*/
|
|
481
550
|
function extractTypeFromMDXFrontmatter(filePath) {
|
|
482
551
|
try {
|
|
483
|
-
const file =
|
|
552
|
+
const file = fs.readFileSync(filePath, "utf8");
|
|
484
553
|
// Quick check if file starts with frontmatter
|
|
485
554
|
if (!file.startsWith("---\n") && !file.startsWith("---\r\n")) {
|
|
486
555
|
return undefined;
|
|
@@ -495,7 +564,7 @@ function extractTypeFromMDXFrontmatter(filePath) {
|
|
|
495
564
|
}
|
|
496
565
|
// Extract and parse only the frontmatter
|
|
497
566
|
const frontmatterContent = file.slice(4, endIndex);
|
|
498
|
-
const { data } = (
|
|
567
|
+
const { data } = matter(`---\n${frontmatterContent}\n---`);
|
|
499
568
|
return data.type;
|
|
500
569
|
}
|
|
501
570
|
catch {
|
|
@@ -508,7 +577,7 @@ function extractTypeFromMDXFrontmatter(filePath) {
|
|
|
508
577
|
*/
|
|
509
578
|
function extractTypeFromJSON(filePath) {
|
|
510
579
|
try {
|
|
511
|
-
const file =
|
|
580
|
+
const file = fs.readFileSync(filePath, "utf8");
|
|
512
581
|
// For small files, just parse normally
|
|
513
582
|
if (file.length < 100) {
|
|
514
583
|
const data = JSON.parse(file);
|
|
@@ -543,7 +612,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
|
|
|
543
612
|
let cacheKey;
|
|
544
613
|
// If userUid is provided, try draft version first
|
|
545
614
|
if (userUid) {
|
|
546
|
-
const draftConfigPath =
|
|
615
|
+
const draftConfigPath = path.join(localeContentDir, `${configName}-${userUid}.json`);
|
|
547
616
|
cacheKey = `${draftConfigPath}:${locale}:${configName}:${userUid}`;
|
|
548
617
|
// Check cache first for draft
|
|
549
618
|
const cached = contentCache.get(cacheKey);
|
|
@@ -551,8 +620,8 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
|
|
|
551
620
|
if (cached && (now - cached.timestamp) < CONTENT_CACHE_TTL) {
|
|
552
621
|
return cached.content;
|
|
553
622
|
}
|
|
554
|
-
if (
|
|
555
|
-
const draftConfigContent =
|
|
623
|
+
if (fs.existsSync(draftConfigPath)) {
|
|
624
|
+
const draftConfigContent = fs.readFileSync(draftConfigPath, "utf-8");
|
|
556
625
|
const parsed = JSON.parse(draftConfigContent);
|
|
557
626
|
// Cache the result
|
|
558
627
|
contentCache.set(cacheKey, {
|
|
@@ -564,7 +633,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
|
|
|
564
633
|
}
|
|
565
634
|
}
|
|
566
635
|
// Fall back to regular config file
|
|
567
|
-
const configPath =
|
|
636
|
+
const configPath = path.join(localeContentDir, `${configName}.json`);
|
|
568
637
|
cacheKey = `${configPath}:${locale}:${configName}`;
|
|
569
638
|
// Check cache for regular config
|
|
570
639
|
const cached = contentCache.get(cacheKey);
|
|
@@ -572,7 +641,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
|
|
|
572
641
|
if (cached && (now - cached.timestamp) < CONTENT_CACHE_TTL) {
|
|
573
642
|
return cached.content;
|
|
574
643
|
}
|
|
575
|
-
if (!
|
|
644
|
+
if (!fs.existsSync(configPath)) {
|
|
576
645
|
// Cache the null result too to avoid repeated file system checks
|
|
577
646
|
contentCache.set(cacheKey, {
|
|
578
647
|
content: null,
|
|
@@ -587,7 +656,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
|
|
|
587
656
|
error.filePath = configPath;
|
|
588
657
|
throw error;
|
|
589
658
|
}
|
|
590
|
-
const configContent =
|
|
659
|
+
const configContent = fs.readFileSync(configPath, "utf-8");
|
|
591
660
|
const parsed = JSON.parse(configContent);
|
|
592
661
|
// Cache the result
|
|
593
662
|
contentCache.set(cacheKey, {
|
|
@@ -602,7 +671,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
|
|
|
602
671
|
if (error instanceof Error && error.name === 'MissingConfigurationFile') {
|
|
603
672
|
console.error(`[LeadCMS] ${error.message}`);
|
|
604
673
|
// Re-throw with more context for debugging
|
|
605
|
-
const detailedError = new Error(`Missing configuration files - configName: '${configName}', locale: '${locale}', expected path: ${
|
|
674
|
+
const detailedError = new Error(`Missing configuration files - configName: '${configName}', locale: '${locale}', expected path: ${path.join(getContentDirForLocale(contentDir, locale), `${configName}.json`)}`);
|
|
606
675
|
detailedError.name = 'MissingConfigurationFile';
|
|
607
676
|
detailedError.configName = configName;
|
|
608
677
|
detailedError.locale = locale;
|
|
@@ -627,7 +696,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
|
|
|
627
696
|
* @param userUid - Optional user UID for draft content
|
|
628
697
|
* @returns Parsed config object or null if not found
|
|
629
698
|
*/
|
|
630
|
-
function loadContentConfig(configName, locale, userUid) {
|
|
699
|
+
export function loadContentConfig(configName, locale, userUid) {
|
|
631
700
|
const config = getLeadCMSConfig();
|
|
632
701
|
const actualLocale = locale || config.defaultLanguage;
|
|
633
702
|
const contentDir = config.contentDir;
|
|
@@ -697,7 +766,7 @@ function getFooterConfigInternal(contentDir, locale, userUid) {
|
|
|
697
766
|
* @param locale - Locale code (optional, uses default language from config if not provided)
|
|
698
767
|
* @param userUid - Optional user UID for draft content
|
|
699
768
|
*/
|
|
700
|
-
function getHeaderConfig(locale, userUid) {
|
|
769
|
+
export function getHeaderConfig(locale, userUid) {
|
|
701
770
|
return loadContentConfig('header', locale, userUid);
|
|
702
771
|
}
|
|
703
772
|
/**
|
|
@@ -705,7 +774,7 @@ function getHeaderConfig(locale, userUid) {
|
|
|
705
774
|
* @param locale - Locale code (optional, uses default language from config if not provided)
|
|
706
775
|
* @param userUid - Optional user UID for draft content
|
|
707
776
|
*/
|
|
708
|
-
function getFooterConfig(locale, userUid) {
|
|
777
|
+
export function getFooterConfig(locale, userUid) {
|
|
709
778
|
return loadContentConfig('footer', locale, userUid);
|
|
710
779
|
}
|
|
711
780
|
/**
|
|
@@ -713,7 +782,7 @@ function getFooterConfig(locale, userUid) {
|
|
|
713
782
|
* @param pathname - The pathname to extract locale from
|
|
714
783
|
* @param contentDir - Content directory path (optional, uses configured contentDir if not provided)
|
|
715
784
|
*/
|
|
716
|
-
function getLocaleFromPath(pathname) {
|
|
785
|
+
export function getLocaleFromPath(pathname) {
|
|
717
786
|
const config = getLeadCMSConfig();
|
|
718
787
|
const contentDir = config.contentDir;
|
|
719
788
|
const defaultLanguage = config.defaultLanguage;
|
|
@@ -752,7 +821,7 @@ function getLocaleFromPath(pathname) {
|
|
|
752
821
|
* @param href - The href to make locale-aware
|
|
753
822
|
* @param currentLocale - The current locale
|
|
754
823
|
*/
|
|
755
|
-
function makeLocaleAwareLink(href, currentLocale) {
|
|
824
|
+
export function makeLocaleAwareLink(href, currentLocale) {
|
|
756
825
|
// Don't modify external links or anchors
|
|
757
826
|
if (href.startsWith("http") || href.startsWith("#")) {
|
|
758
827
|
return href;
|
|
@@ -782,7 +851,7 @@ function makeLocaleAwareLink(href, currentLocale) {
|
|
|
782
851
|
* @returns Parsed config object (never returns null)
|
|
783
852
|
* @throws {Error} With detailed information about missing files including configName, locale, and expected path
|
|
784
853
|
*/
|
|
785
|
-
function loadContentConfigStrict(configName, locale, userUid) {
|
|
854
|
+
export function loadContentConfigStrict(configName, locale, userUid) {
|
|
786
855
|
const config = getLeadCMSConfig();
|
|
787
856
|
const actualLocale = locale || config.defaultLanguage;
|
|
788
857
|
const contentDir = config.contentDir;
|