@contentstorage/core 0.3.10 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/lib/contentManagement.d.ts +23 -0
- package/dist/lib/contentManagement.js +101 -0
- package/dist/scripts/generate-types.js +34 -26
- package/dist/types.d.ts +28 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { AppConfig, LanguageCode } from './types.js';
|
|
2
|
-
import {
|
|
3
|
-
export
|
|
4
|
-
export {
|
|
2
|
+
import { setContentLanguage, getCurrentLanguage, getText } from './lib/contentManagement.js';
|
|
3
|
+
export { AppConfig, LanguageCode };
|
|
4
|
+
export { setContentLanguage, getText, getCurrentLanguage };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { setContentLanguage, getCurrentLanguage, getText, } from './lib/contentManagement.js';
|
|
2
|
+
export { setContentLanguage, getText, getCurrentLanguage };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { DotNotationPaths } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Loads and sets the content for a specific language.
|
|
4
|
+
* It will internally ensure the application configuration (for contentDir) is loaded.
|
|
5
|
+
* @param languageCode The language code (e.g., 'EN', 'FR') for the JSON file to load.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setContentLanguage(languageCode: string): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Retrieves the text string from the loaded JSON content for the given path.
|
|
10
|
+
* Autocompletion for pathString is enabled via module augmentation of CustomContentStructure.
|
|
11
|
+
* `setContentLanguage()` must be called successfully before using this.
|
|
12
|
+
*
|
|
13
|
+
* @param pathString A dot-notation path string (e.g., 'HomePage.Login'). Autocompletion is provided.
|
|
14
|
+
* @param fallbackValue Optional string to return if the path is not found or the value is not a string.
|
|
15
|
+
* If not provided, and path is not found/value not string, undefined is returned.
|
|
16
|
+
* @returns The text string from the JSON, or the fallbackValue, or undefined.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getText(pathString: DotNotationPaths, fallbackValue?: string): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Gets the currently active language code.
|
|
21
|
+
* @returns The active language code or null if no language is set.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getCurrentLanguage(): string | null;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { loadConfig } from './configLoader.js';
|
|
4
|
+
let activeContent = null;
|
|
5
|
+
let activeLanguage = null;
|
|
6
|
+
let loadedAppConfig = null; // Cache for the loaded config
|
|
7
|
+
/**
|
|
8
|
+
* Ensures the application configuration (especially contentDir) is loaded.
|
|
9
|
+
* Calls your library's loadConfig function.
|
|
10
|
+
*/
|
|
11
|
+
async function ensureConfigInitialized() {
|
|
12
|
+
if (loadedAppConfig) {
|
|
13
|
+
return loadedAppConfig;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const config = await loadConfig(); // This is your library's function
|
|
17
|
+
if (!config || !config.contentDir) {
|
|
18
|
+
throw new Error('contentDir not found in the loaded configuration (contentstorage.config.js).');
|
|
19
|
+
}
|
|
20
|
+
loadedAppConfig = config; // Ensure it matches the expected structure
|
|
21
|
+
console.log(`[LocalizationLibrary] Configuration loaded. Content directory set to: ${loadedAppConfig.contentDir}`);
|
|
22
|
+
return loadedAppConfig;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error('[Contentstorage] Failed to initialize configuration:', error);
|
|
26
|
+
throw new Error(`[Contentstorage] Critical error: Could not load or validate app configuration. ${error.message}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Loads and sets the content for a specific language.
|
|
31
|
+
* It will internally ensure the application configuration (for contentDir) is loaded.
|
|
32
|
+
* @param languageCode The language code (e.g., 'EN', 'FR') for the JSON file to load.
|
|
33
|
+
*/
|
|
34
|
+
export async function setContentLanguage(languageCode) {
|
|
35
|
+
if (!languageCode ||
|
|
36
|
+
typeof languageCode !== 'string' ||
|
|
37
|
+
languageCode.trim() === '') {
|
|
38
|
+
throw new Error('[Contentstorage] Invalid language code provided to setContentLanguage.');
|
|
39
|
+
}
|
|
40
|
+
const config = await ensureConfigInitialized(); // Gets contentDir from loaded config
|
|
41
|
+
const targetFilename = `${languageCode}.json`;
|
|
42
|
+
const jsonFilePath = path.join(config.contentDir, targetFilename);
|
|
43
|
+
console.log(`[Contentstorage] Attempting to load content for language '${languageCode}' from ${jsonFilePath}...`);
|
|
44
|
+
try {
|
|
45
|
+
const jsonContentString = await fs.readFile(jsonFilePath, 'utf-8');
|
|
46
|
+
activeContent = JSON.parse(jsonContentString); // Relies on augmentation
|
|
47
|
+
activeLanguage = languageCode;
|
|
48
|
+
console.log(`[Contentstorage] Content for language '${languageCode}' loaded successfully.`);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
activeContent = null; // Reset on failure
|
|
52
|
+
console.error(`[Contentstorage] Failed to load content for language '${languageCode}' from ${jsonFilePath}. Error: ${error.message}`);
|
|
53
|
+
throw new Error(`[Contentstorage] Could not load content for language: ${languageCode}. Ensure file exists at '${jsonFilePath}' and is valid JSON.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Retrieves the text string from the loaded JSON content for the given path.
|
|
58
|
+
* Autocompletion for pathString is enabled via module augmentation of CustomContentStructure.
|
|
59
|
+
* `setContentLanguage()` must be called successfully before using this.
|
|
60
|
+
*
|
|
61
|
+
* @param pathString A dot-notation path string (e.g., 'HomePage.Login'). Autocompletion is provided.
|
|
62
|
+
* @param fallbackValue Optional string to return if the path is not found or the value is not a string.
|
|
63
|
+
* If not provided, and path is not found/value not string, undefined is returned.
|
|
64
|
+
* @returns The text string from the JSON, or the fallbackValue, or undefined.
|
|
65
|
+
*/
|
|
66
|
+
export function getText(
|
|
67
|
+
// @ts-expect-error Is fine
|
|
68
|
+
pathString, fallbackValue) {
|
|
69
|
+
if (!activeContent) {
|
|
70
|
+
const msg = `[Contentstorage] getText: Content not loaded (Path: "${String(pathString)}"). Ensure setContentLanguage() was called and completed successfully.`;
|
|
71
|
+
console.warn(msg);
|
|
72
|
+
return fallbackValue;
|
|
73
|
+
}
|
|
74
|
+
const keys = pathString.split('.');
|
|
75
|
+
let current = activeContent;
|
|
76
|
+
for (const key of keys) {
|
|
77
|
+
if (current && typeof current === 'object' && key in current) {
|
|
78
|
+
current = current[key];
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const msg = `[Contentstorage] getText: Path "${String(pathString)}" not found in loaded content for language '${activeLanguage}'.`;
|
|
82
|
+
console.warn(msg);
|
|
83
|
+
return fallbackValue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (typeof current === 'string') {
|
|
87
|
+
return current;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const msg = `[Contentstorage] getText: Value at path "${String(pathString)}" is not a string (actual type: ${typeof current}).`;
|
|
91
|
+
console.warn(msg);
|
|
92
|
+
return fallbackValue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Gets the currently active language code.
|
|
97
|
+
* @returns The active language code or null if no language is set.
|
|
98
|
+
*/
|
|
99
|
+
export function getCurrentLanguage() {
|
|
100
|
+
return activeLanguage;
|
|
101
|
+
}
|
|
@@ -7,52 +7,56 @@ import chalk from 'chalk'; // Optional: for colored output
|
|
|
7
7
|
import { loadConfig } from '../lib/configLoader.js';
|
|
8
8
|
export async function generateTypes() {
|
|
9
9
|
console.log(chalk.blue('Starting type generation...'));
|
|
10
|
-
const config = await loadConfig();
|
|
11
|
-
console.log(chalk.gray(`
|
|
10
|
+
const config = await loadConfig(); // Ensure loadConfig provides languageCodes
|
|
11
|
+
console.log(chalk.gray(`Content will be read from directory: ${config.contentDir}`));
|
|
12
12
|
console.log(chalk.gray(`Saving TypeScript types to: ${config.typesOutputFile}`));
|
|
13
13
|
try {
|
|
14
|
-
//
|
|
15
|
-
|
|
14
|
+
// Validate languageCodes from config
|
|
15
|
+
if (!config.languageCodes ||
|
|
16
|
+
!Array.isArray(config.languageCodes) ||
|
|
17
|
+
config.languageCodes.length === 0) {
|
|
18
|
+
throw new Error('config.languageCodes is missing, not an array, or empty. Cannot determine which JSON file to use for type generation.');
|
|
19
|
+
}
|
|
20
|
+
const firstLanguageCode = config.languageCodes[0];
|
|
21
|
+
const targetFilename = `${firstLanguageCode}.json`;
|
|
22
|
+
const jsonFilePath = path.join(config.contentDir, targetFilename);
|
|
23
|
+
console.log(chalk.gray(`Attempting to generate types using the JSON file for the first configured language code ('${firstLanguageCode}').`));
|
|
24
|
+
console.log(chalk.gray(`Target file: ${jsonFilePath}`));
|
|
25
|
+
// Read the specific JSON file
|
|
26
|
+
let jsonContent;
|
|
16
27
|
try {
|
|
17
|
-
|
|
28
|
+
jsonContent = await fs.readFile(jsonFilePath, 'utf-8');
|
|
18
29
|
}
|
|
19
30
|
catch (err) {
|
|
20
31
|
if (err.code === 'ENOENT') {
|
|
21
|
-
throw new Error(`
|
|
32
|
+
throw new Error(`Target JSON file not found: ${jsonFilePath}. Ensure content for language code '${firstLanguageCode}' has been pulled and exists at this location.`);
|
|
22
33
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Filter for JSON files and sort them (optional, but good for consistency)
|
|
26
|
-
const jsonFiles = files
|
|
27
|
-
.filter((file) => file.toLowerCase().endsWith('.json'))
|
|
28
|
-
.sort();
|
|
29
|
-
if (jsonFiles.length === 0) {
|
|
30
|
-
throw new Error(`No JSON files found in ${config.contentDir}.`);
|
|
34
|
+
// Re-throw other fs.readFile errors (e.g., permission issues)
|
|
35
|
+
throw new Error(`Failed to read file ${jsonFilePath}: ${err.message}`);
|
|
31
36
|
}
|
|
32
|
-
const firstJsonFile = jsonFiles[0];
|
|
33
|
-
const jsonFilePath = path.join(config.contentDir, firstJsonFile);
|
|
34
|
-
console.log(chalk.gray(`Using first JSON file for type generation: ${firstJsonFile}`));
|
|
35
|
-
// Read the first JSON file
|
|
36
|
-
const jsonContent = await fs.readFile(jsonFilePath, 'utf-8');
|
|
37
37
|
// Parse the JSON content
|
|
38
38
|
let jsonObject;
|
|
39
39
|
try {
|
|
40
40
|
jsonObject = JSON.parse(jsonContent);
|
|
41
41
|
}
|
|
42
42
|
catch (parseError) {
|
|
43
|
-
throw new Error(`Failed to parse JSON file ${
|
|
43
|
+
throw new Error(`Failed to parse JSON from file ${targetFilename}: ${parseError.message}`);
|
|
44
44
|
}
|
|
45
45
|
// Generate TypeScript interfaces using json-to-ts
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
const rootTypeName = 'RootContentItem'; // Or derive from filename, or make configurable
|
|
46
|
+
// The root type name for the generated interface. You might want to make this configurable.
|
|
47
|
+
const rootTypeName = 'ContentRoot';
|
|
49
48
|
const typeDeclarations = jsonToTS.default(jsonObject, {
|
|
50
49
|
rootName: rootTypeName,
|
|
51
50
|
});
|
|
51
|
+
// If your previous code `jsonToTS.default(...)` was correct for your setup,
|
|
52
|
+
// please revert the line above to:
|
|
53
|
+
// const typeDeclarations: string[] = jsonToTS.default(jsonObject, {
|
|
54
|
+
// rootName: rootTypeName,
|
|
55
|
+
// });
|
|
52
56
|
if (!typeDeclarations || typeDeclarations.length === 0) {
|
|
53
|
-
throw new Error(`Could not generate types from ${
|
|
57
|
+
throw new Error(`Could not generate types from ${targetFilename}. The file might be empty, malformed, or not produce any types.`);
|
|
54
58
|
}
|
|
55
|
-
const outputContent = typeDeclarations.join('\n\n'); // Add extra newline between interfaces
|
|
59
|
+
const outputContent = typeDeclarations.join('\n\n'); // Add extra newline between interfaces for readability
|
|
56
60
|
// Ensure the output directory exists
|
|
57
61
|
const outputDir = path.dirname(config.typesOutputFile);
|
|
58
62
|
await fs.mkdir(outputDir, { recursive: true });
|
|
@@ -61,8 +65,12 @@ export async function generateTypes() {
|
|
|
61
65
|
console.log(chalk.green(`TypeScript types generated successfully at ${config.typesOutputFile}`));
|
|
62
66
|
}
|
|
63
67
|
catch (error) {
|
|
64
|
-
console.error(chalk.red('
|
|
68
|
+
console.error(chalk.red.bold('\nError generating TypeScript types:'));
|
|
65
69
|
console.error(chalk.red(error.message));
|
|
70
|
+
// It's good practice to log the stack for unexpected errors if not already done by a higher-level handler
|
|
71
|
+
// if (error.stack) {
|
|
72
|
+
// console.error(chalk.gray(error.stack));
|
|
73
|
+
// }
|
|
66
74
|
process.exit(1); // Exit with error code
|
|
67
75
|
}
|
|
68
76
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -5,3 +5,31 @@ export type AppConfig = {
|
|
|
5
5
|
typesOutputFile: string;
|
|
6
6
|
};
|
|
7
7
|
export type LanguageCode = 'SQ' | 'BE' | 'BS' | 'BG' | 'HR' | 'CS' | 'DA' | 'NL' | 'EN' | 'ET' | 'FI' | 'FR' | 'DE' | 'EL' | 'HU' | 'GA' | 'IT' | 'LV' | 'LT' | 'MK' | 'NO' | 'PL' | 'PT' | 'RO' | 'RU' | 'SR' | 'SK' | 'SL' | 'ES' | 'SV' | 'TR' | 'UK';
|
|
8
|
+
/**
|
|
9
|
+
* This interface is intended to be augmented by the consumer application.
|
|
10
|
+
* By augmenting this interface with their specific RootContentItem type,
|
|
11
|
+
* consumers enable type-safe autocompletion for localization path strings
|
|
12
|
+
* used with functions like `getText`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // In consumer's project (e.g., in a .d.ts file):
|
|
16
|
+
* import 'your-library-name'; // Your library's package name
|
|
17
|
+
* import type { RootContentItem as AppSpecificRootItem } from './generated_content_types';
|
|
18
|
+
*
|
|
19
|
+
* declare module 'your-library-name' {
|
|
20
|
+
* export interface ContentStructure extends AppSpecificRootItem {}
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
export interface ContentStructure {
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generates a union of all possible dot-notation path strings for a given object type `T`.
|
|
28
|
+
* Defaults to using the `ContentStructure` interface, which consumers augment.
|
|
29
|
+
*
|
|
30
|
+
* @template T The object type to generate paths from. Defaults to `ContentStructure`.
|
|
31
|
+
* @template Prefix Internal accumulator for the current path prefix during recursion.
|
|
32
|
+
*/
|
|
33
|
+
export type DotNotationPaths<T = ContentStructure, Prefix extends string = ''> = T extends object ? {
|
|
34
|
+
[K in keyof T]-?: K extends string | number ? T[K] extends object ? `${Prefix}${K}` | DotNotationPaths<T[K], `${Prefix}${K}.`> : `${Prefix}${K}` : never;
|
|
35
|
+
}[keyof T] : '';
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@contentstorage/core",
|
|
3
3
|
"author": "Kaido Hussar <kaidohus@gmail.com>",
|
|
4
4
|
"homepage": "https://contentstorage.app",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.12",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "Fetch content from contentstorage and generate TypeScript types",
|
|
8
8
|
"module": "dist/index.js",
|