@fourtwelvelabs/fetch-contentful 0.4.0 → 1.0.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/CHANGELOG.md +177 -0
- package/README.md +192 -57
- package/dist/cli/index.mjs +82 -77
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +309 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +254 -26
- package/dist/index.d.ts +254 -26
- package/dist/index.mjs +305 -140
- package/dist/index.mjs.map +1 -1
- package/docs/tada.md +57 -32
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
3
3
|
import { resolve, dirname, relative, isAbsolute, sep, join } from 'path';
|
|
4
4
|
import { getIntrospectionQuery, printSchema, buildClientSchema } from 'graphql';
|
|
5
5
|
|
|
@@ -13,7 +13,6 @@ function readEnvSettings() {
|
|
|
13
13
|
space: void 0,
|
|
14
14
|
environment: void 0,
|
|
15
15
|
deliveryToken: void 0,
|
|
16
|
-
token: void 0,
|
|
17
16
|
previewToken: void 0
|
|
18
17
|
};
|
|
19
18
|
}
|
|
@@ -28,7 +27,6 @@ function readEnvSettings() {
|
|
|
28
27
|
process.env.CONTENTFUL_ENVIRONMENT || process.env.NEXT_PUBLIC_CONTENTFUL_ENVIRONMENT
|
|
29
28
|
),
|
|
30
29
|
deliveryToken,
|
|
31
|
-
token: deliveryToken,
|
|
32
30
|
// No `NEXT_PUBLIC_` fallback: see the note at the top of this file.
|
|
33
31
|
previewToken: orUndefined(process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN)
|
|
34
32
|
};
|
|
@@ -53,7 +51,8 @@ var VALUE_FLAGS = [
|
|
|
53
51
|
"tada-output",
|
|
54
52
|
"graphql-file",
|
|
55
53
|
"tsconfig",
|
|
56
|
-
"cwd"
|
|
54
|
+
"cwd",
|
|
55
|
+
"env-file"
|
|
57
56
|
];
|
|
58
57
|
function isBooleanFlag(name) {
|
|
59
58
|
return BOOLEAN_FLAGS.includes(name);
|
|
@@ -158,6 +157,53 @@ function formatBytes(bytes) {
|
|
|
158
157
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} kB`;
|
|
159
158
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
160
159
|
}
|
|
160
|
+
var ENV_FILES = [".env.local", ".env"];
|
|
161
|
+
var ASSIGNMENT = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/;
|
|
162
|
+
function parseEnvFile(source) {
|
|
163
|
+
const values = {};
|
|
164
|
+
for (const rawLine of source.split(/\r?\n/)) {
|
|
165
|
+
const line = rawLine.trim();
|
|
166
|
+
if (line === "" || line.startsWith("#")) continue;
|
|
167
|
+
const match = ASSIGNMENT.exec(line);
|
|
168
|
+
if (!match) continue;
|
|
169
|
+
const key = match[1];
|
|
170
|
+
let value = match[2].trim();
|
|
171
|
+
const quote = value[0];
|
|
172
|
+
if ((quote === '"' || quote === "'") && value.length > 1 && value.endsWith(quote)) {
|
|
173
|
+
value = value.slice(1, -1);
|
|
174
|
+
if (quote === '"') value = value.replace(/\\n/g, "\n");
|
|
175
|
+
} else {
|
|
176
|
+
const comment = value.search(/\s#/);
|
|
177
|
+
if (comment !== -1) value = value.slice(0, comment).trimEnd();
|
|
178
|
+
}
|
|
179
|
+
values[key] = value;
|
|
180
|
+
}
|
|
181
|
+
return values;
|
|
182
|
+
}
|
|
183
|
+
function loadEnvFiles(directory, explicit) {
|
|
184
|
+
const load = { directory, files: [], applied: [] };
|
|
185
|
+
if (explicit !== void 0 && !existsSync(resolve(directory, explicit))) {
|
|
186
|
+
throw new CliError(`No env file at ${explicit}.`);
|
|
187
|
+
}
|
|
188
|
+
for (const candidate of explicit === void 0 ? ENV_FILES : [explicit]) {
|
|
189
|
+
const path = resolve(directory, candidate);
|
|
190
|
+
if (!existsSync(path)) continue;
|
|
191
|
+
load.files.push(candidate);
|
|
192
|
+
for (const [key, value] of Object.entries(parseEnvFile(readFileSync(path, "utf8")))) {
|
|
193
|
+
if (!process.env[key]) {
|
|
194
|
+
process.env[key] = value;
|
|
195
|
+
load.applied.push(key);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return load;
|
|
200
|
+
}
|
|
201
|
+
function describeEnvFiles(load) {
|
|
202
|
+
if (load.files.length === 0) {
|
|
203
|
+
return `No ${ENV_FILES.join(" or ")} file was found in ${load.directory}, so only the shell environment was read. Point at one with --env-file if it lives elsewhere.`;
|
|
204
|
+
}
|
|
205
|
+
return `Read ${load.files.join(" and ")}; anything already set in the environment takes precedence over them.`;
|
|
206
|
+
}
|
|
161
207
|
var PACKAGE_NAME = "@fourtwelvelabs/fetch-contentful";
|
|
162
208
|
function relativeSpecifier(fromFile, toFile) {
|
|
163
209
|
const path = relative(dirname(fromFile), toFile).split(sep).join("/");
|
|
@@ -212,26 +258,19 @@ async function fetchSchemaSdl(options) {
|
|
|
212
258
|
});
|
|
213
259
|
} catch (cause) {
|
|
214
260
|
throw new CliError(
|
|
215
|
-
`Could not reach Contentful at ${url}: ${redact(
|
|
216
|
-
String(cause),
|
|
217
|
-
options.token
|
|
218
|
-
)}`
|
|
261
|
+
`Could not reach Contentful at ${url}: ${redact(String(cause), options.token)}`
|
|
219
262
|
);
|
|
220
263
|
}
|
|
221
264
|
if (response.status === 401 || response.status === 403) {
|
|
222
265
|
throw new CliError(
|
|
223
|
-
`Contentful rejected the access token (HTTP ${String(
|
|
224
|
-
response.status
|
|
225
|
-
)}).
|
|
266
|
+
`Contentful rejected the access token (HTTP ${String(response.status)}).
|
|
226
267
|
Check that the token is a Content Delivery API token for space "${options.space}", and that it has access to environment "${options.environment}".`
|
|
227
268
|
);
|
|
228
269
|
}
|
|
229
270
|
if (!response.ok) {
|
|
230
271
|
const body = bodySnippet(await response.text().catch(() => ""), options.token);
|
|
231
|
-
throw new CliError(
|
|
232
|
-
|
|
233
|
-
${body}`
|
|
234
|
-
);
|
|
272
|
+
throw new CliError(`Contentful responded with HTTP ${String(response.status)}.
|
|
273
|
+
${body}`);
|
|
235
274
|
}
|
|
236
275
|
let payload;
|
|
237
276
|
try {
|
|
@@ -242,10 +281,7 @@ async function fetchSchemaSdl(options) {
|
|
|
242
281
|
if (payload.errors && payload.errors.length > 0) {
|
|
243
282
|
const messages = payload.errors.map((error) => error.message ?? "unknown error").join("; ");
|
|
244
283
|
throw new CliError(
|
|
245
|
-
`Contentful returned GraphQL errors during introspection: ${redact(
|
|
246
|
-
messages,
|
|
247
|
-
options.token
|
|
248
|
-
)}`
|
|
284
|
+
`Contentful returned GraphQL errors during introspection: ${redact(messages, options.token)}`
|
|
249
285
|
);
|
|
250
286
|
}
|
|
251
287
|
if (!payload.data) {
|
|
@@ -258,9 +294,7 @@ async function fetchSchemaSdl(options) {
|
|
|
258
294
|
`;
|
|
259
295
|
} catch (cause) {
|
|
260
296
|
throw new CliError(
|
|
261
|
-
`Could not build a schema from Contentful's introspection response: ${String(
|
|
262
|
-
cause
|
|
263
|
-
)}`
|
|
297
|
+
`Could not build a schema from Contentful's introspection response: ${String(cause)}`
|
|
264
298
|
);
|
|
265
299
|
}
|
|
266
300
|
}
|
|
@@ -293,9 +327,7 @@ function installCommand(manager, packages) {
|
|
|
293
327
|
function missingDependencies(cwd, packages) {
|
|
294
328
|
let manifest;
|
|
295
329
|
try {
|
|
296
|
-
manifest = JSON.parse(
|
|
297
|
-
readFileSync(join(cwd, "package.json"), "utf8")
|
|
298
|
-
);
|
|
330
|
+
manifest = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
|
|
299
331
|
} catch {
|
|
300
332
|
return [...packages];
|
|
301
333
|
}
|
|
@@ -356,9 +388,7 @@ var Scanner = class {
|
|
|
356
388
|
}
|
|
357
389
|
expect(character) {
|
|
358
390
|
if (this.peek() !== character) {
|
|
359
|
-
throw new JsoncError(
|
|
360
|
-
`Expected "${character}" at offset ${String(this.index)}.`
|
|
361
|
-
);
|
|
391
|
+
throw new JsoncError(`Expected "${character}" at offset ${String(this.index)}.`);
|
|
362
392
|
}
|
|
363
393
|
this.index += 1;
|
|
364
394
|
}
|
|
@@ -366,9 +396,7 @@ var Scanner = class {
|
|
|
366
396
|
const node = this.parseValue();
|
|
367
397
|
this.skipTrivia();
|
|
368
398
|
if (this.index !== this.source.length) {
|
|
369
|
-
throw new JsoncError(
|
|
370
|
-
`Unexpected trailing content at offset ${String(this.index)}.`
|
|
371
|
-
);
|
|
399
|
+
throw new JsoncError(`Unexpected trailing content at offset ${String(this.index)}.`);
|
|
372
400
|
}
|
|
373
401
|
return node;
|
|
374
402
|
}
|
|
@@ -488,9 +516,7 @@ function renderPluginEntry(indent, unit, config) {
|
|
|
488
516
|
"{",
|
|
489
517
|
`${indent}${unit}"name": ${JSON.stringify(GRAPHQLSP_PLUGIN)},`,
|
|
490
518
|
`${indent}${unit}"schema": ${JSON.stringify(config.schema)},`,
|
|
491
|
-
`${indent}${unit}"tadaOutputLocation": ${JSON.stringify(
|
|
492
|
-
config.tadaOutputLocation
|
|
493
|
-
)}`,
|
|
519
|
+
`${indent}${unit}"tadaOutputLocation": ${JSON.stringify(config.tadaOutputLocation)}`,
|
|
494
520
|
`${indent}}`
|
|
495
521
|
].join("\n");
|
|
496
522
|
}
|
|
@@ -518,11 +544,7 @@ ${closeIndent}`
|
|
|
518
544
|
function renderPluginsArray(indent, unit, config) {
|
|
519
545
|
const entryIndent = `${indent}${unit}`;
|
|
520
546
|
return `[
|
|
521
|
-
${entryIndent}${renderPluginEntry(
|
|
522
|
-
entryIndent,
|
|
523
|
-
unit,
|
|
524
|
-
config
|
|
525
|
-
)}
|
|
547
|
+
${entryIndent}${renderPluginEntry(entryIndent, unit, config)}
|
|
526
548
|
${indent}]`;
|
|
527
549
|
}
|
|
528
550
|
function patchTsconfig(source, config) {
|
|
@@ -592,9 +614,7 @@ ${indent}}`,
|
|
|
592
614
|
text: JSON.stringify(value)
|
|
593
615
|
});
|
|
594
616
|
} else {
|
|
595
|
-
edits.push(
|
|
596
|
-
insertMember(source, existing, key, () => JSON.stringify(value), unit)
|
|
597
|
-
);
|
|
617
|
+
edits.push(insertMember(source, existing, key, () => JSON.stringify(value), unit));
|
|
598
618
|
}
|
|
599
619
|
}
|
|
600
620
|
const patched = applyEdits(source, edits);
|
|
@@ -624,11 +644,7 @@ ${indent}${renderPluginEntry(indent, unit, config)}`
|
|
|
624
644
|
start: plugins.value.start + 1,
|
|
625
645
|
end: plugins.value.end - 1,
|
|
626
646
|
text: `
|
|
627
|
-
${entryIndent}${renderPluginEntry(
|
|
628
|
-
entryIndent,
|
|
629
|
-
unit,
|
|
630
|
-
config
|
|
631
|
-
)}
|
|
647
|
+
${entryIndent}${renderPluginEntry(entryIndent, unit, config)}
|
|
632
648
|
${closeIndent}`
|
|
633
649
|
}
|
|
634
650
|
])
|
|
@@ -668,11 +684,14 @@ Other:
|
|
|
668
684
|
--dry-run Show what would change; write nothing
|
|
669
685
|
--force Overwrite an existing graphql file (tada-init)
|
|
670
686
|
--cwd <path> Run against another directory
|
|
687
|
+
--env-file <path> Read this file instead of .env.local / .env
|
|
671
688
|
-h, --help Show this help
|
|
672
689
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
690
|
+
Configuration is read from .env.local, then .env, then the shell \u2014 anything
|
|
691
|
+
already exported wins over a file, and flags win over everything. The
|
|
692
|
+
NEXT_PUBLIC_-prefixed names are accepted as fallbacks, exactly as the
|
|
693
|
+
library reads them at runtime.`;
|
|
694
|
+
function resolveConfig(args, envFiles) {
|
|
676
695
|
const env = readEnvSettings();
|
|
677
696
|
const space = args.values.space ?? env.space;
|
|
678
697
|
const token = args.values.token ?? env.deliveryToken;
|
|
@@ -693,7 +712,8 @@ function resolveConfig(args) {
|
|
|
693
712
|
`Missing required Contentful configuration:
|
|
694
713
|
- ${missing.join(
|
|
695
714
|
"\n - "
|
|
696
|
-
)}
|
|
715
|
+
)}
|
|
716
|
+
${describeEnvFiles(envFiles)}`
|
|
697
717
|
);
|
|
698
718
|
}
|
|
699
719
|
return { space, environment, token };
|
|
@@ -760,9 +780,7 @@ function planTsconfig(paths) {
|
|
|
760
780
|
}
|
|
761
781
|
function planGraphqlModule(paths, force) {
|
|
762
782
|
const before = readIfExists(paths.graphqlFile);
|
|
763
|
-
const after = renderGraphqlModule(
|
|
764
|
-
relativeSpecifier(paths.graphqlFile, paths.tadaOutput)
|
|
765
|
-
);
|
|
783
|
+
const after = renderGraphqlModule(relativeSpecifier(paths.graphqlFile, paths.tadaOutput));
|
|
766
784
|
if (before !== void 0 && !force) {
|
|
767
785
|
return {
|
|
768
786
|
kind: "skip",
|
|
@@ -829,9 +847,7 @@ function apply(action) {
|
|
|
829
847
|
function reportManual(action, paths, io) {
|
|
830
848
|
if (action.kind !== "manual") return;
|
|
831
849
|
io.stdout("");
|
|
832
|
-
io.stdout(
|
|
833
|
-
`${display(paths.root, action.path)} was left untouched because ${action.reason}.`
|
|
834
|
-
);
|
|
850
|
+
io.stdout(`${display(paths.root, action.path)} was left untouched because ${action.reason}.`);
|
|
835
851
|
io.stdout('Add this entry to "compilerOptions.plugins" by hand:');
|
|
836
852
|
io.stdout("");
|
|
837
853
|
for (const line of action.block.split("\n")) io.stdout(` ${line}`);
|
|
@@ -844,8 +860,9 @@ function reportPeers(paths, io) {
|
|
|
844
860
|
io.stdout(` ${installCommand(detectPackageManager(paths.root), missing)}`);
|
|
845
861
|
}
|
|
846
862
|
async function tadaInit(args, io) {
|
|
847
|
-
const config = resolveConfig(args);
|
|
848
863
|
const paths = resolvePaths(args, io);
|
|
864
|
+
const envFiles = loadEnvFiles(paths.root, args.values["env-file"]);
|
|
865
|
+
const config = resolveConfig(args, envFiles);
|
|
849
866
|
const dryRun = args.flags.has("dry-run");
|
|
850
867
|
const tsconfigAction = planTsconfig(paths);
|
|
851
868
|
const sdl = await fetchSchemaSdl({ ...config, fetch: io.fetch });
|
|
@@ -868,32 +885,23 @@ async function tadaInit(args, io) {
|
|
|
868
885
|
io.stdout("Next steps:");
|
|
869
886
|
io.stdout(` 1. Restart the TypeScript server so ${GRAPHQLSP_PLUGIN} loads.`);
|
|
870
887
|
io.stdout(
|
|
871
|
-
` 2. Write a query with the \`graphql\` helper from ${display(
|
|
872
|
-
paths.root,
|
|
873
|
-
paths.graphqlFile
|
|
874
|
-
)},`
|
|
875
|
-
);
|
|
876
|
-
io.stdout(
|
|
877
|
-
` then pass it to fetchContentful from ${PACKAGE_NAME}.`
|
|
888
|
+
` 2. Write a query with the \`graphql\` helper from ${display(paths.root, paths.graphqlFile)},`
|
|
878
889
|
);
|
|
890
|
+
io.stdout(` then pass it to fetchContentful from ${PACKAGE_NAME}.`);
|
|
879
891
|
io.stdout(" Result and variable types are inferred from the document.");
|
|
880
|
-
io.stdout(
|
|
881
|
-
" 3. After a content-model change, run `fetch-contentful tada-refresh`."
|
|
882
|
-
);
|
|
892
|
+
io.stdout(" 3. After a content-model change, run `fetch-contentful tada-refresh`.");
|
|
883
893
|
return 0;
|
|
884
894
|
}
|
|
885
895
|
async function tadaRefresh(args, io) {
|
|
886
|
-
const config = resolveConfig(args);
|
|
887
896
|
const paths = resolvePaths(args, io);
|
|
897
|
+
const envFiles = loadEnvFiles(paths.root, args.values["env-file"]);
|
|
898
|
+
const config = resolveConfig(args, envFiles);
|
|
888
899
|
const dryRun = args.flags.has("dry-run");
|
|
889
900
|
const sdl = await fetchSchemaSdl({ ...config, fetch: io.fetch });
|
|
890
901
|
const action = planSchema(sdl, paths);
|
|
891
902
|
if (action.kind === "unchanged") {
|
|
892
903
|
io.stdout(
|
|
893
|
-
`Schema is already up to date \u2014 ${display(
|
|
894
|
-
paths.root,
|
|
895
|
-
paths.schema
|
|
896
|
-
)} left untouched.`
|
|
904
|
+
`Schema is already up to date \u2014 ${display(paths.root, paths.schema)} left untouched.`
|
|
897
905
|
);
|
|
898
906
|
return 0;
|
|
899
907
|
}
|
|
@@ -918,10 +926,7 @@ async function dispatch(argv, io) {
|
|
|
918
926
|
}
|
|
919
927
|
function knownTokens(argv) {
|
|
920
928
|
const env = readEnvSettings();
|
|
921
|
-
const tokens = [
|
|
922
|
-
env.deliveryToken,
|
|
923
|
-
env.previewToken
|
|
924
|
-
];
|
|
929
|
+
const tokens = [env.deliveryToken, env.previewToken];
|
|
925
930
|
for (const [index, argument] of argv.entries()) {
|
|
926
931
|
if (argument.startsWith("--token=")) tokens.push(argument.slice(8));
|
|
927
932
|
else if (argument === "--token") tokens.push(argv[index + 1]);
|