npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@altearius/cs-gen

v0.1.8

Published

Generate code based on a Contentstack stack

Downloads

53

Readme

Contentstack Code Generation

This utility examines a Contentstack account and generates code based on the content types that it finds:

  1. JSON Schema
  2. TypeScript interfaces
  3. Validation code

The JSON schema is broadly applicable and should be usable in any environment with tooling to support it, for instance, it could be used with Newtonsoft.JSON in a dotnet tool chain.

The TypeScript interfaces provide an alternative to those generated by the Contentstack CLI Tsgen plugin. Unlike the Tsgen plugin, cs-gen does not require the Contentstack CLI to be installed, and cs-gen can more easily be configured with project-specific defaults using a .env file.

The validation code is intended for run-time validation of entities received through the Contentstack REST endpoint. The validation code will ensure that the entities you receive match the interfaces that your code was compiled against.

Installation

cs-gen is designed to be installed as a dev dependency in your project:

npm install --save-dev @altearius/cs-gen

or

yarn add --dev @altearius/cs-gen

Optional: If you will be using the generated validation code, your project must also take a run-time dependency to Ajv.

yarn add ajv

Usage

cs-gen can generate JSON Schema documents, TypeScript definitions, and standalone validation code.

A CLI is provided:

cs-gen generate [...options]

By default, it will output nothing; you must tell it what sort of output you want. This is done with one of the following options:

  • --json-schema-path
  • --response-path
  • --typescript-path
  • --validation-path

Options

All options may also be provided as environment variables. cs-gen honors any .env file it finds in the working directory.

| Option | Environment Variable | Description | | ------------------ | ----------------------- | ---------------------------- | | --api-key | Cs_gen_api_key | Contentstack API key | | --base-url | Cs_gen_base_url | Contentstack API base URL | | --branch | Cs_gen_branch | Contentstack branch | | --json-schema-path | Cs_gen_json_schema_path | Output a JSON schema | | --management-token | Cs_gen_management_token | API management token | | --prefix | Cs_gen_prefix | Prefix for TS interfaces | | --response-path | Cs_gen_response_path | Output raw API responses | | --typescript-path | Cs_gen_typescript_path | Output TypeScript interfaces | | --validation-path | Cs_gen_validation_path | Output validation code |

Example

All examples assume that the .env file has been configured with values for Cs_gen_api_key, Cs_gen_base_url, and Cs_gen_management_token that are appropriate for the given project.

Output TypeScript interface definitions only:

yarn cs-gen generate --typescript-path ./models/Contentstack.d.ts

Output both a JSON Schema and JavaScript validation code:

yarn cs-gen generate \
  --json-schema-path ./Contentstack.schema.json \
  --validation-path ./src/validation

Limitations

I am not sure how to handle the json data type provided by the Contentstack JSON-RTE editor.

TSgen currently types this field as any.

I can't find documentation for it. I've typed it as { [k: string]: unknown | undefined }, because at least it appears to always resemble an object.

One of the properties of that object is named _version that presently has a value of 9. So I guess there are other versions?

I could try to reverse-engineer this. It would look something like:

const name = this.getOrCreateJsonDefinition();
const ref = S.ref(`#/definitions/${name}`);
const multiple = handleMultiple(field, ref);
return applyBaseFieldsFrom(field).to(multiple);

Where getOrCreateJsonDefinition would create a definition like this:

S.object()
  .required(['attrs', 'children', 'uid'])
  .prop('uid', S.string())
  .prop('attrs', S.object()) // <-- unclear what goes here
  .prop(
    'children',
    S.array().items(
      S.oneOf([
        S.ref(`#/definitions/${name}`), // <-- self-referential
        S.object().required(['text']).prop('text', S.string())
      ])
    )
  );

But the _version number and lack of documentation scares me. Maybe if I could find the docs for this and a roadmap for future version iterations, it would make more sense. As it is, it seems complex and in flux, and I'm worried that this target is moving too quickly to hit. Perhaps it is better to leave this as "any" until I can find more information.

