@jungvonmatt/contentful-config 4.0.1 → 4.1.0
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 +55 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +119 -0
- package/dist/cli.js.map +1 -0
- package/package.json +8 -4
- package/src/cli.ts +147 -0
package/README.md
CHANGED
|
@@ -48,5 +48,60 @@ const { config } = await loadContentfulConfig('myapp', {
|
|
|
48
48
|
// either from existing sources or user input
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
## CLI
|
|
52
|
+
|
|
53
|
+
The package includes a CLI tool to generate `.env`-compatible configuration files.
|
|
54
|
+
|
|
55
|
+
### Usage
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
contentful-config [options]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Options
|
|
62
|
+
|
|
63
|
+
| Option | Description |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `-n, --name <name>` | Config name (default: `contentful`) |
|
|
66
|
+
| `-o, --output <file>` | Write output to a file instead of stdout |
|
|
67
|
+
| `-r, --required <keys>` | Required config keys, comma-separated or repeated (default: `spaceId,environmentId,accessToken,previewAccessToken`) |
|
|
68
|
+
| `-h, --help` | Show help message |
|
|
69
|
+
|
|
70
|
+
### Examples
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Print .env config to stdout
|
|
74
|
+
contentful-config
|
|
75
|
+
|
|
76
|
+
# Write to a .env file (merges with existing content)
|
|
77
|
+
contentful-config -o .env
|
|
78
|
+
|
|
79
|
+
# Use a custom config name
|
|
80
|
+
contentful-config -n myapp -o .env
|
|
81
|
+
|
|
82
|
+
# Specify custom required keys
|
|
83
|
+
contentful-config -r spaceId,accessToken
|
|
84
|
+
|
|
85
|
+
# Or repeat the flag
|
|
86
|
+
contentful-config -r spaceId -r accessToken -r environmentId
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Behavior
|
|
90
|
+
|
|
91
|
+
- **Login check**: If no Contentful management token is found, the CLI automatically runs `contentful login` to authenticate.
|
|
92
|
+
- **Interactive prompts**: All required keys are prompted interactively, with existing values pre-filled as defaults.
|
|
93
|
+
- **File merging**: When using `-o`, existing file content is preserved. Matching keys are updated in place, new keys are appended. Comments and unrelated entries remain untouched.
|
|
94
|
+
- **Output**: The following environment variables are generated:
|
|
95
|
+
|
|
96
|
+
| Config Key | Environment Variable |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `spaceId` | `CONTENTFUL_SPACE_ID` |
|
|
99
|
+
| `environmentId` | `CONTENTFUL_ENVIRONMENT_ID` |
|
|
100
|
+
| `accessToken` | `CONTENTFUL_DELIVERY_ACCESS_TOKEN` |
|
|
101
|
+
| `previewAccessToken` | `CONTENTFUL_PREVIEW_ACCESS_TOKEN` |
|
|
102
|
+
| `host` | `CONTENTFUL_HOST` |
|
|
103
|
+
|
|
104
|
+
> **Note:** The management token is intentionally excluded from the output as it is read from the Contentful CLI config file.
|
|
105
|
+
|
|
51
106
|
[npm-url]: https://www.npmjs.com/package/@jungvonmatt/contentful-config
|
|
52
107
|
[npm-image]: https://img.shields.io/npm/v/@jungvonmatt/contentful-config.svg
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { parseArgs } from 'node:util';
|
|
6
|
+
import { loadContentfulConfig } from './index.js';
|
|
7
|
+
const ENV_MAP = {
|
|
8
|
+
spaceId: 'CONTENTFUL_SPACE_ID',
|
|
9
|
+
environmentId: 'CONTENTFUL_ENVIRONMENT_ID',
|
|
10
|
+
managementToken: 'CONTENTFUL_MANAGEMENT_TOKEN',
|
|
11
|
+
previewAccessToken: 'CONTENTFUL_PREVIEW_ACCESS_TOKEN',
|
|
12
|
+
accessToken: 'CONTENTFUL_DELIVERY_ACCESS_TOKEN',
|
|
13
|
+
host: 'CONTENTFUL_HOST',
|
|
14
|
+
};
|
|
15
|
+
const SKIP_KEYS = new Set([
|
|
16
|
+
'organizationId',
|
|
17
|
+
'activeSpaceId',
|
|
18
|
+
'activeEnvironmentId',
|
|
19
|
+
'managementToken',
|
|
20
|
+
]);
|
|
21
|
+
function usage() {
|
|
22
|
+
console.log(`Usage: contentful-config [options]
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
-n, --name <name> Config name (default: "contentful")
|
|
26
|
+
-o, --output <file> Write output to file instead of stdout
|
|
27
|
+
-r, --required <keys> Required config keys (comma-separated or repeated)
|
|
28
|
+
Default: spaceId,environmentId,accessToken,previewAccessToken
|
|
29
|
+
-h, --help Show this help message`);
|
|
30
|
+
}
|
|
31
|
+
async function ensureLogin(name) {
|
|
32
|
+
const { config } = await loadContentfulConfig(name, { prompts: false });
|
|
33
|
+
if (config.managementToken) {
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
console.error('No management token found. Starting contentful login…');
|
|
37
|
+
execFileSync('contentful', ['login'], { stdio: 'inherit' });
|
|
38
|
+
const { config: updatedConfig } = await loadContentfulConfig(name, { prompts: false });
|
|
39
|
+
if (!updatedConfig.managementToken) {
|
|
40
|
+
throw new Error('Login failed. No management token found after login.');
|
|
41
|
+
}
|
|
42
|
+
return updatedConfig;
|
|
43
|
+
}
|
|
44
|
+
async function main() {
|
|
45
|
+
const { values } = parseArgs({
|
|
46
|
+
options: {
|
|
47
|
+
name: { type: 'string', short: 'n', default: 'contentful' },
|
|
48
|
+
output: { type: 'string', short: 'o' },
|
|
49
|
+
required: { type: 'string', short: 'r', multiple: true },
|
|
50
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
51
|
+
},
|
|
52
|
+
allowPositionals: false,
|
|
53
|
+
});
|
|
54
|
+
if (values.help) {
|
|
55
|
+
usage();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const { name } = values;
|
|
59
|
+
const defaultRequired = ['spaceId', 'environmentId', 'accessToken', 'previewAccessToken'];
|
|
60
|
+
const required = values.required?.length
|
|
61
|
+
? values.required.flatMap((v) => v.split(','))
|
|
62
|
+
: defaultRequired;
|
|
63
|
+
await ensureLogin(name);
|
|
64
|
+
const { config } = await loadContentfulConfig(name, {
|
|
65
|
+
required,
|
|
66
|
+
prompt: required,
|
|
67
|
+
});
|
|
68
|
+
const lines = [];
|
|
69
|
+
for (const [key, value] of Object.entries(config)) {
|
|
70
|
+
if (SKIP_KEYS.has(key) || value === undefined || value === null || value === '') {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const envKey = ENV_MAP[key] ?? `CONTENTFUL_${key.replace(/([A-Z])/g, '_$1').toUpperCase()}`;
|
|
74
|
+
lines.push(`${envKey}=${String(value)}`);
|
|
75
|
+
}
|
|
76
|
+
const output = lines.join('\n') + '\n';
|
|
77
|
+
if (values.output) {
|
|
78
|
+
const filePath = resolve(values.output);
|
|
79
|
+
const newEntries = new Map(lines.map((line) => {
|
|
80
|
+
const [key, ...rest] = line.split('=');
|
|
81
|
+
return [key, rest.join('=')];
|
|
82
|
+
}));
|
|
83
|
+
const existingLines = [];
|
|
84
|
+
if (existsSync(filePath)) {
|
|
85
|
+
const existing = readFileSync(filePath, 'utf-8');
|
|
86
|
+
for (const line of existing.split('\n')) {
|
|
87
|
+
const trimmed = line.trim();
|
|
88
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
89
|
+
existingLines.push(line);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const [key] = trimmed.split('=');
|
|
93
|
+
if (newEntries.has(key)) {
|
|
94
|
+
existingLines.push(`${key}=${newEntries.get(key)}`);
|
|
95
|
+
newEntries.delete(key);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
existingLines.push(line);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (newEntries.size > 0) {
|
|
103
|
+
for (const [key, value] of newEntries) {
|
|
104
|
+
existingLines.push(`${key}=${value}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const merged = existingLines.join('\n').replace(/\n*$/, '\n');
|
|
108
|
+
writeFileSync(filePath, merged, 'utf-8');
|
|
109
|
+
console.error(`Config written to ${filePath}`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
process.stdout.write(output);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
main().catch((error) => {
|
|
116
|
+
console.error(error);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jungvonmatt/contentful-config",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"contentful-
|
|
8
|
+
"contentful-config": "./dist/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"src",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"clean": "rimraf ./dist",
|
|
22
22
|
"test": "jest",
|
|
23
23
|
"lint": "eslint --color src --fix --ext .ts",
|
|
24
|
-
"precompile": "
|
|
24
|
+
"precompile": "pnpm run clean",
|
|
25
25
|
"compile": "tsc --build",
|
|
26
26
|
"watch": "tsc --build --watch"
|
|
27
27
|
},
|
|
@@ -37,11 +37,15 @@
|
|
|
37
37
|
"homepage": "https://github.com/jungvonmatt/contentful-ssg#readme",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@jungvonmatt/config-loader": "^0.6.0",
|
|
40
|
+
"contentful-cli": "^3.9.0",
|
|
40
41
|
"contentful-management": "^11.54.4",
|
|
41
42
|
"node-homedir": "^2.0.0",
|
|
42
43
|
"package-up": "^5.0.0",
|
|
43
44
|
"pathe": "^2.0.3",
|
|
44
45
|
"type-fest": "^4.41.0"
|
|
45
46
|
},
|
|
46
|
-
"
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.11.5"
|
|
49
|
+
},
|
|
50
|
+
"gitHead": "ad36885939a63334f5900a7bbc6cef168f72f8c7"
|
|
47
51
|
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { parseArgs } from 'node:util';
|
|
6
|
+
import { loadContentfulConfig } from './index.js';
|
|
7
|
+
|
|
8
|
+
const ENV_MAP: Record<string, string> = {
|
|
9
|
+
spaceId: 'CONTENTFUL_SPACE_ID',
|
|
10
|
+
environmentId: 'CONTENTFUL_ENVIRONMENT_ID',
|
|
11
|
+
managementToken: 'CONTENTFUL_MANAGEMENT_TOKEN',
|
|
12
|
+
previewAccessToken: 'CONTENTFUL_PREVIEW_ACCESS_TOKEN',
|
|
13
|
+
accessToken: 'CONTENTFUL_DELIVERY_ACCESS_TOKEN',
|
|
14
|
+
host: 'CONTENTFUL_HOST',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const SKIP_KEYS = new Set([
|
|
18
|
+
'organizationId',
|
|
19
|
+
'activeSpaceId',
|
|
20
|
+
'activeEnvironmentId',
|
|
21
|
+
'managementToken',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
function usage() {
|
|
25
|
+
console.log(`Usage: contentful-config [options]
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
-n, --name <name> Config name (default: "contentful")
|
|
29
|
+
-o, --output <file> Write output to file instead of stdout
|
|
30
|
+
-r, --required <keys> Required config keys (comma-separated or repeated)
|
|
31
|
+
Default: spaceId,environmentId,accessToken,previewAccessToken
|
|
32
|
+
-h, --help Show this help message`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function ensureLogin(name: string) {
|
|
36
|
+
const { config } = await loadContentfulConfig(name, { prompts: false });
|
|
37
|
+
|
|
38
|
+
if (config.managementToken) {
|
|
39
|
+
return config;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.error('No management token found. Starting contentful login…');
|
|
43
|
+
execFileSync('contentful', ['login'], { stdio: 'inherit' });
|
|
44
|
+
|
|
45
|
+
// Reload config after login
|
|
46
|
+
const { config: updatedConfig } = await loadContentfulConfig(name, { prompts: false });
|
|
47
|
+
|
|
48
|
+
if (!updatedConfig.managementToken) {
|
|
49
|
+
throw new Error('Login failed. No management token found after login.');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return updatedConfig;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function main() {
|
|
56
|
+
const { values } = parseArgs({
|
|
57
|
+
options: {
|
|
58
|
+
name: { type: 'string', short: 'n', default: 'contentful' },
|
|
59
|
+
output: { type: 'string', short: 'o' },
|
|
60
|
+
required: { type: 'string', short: 'r', multiple: true },
|
|
61
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
62
|
+
},
|
|
63
|
+
allowPositionals: false,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (values.help) {
|
|
67
|
+
usage();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { name } = values;
|
|
72
|
+
|
|
73
|
+
const defaultRequired = ['spaceId', 'environmentId', 'accessToken', 'previewAccessToken'];
|
|
74
|
+
const required = values.required?.length
|
|
75
|
+
? values.required.flatMap((v) => v.split(','))
|
|
76
|
+
: defaultRequired;
|
|
77
|
+
|
|
78
|
+
await ensureLogin(name);
|
|
79
|
+
|
|
80
|
+
const { config } = await loadContentfulConfig(name, {
|
|
81
|
+
required,
|
|
82
|
+
prompt: required,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const lines: string[] = [];
|
|
86
|
+
for (const [key, value] of Object.entries(config)) {
|
|
87
|
+
if (SKIP_KEYS.has(key) || value === undefined || value === null || value === '') {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const envKey = ENV_MAP[key] ?? `CONTENTFUL_${key.replace(/([A-Z])/g, '_$1').toUpperCase()}`;
|
|
92
|
+
lines.push(`${envKey}=${String(value)}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const output = lines.join('\n') + '\n';
|
|
96
|
+
|
|
97
|
+
if (values.output) {
|
|
98
|
+
const filePath = resolve(values.output);
|
|
99
|
+
const newEntries = new Map(
|
|
100
|
+
lines.map((line) => {
|
|
101
|
+
const [key, ...rest] = line.split('=');
|
|
102
|
+
return [key, rest.join('=')];
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Read existing file and merge
|
|
107
|
+
const existingLines: string[] = [];
|
|
108
|
+
if (existsSync(filePath)) {
|
|
109
|
+
const existing = readFileSync(filePath, 'utf-8');
|
|
110
|
+
for (const line of existing.split('\n')) {
|
|
111
|
+
const trimmed = line.trim();
|
|
112
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
113
|
+
existingLines.push(line);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const [key] = trimmed.split('=');
|
|
118
|
+
if (newEntries.has(key)) {
|
|
119
|
+
// Replace with new value
|
|
120
|
+
existingLines.push(`${key}=${newEntries.get(key)}`);
|
|
121
|
+
newEntries.delete(key);
|
|
122
|
+
} else {
|
|
123
|
+
existingLines.push(line);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Append remaining new entries
|
|
129
|
+
if (newEntries.size > 0) {
|
|
130
|
+
for (const [key, value] of newEntries) {
|
|
131
|
+
existingLines.push(`${key}=${value}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Ensure trailing newline
|
|
136
|
+
const merged = existingLines.join('\n').replace(/\n*$/, '\n');
|
|
137
|
+
writeFileSync(filePath, merged, 'utf-8');
|
|
138
|
+
console.error(`Config written to ${filePath}`);
|
|
139
|
+
} else {
|
|
140
|
+
process.stdout.write(output);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
main().catch((error) => {
|
|
145
|
+
console.error(error);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
});
|