Skip to content

Commit

Permalink
feat: generate @prismicio/client Content namespace (#28)
Browse files Browse the repository at this point in the history
* feat: generate `@prismicio/client` `Content` namespace

* fix: include all top-level exported types in `Content`

* refactor: removed unused variables

* docs: update instructions for projects with multiple Prismic repositories

* docs: fix typo

* style: run Prettier
  • Loading branch information
angeloashmore authored Sep 7, 2022
1 parent 1507a06 commit 7d513e9
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 28 deletions.
1 change: 1 addition & 0 deletions src/cli/configSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const configSchema = Joi.object<Config>({

clientIntegration: Joi.object({
includeCreateClientInterface: Joi.boolean(),
includeContentNamespace: Joi.boolean(),
}),

locales: Joi.alternatives(
Expand Down
4 changes: 3 additions & 1 deletion src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ const main = async () => {
includeCreateClientInterface: hasCustomTypeModels
? config.clientIntegration?.includeCreateClientInterface ?? true
: false,
includeContentNamespace:
config.clientIntegration?.includeContentNamespace ?? true,
},
});

Expand All @@ -119,7 +121,7 @@ const main = async () => {
!hasCustomTypeModels
) {
console.info(
"[INFO]: prismic-ts-codegen was configured to automatically integrate with `@prismicio/client`, but the integration was generated because no Custom Type models were found. Automatic integration requires at least one Custom Type model.",
"[INFO]: prismic-ts-codegen was configured to automatically integrate with `@prismicio/client`, but the integration was not generated because no Custom Type models were found. Automatic integration requires at least one Custom Type model.",
);
}

Expand Down
26 changes: 23 additions & 3 deletions src/cli/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,33 @@ export type Config = {
* generated Custom Types and Slices.
*
* **Note**: If your project queries content from multiple Prismic
* repositories, set `includeCreateClientInterface` to `false` and manually
* provide the generated `AllDocumentTypes` type to `@prismicio/client`'s
* `createClient()` function instead.
* repositories, set `includeCreateClientInterface` to `true` for the
* primary repository and `false` or any other repository. The generated
* `AllDocumentTypes` type for non-primary repositories can be provided to
* `@prismicio/client`'s `creatClient()` function as its only type parameter
* to type the client.
*
* @defaultValue `true`
*/
includeCreateClientInterface?: boolean;

/**
* Determines if a `@prismicio/client` namespace named `Content` containing
* all Document and Slice types should be included in the output.
*
* If set to `true`, a `Content` namespace from `@prismicio/client` will be
* available to import to easily access types for your Prismic repository
* content.
*
* **Note**: If your project queries content from multiple Prismic
* repositories, set `includeContentNamespace` to `true` for the primary
* repository and `false` or any other repository. Types for non-primary
* repositories should be imported directly from the generated file rather
* than via the `Content` namespace.
*
* @defaultValue `true`
*/
includeContentNamespace?: boolean;
};

/**
Expand Down
79 changes: 55 additions & 24 deletions src/generateTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type GenerateTypesConfig = {
fieldConfigs?: FieldConfigs;
clientIntegration?: {
includeCreateClientInterface?: boolean;
includeContentNamespace?: boolean;
};
};

Expand All @@ -34,7 +35,7 @@ export const generateTypes = (config: GenerateTypesConfig = {}) => {

sourceFile.addStatements(BLANK_LINE_IDENTIFIER);

sourceFile.addTypeAlias({
const simplifyTypeAlias = sourceFile.addTypeAlias({
name: "Simplify",
typeParameters: [
{
Expand Down Expand Up @@ -77,7 +78,10 @@ export const generateTypes = (config: GenerateTypesConfig = {}) => {
}
}

if (config.clientIntegration?.includeCreateClientInterface) {
if (
config.clientIntegration?.includeCreateClientInterface ||
config.clientIntegration?.includeContentNamespace
) {
sourceFile.addImportDeclaration({
moduleSpecifier: "@prismicio/client",
namespaceImport: "prismic",
Expand All @@ -90,28 +94,55 @@ export const generateTypes = (config: GenerateTypesConfig = {}) => {
declarationKind: ModuleDeclarationKind.Module,
});

clientModuleDeclaration.addInterface({
name: "CreateClient",
callSignatures: [
{
parameters: [
{
name: "repositoryNameOrEndpoint",
type: "string",
},
{
name: "options",
type: "prismic.ClientConfig",
hasQuestionToken: true,
},
],
returnType:
(config.customTypeModels?.length || 0) > 0
? "prismic.Client<AllDocumentTypes>"
: "prismic.Client",
},
],
});
if (config.clientIntegration.includeCreateClientInterface) {
clientModuleDeclaration.addInterface({
name: "CreateClient",
callSignatures: [
{
parameters: [
{
name: "repositoryNameOrEndpoint",
type: "string",
},
{
name: "options",
type: "prismic.ClientConfig",
hasQuestionToken: true,
},
],
returnType:
(config.customTypeModels?.length || 0) > 0
? "prismic.Client<AllDocumentTypes>"
: "prismic.Client",
},
],
});
}

if (config.clientIntegration.includeContentNamespace) {
const contentNamespaceDeclaration = clientModuleDeclaration.addModule({
name: "Content",
declarationKind: ModuleDeclarationKind.Namespace,
});

const exportSymbols = sourceFile
.getExportSymbols()
.filter((exportSymbol) => {
// The Simplify utility type should not
// be exported, but it is included in
// `getExportSymbols()`'s result.
return exportSymbol.getName() !== simplifyTypeAlias.getName();
});

contentNamespaceDeclaration.addExportDeclaration({
isTypeOnly: true,
namedExports: exportSymbols.map((exportSymbol) => {
return {
name: exportSymbol.getName(),
};
}),
});
}
}

return getSourceFileText(sourceFile);
Expand Down
60 changes: 60 additions & 0 deletions test/generateTypes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,63 @@ test("includes untyped `@prismicio/client` in CreateClient interface if no Custo
"prismic.Client",
);
});

test("includes @prismicio/client Content namespace containing all document and Slice types if configured", (t) => {
const res = lib.generateTypes({
clientIntegration: {
includeContentNamespace: true,
},
customTypeModels: [prismicM.model.customType({ seed: t.title, id: "foo" })],
sharedSliceModels: [
prismicM.model.sharedSlice({
seed: t.title,
id: "bar",
variations: [
prismicM.model.sharedSliceVariation({ seed: t.title, id: "baz" }),
],
}),
],
});

const file = parseSourceFile(res);
const contentNamespace = file
.getModuleOrThrow('"@prismicio/client"')
.getModuleOrThrow("Content");

const exportSymbolNames = contentNamespace
.getExportSymbols()
.map((exportSymbol) => {
return exportSymbol.getName();
});

// Documents
t.true(exportSymbolNames.includes("FooDocument"));
t.true(exportSymbolNames.includes("FooDocumentData"));
t.true(exportSymbolNames.includes("AllDocumentTypes"));

// Slices
t.true(exportSymbolNames.includes("BarSliceBaz"));
t.true(exportSymbolNames.includes("BarSliceVariation"));
t.true(exportSymbolNames.includes("BarSlice"));
});

test("includes empty @prismicio/client Content namespace if configured and no models are provided", (t) => {
const res = lib.generateTypes({
clientIntegration: {
includeContentNamespace: true,
},
});

const file = parseSourceFile(res);
const contentNamespace = file
.getModuleOrThrow('"@prismicio/client"')
.getModuleOrThrow("Content");

const exportSymbolNames = contentNamespace
.getExportSymbols()
.map((exportSymbol) => {
return exportSymbol.getName();
});

t.is(exportSymbolNames.length, 0);
});

0 comments on commit 7d513e9

Please sign in to comment.