Or maybe, is it always going to be an object? Is that a safe bet? I could imagine it being a string in some cases, if there's some way to have the server render HTML that I just don't know about yet.

Comparison with TSGen

Tsgen is a plugin for the Contentstack CLI that can also generate TypeScript interfaces. Here is a summary of the differences you may encounter.

| | cs-gen | tsgen | | ------------------------------------ | ----------- | ----------- | | Generates TypeScript Interfaces | ✓ | ✓ | | Includes JSDoc comments | ✓ | ✓ | | Optional prefix on interface names | ✓ | ✓ | | Generates JSON Schema | ✓ | ✗ | | Generates standalone validation code | ✓ | ✗ | | Schemas support lazy-loading | ✓ | ✗ | | Honors prettier configuration | ✓ | ✗ | | .env file configuration | ✓ | ✗ | | Requires the Contentstack CLI | ✗ | ✓ | | Default installation method | per-project | per-machine |

There are additional some differences in the actual interfaces generated:

UID Field Handling

The REST API, in my experience, consistently returns a uid for entries. I have therefore included this field in the schemas produced by cs-gen. It is absent in the schemas produced by tsgen.

Min/Max Instance Handling

On fields that are declared as multiple, it is possible to set minimum and maximum limitations for the number of items. tsgen honors the maximum limit, but assumes that every item will always contain the maximum limit. cs-gen provides a type that uses tuples to indicate all possible items:

export type IZen = IContentstackEntry & {
  /**
   * Single Line Textbox with multi
   *
   * @maxItems 5
   */
  single_line_textbox_with_multi?:
    | []
    | [string]
    | [string, string]
    | [string, string, string]
    | [string, string, string, string]
    | [string, string, string, string, string];
};
export interface IZen {
  /** Single Line Textbox with multi */
  single_line_textbox_with_multi?: [string, string, string, string, string];
}

Reference Field Handling

If items are loaded from the Contenstack REST API, reference fields are expanded only if the expansion was requested in the initial request. If expansion was not requested, the resulting object contains only two properties: uid and _content_type_uid. The schema generated by cs-gen reflects this possibility. Compare:

export interface IMenuItem {
  /**
   * Internal Link
   */
  internal_link?: (
    | {
        uid: string;
        _content_type_uid: 'news_article' | 'homepage';
      }
    | INewsArticle
    | IHomepage
    | IFaq
    | IAuthor
  )[];
}
export interface IMenuItem {
  /** Internal Link */
  internal_link?: (INewsArticle | IHomepage | IFaq | IAuthor)[];
}

Development Guide

This project uses yarn as a package manager.

The following development scripts are provided:

  • build: Compile the TypeScript for distribution.
  • clean: Remove all generated files.
  • cs-gen: Execute the local version of cs-gen.
  • lint: Execute eslint for static code analysis.
  • pretty: Execute prettier for stylistic concerns.
  • test: Execute jest for unit tests.

I'm on linux and haven't tested any of development scripts in Windows or anything other than Ubuntu. I know some of the build scripts require bash and I don't expect that to work on a typical Windows machine. They could be re-written in Node, I expect. Someday.

TODO

It should be possible to improve performance by keeping a "last built state" for each content type and only re-generating the ones that have changed since the last run.


It should be possible to generate some TypeScript wrappers around each bit of standalone validation code. Looks like this:

import type { ValidateFunction } from 'ajv';
import type { IHomepage } from '../models.d.ts';
import validation from './homepage.js';
export default validation as ValidateFunction<IHomepage>;

In my project, I have a structure like this:

const enum ContentType {
  Settings = 'settings',
  Homepage = 'homepage'
}

This gives me intellisense support for the different content types that I have in my stack. Right now, I have to maintain this manually. I could be generating this instead.


It would be nice to be able to white-list or black-list specific content types for the generation process. Might not need every possible content type.


Right now, all TypeScript types get dumped into one large file, which I guess is fine, but it would be neater if we could separate them out.


Maintain a stable sort-order on as many generated files as possible. It will help with source-control churn.


Provide ability to specify depth for array tuples before it gives up and just makes a plain array.