@contentstorage/core 2.1.1 → 2.2.1
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 +10 -10
- package/dist/commands/cli.js +2 -1
- package/dist/commands/pull.js +6 -1
- package/dist/types.d.ts +1 -0
- package/dist/utils/flatten-json.d.ts +1 -1
- package/dist/utils/flatten-json.js +2 -37
- package/package.json +2 -2
- package/dist/contentstorage-config.d.ts +0 -4
- package/dist/contentstorage-config.js +0 -4
- package/dist/helpers/defineConfig.d.ts +0 -6
- package/dist/helpers/defineConfig.js +0 -19
- package/dist/helpers/flattenJson.d.ts +0 -1
- package/dist/helpers/flattenJson.js +0 -56
- package/dist/helpers/populateTextWithVariables.d.ts +0 -1
- package/dist/helpers/populateTextWithVariables.js +0 -10
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -62
- package/dist/lib/configLoader.d.ts +0 -2
- package/dist/lib/configLoader.js +0 -42
- package/dist/lib/contentManagement.d.ts +0 -32
- package/dist/lib/contentManagement.js +0 -257
- package/dist/lib/functions/fetchContent.d.ts +0 -7
- package/dist/lib/functions/fetchContent.js +0 -141
- package/dist/scripts/cli.d.ts +0 -2
- package/dist/scripts/cli.js +0 -96
- package/dist/scripts/generate-types.d.ts +0 -2
- package/dist/scripts/generate-types.js +0 -174
- package/dist/scripts/pull-content.d.ts +0 -2
- package/dist/scripts/pull-content.js +0 -137
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// ^ Ensures the script is executed with Node.js
|
|
3
|
-
import fs from 'fs/promises';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import axios from 'axios';
|
|
6
|
-
import chalk from 'chalk'; // Optional: for colored output
|
|
7
|
-
import { loadConfig } from '../lib/configLoader.js';
|
|
8
|
-
import { flattenJson } from '../helpers/flattenJson.js';
|
|
9
|
-
import { CONTENTSTORAGE_CONFIG } from '../contentstorage-config.js';
|
|
10
|
-
import { jsonToTS } from '../type-generation/index.js';
|
|
11
|
-
export async function generateTypes() {
|
|
12
|
-
console.log(chalk.blue('Starting type generation...'));
|
|
13
|
-
const args = process.argv.slice(2);
|
|
14
|
-
const cliConfig = {};
|
|
15
|
-
for (let i = 0; i < args.length; i++) {
|
|
16
|
-
const arg = args[i];
|
|
17
|
-
if (arg.startsWith('--')) {
|
|
18
|
-
const key = arg.substring(2);
|
|
19
|
-
const value = args[i + 1];
|
|
20
|
-
if (key === 'pending-changes') {
|
|
21
|
-
cliConfig.pendingChanges = true;
|
|
22
|
-
}
|
|
23
|
-
else if (value && !value.startsWith('--')) {
|
|
24
|
-
if (key === 'lang') {
|
|
25
|
-
cliConfig.languageCodes = [value.toUpperCase()];
|
|
26
|
-
}
|
|
27
|
-
else if (key === 'content-key') {
|
|
28
|
-
cliConfig.contentKey = value;
|
|
29
|
-
}
|
|
30
|
-
else if (key === 'output') {
|
|
31
|
-
cliConfig.typesOutputFile = value;
|
|
32
|
-
}
|
|
33
|
-
// Skip the value in the next iteration
|
|
34
|
-
i++;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
let fileConfig = {};
|
|
39
|
-
try {
|
|
40
|
-
fileConfig = await loadConfig();
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
console.log(chalk.yellow('Could not load a configuration file. Proceeding with CLI arguments.'));
|
|
44
|
-
}
|
|
45
|
-
const config = { ...fileConfig, ...cliConfig };
|
|
46
|
-
if (!config.typesOutputFile) {
|
|
47
|
-
console.error(chalk.red.bold("Configuration error: 'typesOutputFile' is missing."));
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
if (!config.languageCodes ||
|
|
51
|
-
!Array.isArray(config.languageCodes) ||
|
|
52
|
-
config.languageCodes.length === 0) {
|
|
53
|
-
console.error(chalk.red.bold("Configuration error: 'languageCodes' must be a non-empty array."));
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
console.log(chalk.blue(`TypeScript types will be saved to: ${config.typesOutputFile}`));
|
|
57
|
-
let jsonObject; // To hold the JSON data from either local or remote source
|
|
58
|
-
let dataSourceDescription = ''; // For clearer logging
|
|
59
|
-
const firstLanguageCode = config.languageCodes[0];
|
|
60
|
-
try {
|
|
61
|
-
let attemptLocalLoad = false;
|
|
62
|
-
if (config.contentDir) {
|
|
63
|
-
try {
|
|
64
|
-
await fs.stat(config.contentDir); // Check if directory exists
|
|
65
|
-
attemptLocalLoad = true;
|
|
66
|
-
console.log(chalk.blue(`Local content directory found: ${config.contentDir}`));
|
|
67
|
-
}
|
|
68
|
-
catch (statError) {
|
|
69
|
-
if (statError.code === 'ENOENT') {
|
|
70
|
-
console.log(chalk.yellow(`Local content directory specified but not found: ${config.contentDir}. Will attempt to fetch from URL.`));
|
|
71
|
-
// attemptLocalLoad remains false
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
// Other errors accessing contentDir (e.g., permissions)
|
|
75
|
-
throw new Error(`Error accessing content directory ${config.contentDir}: ${statError.message}`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
console.log(chalk.yellow(`Local content directory (config.contentDir) not specified. Attempting to fetch from URL.`));
|
|
81
|
-
}
|
|
82
|
-
if (attemptLocalLoad && config.contentDir) {
|
|
83
|
-
// --- Load from local file system ---
|
|
84
|
-
const targetFilename = `${firstLanguageCode}.json`;
|
|
85
|
-
const jsonFilePath = path.join(config.contentDir, targetFilename);
|
|
86
|
-
dataSourceDescription = `local file (${jsonFilePath})`;
|
|
87
|
-
console.log(chalk.blue(`Attempting to read JSON from: ${jsonFilePath}`));
|
|
88
|
-
try {
|
|
89
|
-
const jsonContentString = await fs.readFile(jsonFilePath, 'utf-8');
|
|
90
|
-
console.log(chalk.blue('Parsing JSON'));
|
|
91
|
-
const parsendJsonObject = JSON.parse(jsonContentString);
|
|
92
|
-
console.log(chalk.blue('Flattening JSON for type generation'));
|
|
93
|
-
jsonObject = flattenJson(parsendJsonObject);
|
|
94
|
-
console.log(chalk.green(`Successfully read and parsed JSON from ${jsonFilePath}.`));
|
|
95
|
-
}
|
|
96
|
-
catch (fileError) {
|
|
97
|
-
if (fileError.code === 'ENOENT') {
|
|
98
|
-
throw new Error(`Target JSON file not found at ${jsonFilePath}. ` +
|
|
99
|
-
`Ensure content for language code '${firstLanguageCode}' has been pulled and exists locally, ` +
|
|
100
|
-
`or ensure 'contentDir' is not set if you intend to fetch from URL.`);
|
|
101
|
-
}
|
|
102
|
-
throw new Error(`Failed to read or parse JSON from ${jsonFilePath}: ${fileError.message}`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
if (!config.contentKey) {
|
|
107
|
-
throw new Error('Cannot generate types: contentKey is missing');
|
|
108
|
-
}
|
|
109
|
-
let fileUrl;
|
|
110
|
-
const requestConfig = { responseType: 'json' };
|
|
111
|
-
if (config.pendingChanges) {
|
|
112
|
-
fileUrl = `${CONTENTSTORAGE_CONFIG.API_URL}/pending-changes/get-json?languageCode=${firstLanguageCode}`;
|
|
113
|
-
requestConfig.headers = {
|
|
114
|
-
'X-Content-Key': config.contentKey,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
fileUrl = `${CONTENTSTORAGE_CONFIG.BASE_URL}/${config.contentKey}/content/${firstLanguageCode}.json`;
|
|
119
|
-
}
|
|
120
|
-
dataSourceDescription = `remote URL (${fileUrl})`;
|
|
121
|
-
console.log(chalk.blue(`Attempting to fetch JSON from: ${fileUrl}`));
|
|
122
|
-
try {
|
|
123
|
-
const response = await axios.get(fileUrl, requestConfig);
|
|
124
|
-
let jsonResponse = response.data;
|
|
125
|
-
// Handle API response structure - API returns { data: actualContent }
|
|
126
|
-
if (config.pendingChanges && jsonResponse && typeof jsonResponse === 'object' && 'data' in jsonResponse) {
|
|
127
|
-
jsonResponse = jsonResponse.data;
|
|
128
|
-
}
|
|
129
|
-
console.log(chalk.blue('Flattening JSON for type generation'));
|
|
130
|
-
jsonObject = flattenJson(jsonResponse);
|
|
131
|
-
if (typeof jsonObject !== 'object' || jsonObject === null) {
|
|
132
|
-
throw new Error(`Workspaceed data from ${fileUrl} is not a valid JSON object. Received type: ${typeof jsonObject}`);
|
|
133
|
-
}
|
|
134
|
-
console.log(chalk.green(`Successfully fetched and parsed JSON from ${fileUrl}. This content will not be saved locally.`));
|
|
135
|
-
}
|
|
136
|
-
catch (fetchError) {
|
|
137
|
-
let errorDetail = fetchError.message;
|
|
138
|
-
if (axios.isAxiosError(fetchError)) {
|
|
139
|
-
errorDetail = `Status: ${fetchError.response?.status}, Response: ${JSON.stringify(fetchError.response?.data)}`;
|
|
140
|
-
}
|
|
141
|
-
throw new Error(`Failed to fetch JSON from ${fileUrl}: ${errorDetail}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
// Validate the obtained jsonObject (must be an object or array for json-to-ts)
|
|
145
|
-
if (typeof jsonObject !== 'object' || jsonObject === null) {
|
|
146
|
-
// jsonToTS can handle root arrays too, but if it's primitive it's an issue.
|
|
147
|
-
// Allowing arrays here explicitly based on jsonToTS capability.
|
|
148
|
-
if (!Array.isArray(jsonObject)) {
|
|
149
|
-
throw new Error(`The content obtained from ${dataSourceDescription} is not a JSON object or array (type: ${typeof jsonObject}). Cannot generate types.`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
// Generate TypeScript interfaces using json-to-ts
|
|
153
|
-
const rootTypeName = 'ContentRoot';
|
|
154
|
-
console.log(chalk.blue(`Generating TypeScript types with root name '${rootTypeName}'...`));
|
|
155
|
-
const typeDeclarations = jsonToTS(jsonObject, {
|
|
156
|
-
rootName: rootTypeName,
|
|
157
|
-
});
|
|
158
|
-
if (!typeDeclarations || typeDeclarations.length === 0) {
|
|
159
|
-
throw new Error(`Could not generate types from the content of ${dataSourceDescription}. 'json-to-ts' returned no declarations.`);
|
|
160
|
-
}
|
|
161
|
-
const outputContent = typeDeclarations.join('\n\n');
|
|
162
|
-
// Ensure the output directory exists for the types file
|
|
163
|
-
const outputDir = path.dirname(config.typesOutputFile);
|
|
164
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
165
|
-
// Write the generated types to the output file
|
|
166
|
-
await fs.writeFile(config.typesOutputFile, outputContent, 'utf-8');
|
|
167
|
-
console.log(chalk.green(`TypeScript types generated successfully at ${config.typesOutputFile} using data from ${dataSourceDescription}.`));
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
console.error(chalk.red.bold('\nError generating TypeScript types:'));
|
|
171
|
-
console.error(chalk.red(error.message));
|
|
172
|
-
process.exit(1);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import axios from 'axios';
|
|
3
|
-
import fs from 'fs/promises';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { loadConfig } from '../lib/configLoader.js';
|
|
7
|
-
import { CONTENTSTORAGE_CONFIG } from '../contentstorage-config.js';
|
|
8
|
-
export async function pullContent() {
|
|
9
|
-
console.log(chalk.blue('Starting content pull...'));
|
|
10
|
-
const args = process.argv.slice(2);
|
|
11
|
-
const cliConfig = {};
|
|
12
|
-
for (let i = 0; i < args.length; i++) {
|
|
13
|
-
const arg = args[i];
|
|
14
|
-
if (arg.startsWith('--')) {
|
|
15
|
-
const key = arg.substring(2);
|
|
16
|
-
const value = args[i + 1];
|
|
17
|
-
if (key === 'pending-changes') {
|
|
18
|
-
cliConfig.pendingChanges = true;
|
|
19
|
-
}
|
|
20
|
-
else if (value && !value.startsWith('--')) {
|
|
21
|
-
if (key === 'lang') {
|
|
22
|
-
cliConfig.languageCodes = [value.toUpperCase()];
|
|
23
|
-
}
|
|
24
|
-
else if (key === 'content-key') {
|
|
25
|
-
cliConfig.contentKey = value;
|
|
26
|
-
}
|
|
27
|
-
else if (key === 'content-dir') {
|
|
28
|
-
cliConfig.contentDir = value;
|
|
29
|
-
}
|
|
30
|
-
// Skip the value in the next iteration
|
|
31
|
-
i++;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
let fileConfig = {};
|
|
36
|
-
try {
|
|
37
|
-
fileConfig = await loadConfig();
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
console.log(chalk.yellow('Could not load a configuration file. Proceeding with CLI arguments.'));
|
|
41
|
-
}
|
|
42
|
-
const config = { ...fileConfig, ...cliConfig };
|
|
43
|
-
// Validate required fields
|
|
44
|
-
if (!config.contentKey) {
|
|
45
|
-
console.error(chalk.red('Error: Configuration is missing the required "contentKey" property.'));
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
if (!config.contentDir) {
|
|
49
|
-
console.error(chalk.red('Error: Configuration is missing the required "contentDir" property.'));
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
console.log(chalk.blue(`Content key: ${config.contentKey}`));
|
|
53
|
-
console.log(chalk.blue(`Saving content to: ${config.contentDir}`));
|
|
54
|
-
try {
|
|
55
|
-
// Validate languageCodes array
|
|
56
|
-
if (!Array.isArray(config.languageCodes)) {
|
|
57
|
-
console.log(chalk.red(`Expected array from config.languageCodes, but received type ${typeof config.languageCodes}. Cannot pull files.`));
|
|
58
|
-
return; // Exit if languageCodes is not an array
|
|
59
|
-
}
|
|
60
|
-
if (config.languageCodes.length === 0) {
|
|
61
|
-
console.log(chalk.yellow('config.languageCodes array is empty. No files to pull.'));
|
|
62
|
-
return; // Exit if languageCodes array is empty
|
|
63
|
-
}
|
|
64
|
-
// Ensure the output directory exists (create it once before the loop)
|
|
65
|
-
await fs.mkdir(config.contentDir, { recursive: true });
|
|
66
|
-
// Process each language code
|
|
67
|
-
for (const languageCode of config.languageCodes) {
|
|
68
|
-
let fileUrl;
|
|
69
|
-
const requestConfig = {};
|
|
70
|
-
if (config.pendingChanges) {
|
|
71
|
-
fileUrl = `${CONTENTSTORAGE_CONFIG.API_URL}/pending-changes/get-json?languageCode=${languageCode}`;
|
|
72
|
-
requestConfig.headers = {
|
|
73
|
-
'X-Content-Key': config.contentKey
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
fileUrl = `${CONTENTSTORAGE_CONFIG.BASE_URL}/${config.contentKey}/content/${languageCode}.json`;
|
|
78
|
-
}
|
|
79
|
-
const filename = `${languageCode}.json`;
|
|
80
|
-
const outputPath = path.join(config.contentDir, filename);
|
|
81
|
-
console.log(chalk.blue(`\nProcessing language: ${languageCode}`));
|
|
82
|
-
console.log(chalk.blue(`Using following contentKey to fetch json: ${config.contentKey}`));
|
|
83
|
-
try {
|
|
84
|
-
// Fetch data for the current language
|
|
85
|
-
const response = await axios.get(fileUrl, requestConfig);
|
|
86
|
-
let jsonData = response.data;
|
|
87
|
-
// Handle API response structure - only for pending changes API
|
|
88
|
-
if (config.pendingChanges && jsonData && typeof jsonData === 'object' && 'data' in jsonData) {
|
|
89
|
-
jsonData = jsonData.data;
|
|
90
|
-
}
|
|
91
|
-
// Basic check for data existence, although axios usually throws for non-2xx responses
|
|
92
|
-
if (jsonData === undefined || jsonData === null) {
|
|
93
|
-
throw new Error(`No data received from ${fileUrl} for language ${languageCode}.`);
|
|
94
|
-
}
|
|
95
|
-
// Validate that jsonData is a single, non-null JSON object (not an array)
|
|
96
|
-
// This check mirrors the original code's expectation for the content of a JSON file.
|
|
97
|
-
if (typeof jsonData !== 'object' ||
|
|
98
|
-
Array.isArray(jsonData) /* jsonData === null is already covered */) {
|
|
99
|
-
throw new Error(`Expected a single JSON object from ${fileUrl} for language ${languageCode}, but received type ${Array.isArray(jsonData) ? 'array' : typeof jsonData}. Cannot save the file.`);
|
|
100
|
-
}
|
|
101
|
-
console.log(chalk.green(`Received JSON for ${languageCode}. Saving to ${outputPath}`));
|
|
102
|
-
await fs.writeFile(outputPath, JSON.stringify(jsonData, null, 2));
|
|
103
|
-
console.log(chalk.green(`Successfully saved ${outputPath}`));
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
// Catch errors related to fetching or saving a single language file
|
|
107
|
-
console.error(chalk.red(`\nError processing language ${languageCode} from ${fileUrl}:`));
|
|
108
|
-
if (axios.isAxiosError(error)) {
|
|
109
|
-
console.error(chalk.red(` Status: ${error.response?.status}`));
|
|
110
|
-
console.error(chalk.red(`Response Data: ${error.response?.data ? JSON.stringify(error.response.data) : 'N/A'}`));
|
|
111
|
-
console.error(chalk.red(` Message: ${error.message}`)); // Axios error message
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
// For non-Axios errors (e.g., manually thrown errors, fs errors)
|
|
115
|
-
console.error(chalk.red(` Error: ${error.message}`));
|
|
116
|
-
}
|
|
117
|
-
// Re-throw the error to be caught by the outer try-catch block,
|
|
118
|
-
// which will then call process.exit(1), maintaining original exit behavior on error.
|
|
119
|
-
throw error;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
console.log(chalk.green('\nAll content successfully pulled and saved.'));
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
// Outer catch for setup errors (like loadConfig) or re-thrown errors from the loop
|
|
126
|
-
// The specific error details for a file operation would have been logged by the inner catch.
|
|
127
|
-
// This block provides a general failure message and ensures the process exits with an error code.
|
|
128
|
-
console.error(chalk.red('\n-----------------------------------------------------'));
|
|
129
|
-
console.error(chalk.red('Content pull failed due to an error. See details above.'));
|
|
130
|
-
// error.message from the re-thrown error will be implicitly part of the error object logged by some environments,
|
|
131
|
-
// or if you add console.error(error) here.
|
|
132
|
-
// The original code logged error.message at this level:
|
|
133
|
-
// if (error.message) console.error(chalk.red(`Underlying error: ${error.message}`));
|
|
134
|
-
console.error(chalk.red('-----------------------------------------------------'));
|
|
135
|
-
process.exit(1); // Exit with error code
|
|
136
|
-
}
|
|
137
|
-
}
|