GithubHelp home page GithubHelp logo

dohomi / storyblok-generate-ts Goto Github PK

View Code? Open in Web Editor NEW
102.0 4.0 32.0 3.87 MB

Generates TypeScript interface types based on Storyblok component file

License: MIT License

JavaScript 10.26% TypeScript 89.74%
storyblok typescript

storyblok-generate-ts's Introduction

storyblok-generate-ts

This plugin uses json-schema-to-typescript to generate TS types based on Storyblok components. You can install and run it as a CLI script

1. Prepare the use of this script

a) Fetch your schema with Storyblok CLI

# Make sure storyblok is installed (npm i storyblok -g)
$ storyblok pull-components --space=[SPACE_ID]

Example:

$ storyblok pull-components --space=123456

b) Install this library as devDependency

$ npm install -D storyblok-generate-ts

c) Create node script inside of your package.json scripts

"generate-sb-types": "storyblok-generate-ts source=./components.[SPACE_ID].json target=./component-types-sb"

Example:

"generate-sb-types": "storyblok-generate-ts source=./components.123456.json target=./component-types-sb"

You can also provide multiple files as source in case you used the --separate-files flag with the pull-components command:

"generate-sb-types": "storyblok-generate-ts source=string hero-[SPACE_ID].json,footer-[SPACE_ID].json target=./component-types-sb"

Example:

"generate-sb-types": "storyblok-generate-ts source=string hero-123456.json,footer-123456.json target=./component-types-sb"

Properties of CLI

- source *required - path of the components.[SPACE_ID].json or multiple files as comma-separated string hero-[SPACE_ID].json,footer-[SPACE_ID].json
- target *optional default: storyblok-component-types.d.ts
- titlePrefix *optional default: '_storyblok' 
- titleSuffix *optional
- resolveLinks *optional 
- compilerOptions.[property] *optional
- customTypeParser *optional - path to a custom parser NodeJS file

Compiler options:

property type default description
unknownAny boolean false Type any will be replaced with unknown where possible if true
bannerComment string '' Disclaimer comment prepended to the top of each generated file
unreachableDefinitions boolean true Generates code for $defs that aren't referenced by the schema.
additionalProperties boolean true Adding [k: string]: any ([k: string]: unknown if unknownAny:true) to all object types (to nested ones too) when set to true
enableConstEnums boolean true Prepend enums with const?
format boolean true Format code? Set this to false to improve performance.
ignoreMinAndMaxItems boolean false Ignore maxItems and minItems for array types, preventing tuples being generated.
maxItems number 20 Maximum number of unioned tuples to emit when representing bounded-size array types, before falling back to emitting unbounded arrays. Increase this to improve precision of emitted types, decrease it to improve performance, or set it to -1 to ignore maxItems.
strictIndexSignatures boolean false Append all index signatures with | undefined so that they are strictly typed.
style object { bracketSpacing: false, printWidth: 120, semi: true, singleQuote: false, tabWidth: 2, trailingComma: 'none', useTabs: false } A Prettier configuration
cwd string process.cwd() Root directory for resolving $refs
declareExternallyReferenced boolean true Declare external schemas referenced via $ref?
$refOptions object {} $RefParser Options, used when resolving $refs

Alternative to CLI script: create a NodeJS javascript file

Example:

const storyblokToTypescript = require('src/index')

storyblokToTypescript({
    // required
    componentsJson: require('./components.xxxxxx.json'), // pull components with storyblok
    // required
    path: __dirname + '/src/typings/generated/components-schema.ts', // make sure path exists
    // optional type prefix (default: none)
    titlePrefix: '',
    // optional type name suffix (default: [Name]_Storyblok)
    titleSuffix: '_storyblok',
    // optional resolveLinks (default: story)
    resolveLinks: "url",
    // optional compilerOptions which get passed through to json-schema-to-typescript
    compilerOptions: {
        unknownAny: false,
        bannerComment: '',
        unreachableDefinitions: true
    }
    // optional function for custom types (key, obj) => {}
    // customTypeParser: exampleCustomParser
})

3. Run your script

$ node ./YOUR_SCRIPT_NAME.js

Example Custom Parser

function exampleCustomParser (key, obj) {
  switch (obj.field_type) {
    case 'bootstrap-utility-class-selector':
      return {
        [key]: {
          type: 'object',
          properties: {
            values: {
              type: 'array',
              items: {
                type: 'string'
              }
            }
          }
        }
      }
    case 'vue-color-picker':
      return {
        [key]: {
          type: 'object',
          properties: {
            rgba: {
              type: 'string'
            }
          }
        }
      }
    default:
      return {}
  }
}

Inspect your generated file

You can inspect all interfaces in the generated output of your file. From now on all your components blocks will be type safe (even if you use the custom blocks of Storyblok).

Generic Types

Following types are available for convenient reasons (if they are used in your component schema):

AssetStoryblok
MultiassetStoryblok
MultilinkStoryblok
TableStoryblok

Resolve relations

If you use resolve_relations you can simply extend your required schema to support fully typed relations.

Example: resolve_relations: "page.author,page.categories,page.tags"

type PageWithRelations = PageStoryblok & {
  author?: StoryData<AuthorStoryblok>
  categories?: StoryData<CategoryStoryblok>[]
  tags?: StoryData<TagStoryblok>[]
}

CHANGELOG

  • 1.0.0 initial version
  • 1.0.1 Added support for datetime and improved multilink
  • 1.1.0 CLI support, for use check 1 c)
  • 1.1.1 README update
  • 1.2.0 Add types for multiasset
  • 1.2.1 Improve type for asset and multiasset
  • 1.3.0 Default custom map. Support seo-metatags
  • 1.4.0 Allow empty string in options type (thanks to @jbeast)
  • 1.5.0 De-Duplicate asset, multiasset and multilink (thanks to @markus-gx)
  • 1.6.0 Add table schema (thanks to @markus-gx)
  • 1.6.1 Add asset focus type (thanks to @markus-gx)
  • 1.7.0 Add compilerOptions option (thanks to @SassNinja)
  • 1.8.0 Use never[] on groups that have no members (thanks to @arduano)
  • 1.9.0 Convert all files to TypeScript and add TypeScript declaration
  • 1.10.0 Resolve typings of internal stories (thanks to @schaschjan)
  • 1.10.2 Minor type adjustment for internal stories
  • 1.11.0 Improve Payload of main script (thanks to @scmx)
  • 1.12.0 Fixes an error when filter_content_type is not an array (thanks to @juanpasolano)
  • 1.13.0 Merged multilink story fieldtype and single options (credits to @juanpasolano and @markus-gx)
  • 1.13.1 Added tests and fix minor typing error (credits to @juanpasolano)
  • 1.13.2 Fixed types
  • 1.14.0 Enable multi-file support and add RichText typings (thanks to @VictorWinberg & @mattcrn)
  • 1.15.0 Change Storyblok number type to "string" as the API does not return a number type (thanks to @davidhoeck)
  • 2.0.0 Change of use of camelcase: now every title is used with camelcase which potentially breaks existing type names but fixes inconsistency

storyblok-generate-ts's People

Contributors

arduano avatar cjpearson avatar dlhck avatar dohomi avatar jbeast avatar juanpasolano avatar markus-gx avatar merisbahti avatar moarief avatar npostulart avatar o8o0o8o avatar rkcode avatar sassninja avatar schaschjan avatar scmx avatar snikidev avatar tohrxyz avatar valtersantosmatos avatar victorwinberg avatar yobananaboy15 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

storyblok-generate-ts's Issues

Unused import when generated via cli

In the latest version when i generate via cli i get an unused import at the top of the file. This did not happen in prior versions.

The unused import is:
import {StoryblokStory} from 'storyblok-generate-ts'

Single-Option type should allow any values if the source is internal stories

When using the Single-Option field with the source set to internal_stories the field type should allow any values. Currently the type is set to string regardless of the actual input type.
This prevents extending a generated type when using resolve_relations to override the field type from any to the actual type.

On custom SB type generate - includes import of StoryBloKStory?

I have this:

const storyblokToTypescript = require("storyblok-generate-ts").default

storyblokToTypescript({
  componentsJson: require("./components.xxxxx.json"),
  path: __dirname + "/sb-types.ts",
  titlePrefix: "nc",
  compilerOptions: {
    additionalProperties: false,
    unknownAny: false,
  },
})

It generates:

import { StoryblokStory } from "storyblok-generate-ts"

export interface NcfeatureStoryblok {
  name?: string
  _uid: string
  component: "feature"
}

export interface NcgridStoryblok {
  columns?: (
    | NcfeatureStoryblok
    | NcgridStoryblok
    | NcpageStoryblok
    | NcteaserStoryblok
  )[]
  _uid: string
  component: "grid"
}

export interface NcpageStoryblok {
  body?: (
    | NcfeatureStoryblok
    | NcgridStoryblok
    | NcpageStoryblok
    | NcteaserStoryblok
  )[]
  _uid: string
  component: "page"
  uuid?: string
}

export interface NcteaserStoryblok {
  headline?: string
  _uid: string
  component: "teaser"
}

How to exclude the import?

Single Option field with restricted content return only the first restricted content type

When creating a Single Option field with restricted content, the api returns the following JSON.
Notice how the filter_content_type has "post" and "page".

    {
      "name": "BlogFeaturedArticle",
      "display_name": null,
      "id": 3380630,
      "schema": {
        "header_post": {
          "type": "option",
          "use_uuid": true,
          "required": true,
          "source": "internal_stories",
          "folder_slug": "pages/blog, pages/press/press",
          "filter_content_type": [
            "post",
            "page"
          ],
          "pos": 0
        },
      },
      "image": null,
      "component_group_name": "redesign 2023"
    },

However the returned type for it only mentions "post".

export interface BlogFeaturedArticleStoryblok {
  header_post: StoryblokStory<PostStoryblok> | string;
  [k: string]: any;
}

Type should be

export interface BlogFeaturedArticleStoryblok {
  header_post: StoryblokStory<PostStoryblok> |  StoryblokStory<PageStoryblok> | string;
  [k: string]: any;
}

Possible to generate types used in links endpoint?

I'm using a folder structure inside Storyblok to set up a new FAQ, where the structure looks like the following:

Parent folder
    - Sub folder
        - Question
        - Question
    - Sub folder
        - Question
        - Question
        - Question
Parent folder
    - Sub folder
        - Question

...and so on. This to get unique slugs for each parent/sub folder/question, to make everything linkable.

I'm fetching the folders to set up the url structure in my project like this:

const customerFaq = await client.getAll('cdn/links', {
	version: "draft",
	starts_with: 'faq/customer/'
});

To filter the folders, I do like this:

const customerFaqFolders = customerFaq.filter((item) => item.is_folder);

Each of these folder entries looks like this:

{
    id: 12345678,
    slug: 'faq/customer/folder',
    name: 'Folder name',
    is_folder: true,
    parent_id: 23456789,
    published: false,
    path: null,
    position: 0,
    uuid: '12345678-1234-1234-1234-012345678910',
    is_startpage: false,
    real_path: '/faq/customer/folder'
}

...but it seems there are not types being generated for the folder structure, only for the Question stories inside the folders.

Is it possible to set up typings for these kind of items, which are fetched with the cdn/links endpoint?

Thanks a lot for this awesome library, it has helped us a lot when working with Storyblok!

Custom Parser not working?

I am trying to resolve default native-color-picker type from storyblok with a custom parser.

Bildschirmfoto 2024-02-20 um 10 45 01

This is my parser:

function customParser(key, obj) {
  switch (obj.plugin) {
    case "native-color-picker":
      return {
        [key]: {
          _uid: "string",
          color: "string",
          plugin: "string",
        },
      }
    default:
      return {}
  }
}

And here is my script to call everything:
storyblok-generate-ts source=./components.XXXX.json target=./src/types/component-types-sb customTypeParser=./custom-parser.js

Unfortunately this does not parse anything for me. Also tried to put everything into a seperate node file but then I also get an error storyblokToTypescript is not a function

What am I doing wrong here?
Thanks for help :)

What about the `[k: string]: any;` in every type definition

First of all: thank you for this tool. I've discovered it recently, and it's very promising. Keep up the good work.


Every type seems to have a [k: string]: any; part attached to it. I'm not sure why though. It partly renders the type definitions useless, as mistakes like these, will go unnoticed:

Imagine we have the following type:

// component-types-sb.d.ts
export interface TestimonialStoryblok {
  title?: string;
  content?: any;
  _uid: string;
  component: "Testimonial";
  [k: string]: any;
}

Then in the application we do something like this:

<p>
  {{ blok.subtitle }}
</p>

It won't throw a type error, although we already know subtitle does not exist on TestimonialStoryblok.

Tell user what format `Space ID` should be in

Hey, so I've tried to generate types for storyblok components and it asked me to specify my space id . So I went to storyblok and copied #XXXXXX and put it inside [ ] like --space=[#XXXXXX]. It let me input that there, but created a file components.[#XXXXXX].json , but ofcourse this didn't work, the json was empty of components (none were there).

It took me a minute to realize that the cli tool maybe wants a different format of space id, so I've tried without # like [XXXXXX] , didn't work either.

So another approach was without those brackets like --space=XXXXXX and it finally worked. I'm not saying that I wasn't dumb for not realizing it on the first try, but your tool probably should notify user that the space id needs to be in particular format. It should even reject the wrong format.

Anywas, apart from this small issue, thanks for an awesome tool :)

Typed groups?

Hi,
within storyblok you can create fields of type group. The group itself becomes a property of the defintion.

For example, I created a group colors and would like to have all colors inside there like primary, secondary.
So far colors is exported as a field with type any.
Bildschirmfoto 2024-04-26 um 11 19 09

I would like to access thoose properties with colors.primary and not just primary. Is this somehow possible?

Why is multi-options field of type `(number | string)[]`?

In Storyblok I have a component with multi-options field called tags. Its source is a Storyblok data source. Data sources usually are key-value pairs where the key is a string so what I would expect is that tags are typed as string[] but instead I get the following:

export interface ExampleStoryblok {
  headline?: string;
  tags?: (number | string)[];
  text?: any;
  _uid: string;
  [k: string]: any;
}

Is there a case which I'm not thinking about where a multi-option field with internal source uses numbers?

PS: Thanks a lot for mantaining this repo, it is very useful!

Request fails when pulling components (code 401)

Summary of the bug:

It is not possible to generate types because of a failing request (code 401)

image

Note: It was working perfectly previously

Reproduction path:

  • Run storyblok pull-components --space=[SPACE_ID]

Expected behaviour:
Types should be generated

Actual behaviour:
Error is thrown

Suffix can't be empty in the CLI

Hi there!

First off. Thanks for this package! Makes working with SB a lot easier!

I'm having the following problem: perhaps I'm using it wrong, but I can't seem to generate interface names without a suffix.

//package.json
		"generate-sb-types": "storyblok-generate-ts source=./components.XXXXXX.json target=./component-types-sb titlePrefix='I' titleSuffix=''",

Expected result:

export interface Ifeature {
  name?: string;
  _uid: string;
  component: "feature";
  [k: string]: any;
}

Actual result:

export interface IfeatureStoryblok {
  name?: string;
  _uid: string;
  component: "feature";
  [k: string]: any;
}

Suggestion: add a default "customTypeParser" to the plugins library

Hi @dohomi , I was going to do another PR, but better ask first, so as an example I am using the seo-metatags plugin which has the following type bellow, everything works well however I was wondering if we should just add this to the library itself so no one else has to type/retype the custom plugin since it should be the same for everyone (you could for example overwrite it if needed).

Let me know if you want me to PR.
Ty
Valter

 customTypeParser: function exampleCustomParser(key, obj) {
    switch (obj.field_type) {
      case 'seo-metatags':
        return {
          [key]: {
            type: 'object',
            properties: {
              _uid: {
                type: 'string'
              },
              title: {
                type: 'string'
              },
              plugin: {
                type: 'string'
              },
              og_image: {
                type: 'string'
              },
              og_title: {
                type: 'string'
              },
              description: {
                type: 'string'
              },
              twitter_image: {
                type: 'string'
              },
              twitter_title: {
                type: 'string'
              },
              og_description: {
                type: 'string'
              },
              twitter_description: {
                type: 'string'
              }
            }
          }
        }
      default:
        return {}
    }
  }

Names of generated types sometimes inconsistent

Hi, thanks again for the useful tool.

I've come across an issue with the types of components when referenced inside another component, sometimes does not match the actual component's name.

For example, when using this stripped down version of components.json:

{
  "components": [
    {
      "name": "ConversationTemplate",
      "display_name": "Conversation Template",
      "id": 1530302,
      "schema": {
        "characters": {
          "type": "bloks",
          "restrict_components": true,
          "component_whitelist": [
            "ConversationTemplate__character"
          ],
          "required": true,
          "pos": 0,
          "description": "Set how many characters will be part of the conversation."
        }
      }
    },
    {
      "name": "ConversationTemplate__character",
      "display_name": "Character",
      "created_at": "2021-05-11T13:56:11.271Z",
      "updated_at": "2022-08-10T08:57:12.313Z",
      "id": 1530304,
      "schema": {
        "name": {
          "type": "text",
          "required": true,
          "max_length": "50",
          "description": "Character name will appear onscreen to the learner and can be specific (e.g., Dale's mum, Nisha) or generic (e.g. Doctor). "
        }
      }
    }
  ]
}

It generates:

export interface ConversationTemplateStoryblok {
  characters: ConversationTemplateCharacterStoryblok[]; // <-- No underscore
  _uid: string;
  component: "ConversationTemplate";
  [k: string]: any;
}

export interface ConversationTemplate_CharacterStoryblok {
  name: string;
  _uid: string;
  component: "ConversationTemplate__character";
  [k: string]: any;
}

One way of fixing this would be for the components in Storyblok not to have the double underscore. However, sadly the naming of the components is not always within my control.

From looking at the code, it seems like sometimes the type's title is generated with:

getTitle(values.name)

and other times:

camelcase(getTitle(value.name), {
  pascalCase: true
})

which I think is leading to the inconsistency.

I was thinking about putting in a PR, but thought it may be best to check with you first, as there may be a reason the 2 different methods are used. Plus this change may be a breaking one for some people.

Would be interested to hear your thoughts!

Best regards,
Chris

Package does not work with `prettier-plugin-astro`

I'm having an issue when using your package together with prettier-plugin-astro. Using storyblok together with Astro seems like a pretty common use case, hence I think this makes sense to do something about.

After installing prettier-plugin-astro I get this error when running:
storyblok-generate-ts source=./components.XXX.json target=./src/storyblok-components compilerOptions.bannerComment=\"// GENERATED FILE. DO NOT EDIT MANUALLY\"

The error I get is:
ERROR Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/user/github/repo/node_modules/prettier-plugin-astro/dist/index.js from /Users/user/github/repo/node_modules/prettier-plugin-astro/dist/index.js in /Users/user/github/repo/node_modules/prettier/index.js to a dynamic import() which is available in all CommonJS modules.

The result is that the generated d.ts includes only one line:
import {StoryblokStory} from 'storyblok-generate-ts'

removing prettier-plugin-astro from package.json and running yarn install generates the d.ts as expected.

Table field type is missing

Hi!

I noticed, that the table field type is missing in generated types, would be great to have StoryblokTable type or smth with proper structure, but at least table: any would be nice to have

Thank you.

Why does MultilinkStoryblok provide `linktype?: string`

For me using the MultilinkStoryblok is hard to use because there's no way to get out of this type because linktype blankets all strings

  | {
      cached_url?: string;
      linktype?: string;
      [k: string]: any;
    }

export type MultilinkStoryblok =
| {
cached_url?: string;
linktype?: string;
[k: string]: any;
}
| {
id?: string;
cached_url?: string;
linktype?: "story";
[k: string]: any;
}
| {
url?: string;
cached_url?: string;
linktype?: "asset" | "url";
[k: string]: any;
}
| {
email?: string;
linktype?: "email";
[k: string]: any;
};

I would want to be able to do something like this

if (blok.linktype === 'url') {
    return blok.url;
}

But it says url is any because the base type is also matched

Cursor_and_frontend_โ€“_helpers_ts

Link field should use a single type definition

At the moment any component that uses a Link field includes duplicated typings:

myLinkfield?: 
    | {
        cached_url?: string;
        linktype?: string;
        [k: string]: any;
      }
    | {
        id?: string;
        cached_url?: string;
        linktype?: "story";
        [k: string]: any;
      }
    | {
        url?: string;
        cached_url?: string;
        linktype?: "asset" | "url";
        [k: string]: any;
      }
    | {
        email?: string;
        linktype?: "email";
        [k: string]: any;
      };

It would be really nice if this was defined as a separate type (e.g. StoryblokLink) that we could import. For example I've got a separate utility script to parse any link field where I'm going to have to hard-code this type for the time being.

Remove bundled dependencies from published package

Right now, this package's latest version in NPM includes:

  • example/node_modules - the full set of dependencies for the example project.
  • .yarn/cache - the PnP offline mirror, useful for development, not useful for publishing.

It drives the total size to 55MB zipped, which in turns generates warnings from Github when using Yarn's Zero Installs.

It probably isn't intentional, and a simple .npmignore should fix it & prevent future re-occurrence.

image

It might also be worth it to exclude the .github folder while we're at it, along with linter configs & tests.

Thanks!

Improve typing of rich text fields

Currently rich text fields are typed as any. Would it be possible to use the Richtext interface from storyblok-js-client instead?

export interface Richtext {
  content: Array<Object>;
}

It's small, so perhaps it could also be done inline?

Typescript error removal

Hi,
While developing a project with astro and storyblok in typescript I realized there is an error message I'm not able to remove unless I modify the component types file by hand.

image

I found out that I can remove the error by extending the component type from the SbBlokData type:

export interface LocationGalleryStoryblok extends SbBlokData {
  [...]
}

Is there a way to add the 'extends' declaration with storyblok-generate-ts?

Thank you

required Single-Option type includes redundant empty string

In Storyblok I have a Single-Option field that is required and has a default value set. It's for a form button so the options are button and submit. When I run storyblok-generate-ts the definition for this field is as follows:

buttonType: "" | "button" | "submit";

In this case the empty string is not a valid option for this field and should not be included in the generated type.

Consider extending `SbBlokData` for all blok-types

At the moment there is no generic way to declare responses from the storyblok API. I'm declaring the response of useStoryblokApi().get() as StoryblokStory. More appropriate would be StoryblokStory, however there is nothing saying that all the generated types match SbBlokData.

Does adding extends SbBlokData to all generated types make sense? If so, please consider adding it. This would also mean that all generated interfaces does no longer need to declare _uid and [k:string] anymore.

Multiple exports of name 'TableStoryblok'

Hey, thanks for this tool, very helpful ๐Ÿ™

Issue:

  • We have a component called table with the field data inside which is of type Table

Screenshot 2022-07-26 at 16 47 50

  • When we generate the types we end up with:
export interface TableStoryblok {
    thead: {
        _uid: string;
        value?: string;
        component: number;
        [k: string]: any;
    }[];
    tbody: {
        _uid: string;
        body: {
            _uid?: string;
            value?: string;
            component?: number;
            [k: string]: any;
        }[];
        component: number;
        [k: string]: any;
    }[];
    [k: string]: any;
}

export interface TableStoryblok {
    data?: TableStoryblok;
    _uid: string;
    component: 'table';
    [k: string]: any;
}

This has generated TableStoryblok twice in our generated file.

We could rename this table component to something like ourTable, but would like to avoid having to do this just for this situation.

Any ideas with this one? Does this look like a bug?

Thanks again ๐Ÿ™

Multilink fieldtype can contain a story object when used with internal links

API Response:
{ id: '105f42b4-3f71-4d7d-b5cc-c3d2971b1b57', url: '', linktype: 'story', fieldtype: 'multilink', cached_url: 'en/imprint', story: { name: 'Imprint', id: 156921521, uuid: '505f42b4-7f71-5d7d-a5ef-b3d2121b1b57', slug: 'imprint', url: 'en/imprint', full_slug: 'en/imprint', _stopResolving: true } }

Missing in MultilinkStoryblok type:
{ id?: string; cached_url?: string; anchor?: string; linktype?: "story"; [k: string]: any; }

Custom Plugin fields are not included in the generated TypeScript schema

When I add a custom plugin field of, for example, a native color picker, the Blok doesn't include that field in its generated schema at all, even though the JSON returned contains the information.

Is this by design? If not, any suggestions on a workaround would be appreciated. Thanks ๐Ÿค

image
image

`target` property missing from `MultiLinkStoryBlok`

When a Link field is configured with "Allow links to be open in a new tab", the JSON will contain a "target" attribute containing either "_self" or "_blank".

Example Storyblok content JSON:

"bottom_link": {
	"id": "106ed398-8719-4ffd-b548-7d583b152d3f",
	"url": "",
	"target": "_self",
	"linktype": "story",
	"fieldtype": "multilink",
	"cached_url": "about"
},

The JSON provided by running storyblok pull-components also includes a flag to tell whether this is enabled or not. Example link field:

"bottom_link": {
	"type": "multilink",
	"pos": 12,
	"allow_target_blank": true
},

Can we include some support for an optional "target" key on the MultiLinkStoryBlok type?

Empty string value with `Hide empty option` check

It seems the Hide empty option doesn't really mean that you can't have an empty string.
If you take a close look at this example :

Screenshot 2024-03-29 at 12 15 30

Even with Hide empty option check and a default, the Storyblok interface allow you to clear the field and return an empty string. The possible values are :

  • "primary"
  • "secondary"
  • "tertiary"
  • ""

I think we should remove this following lines to be 100% typesafe :

if (options.length && element.exclude_empty_option !== true) {
    options.unshift('')
}

WDYT ?

String types are hidden from VSCode autocompletion

All required string fields are hidden from VS Code autocompletion due to them being masked by [k: string]: any;

export interface PageStoryblok {
  title: string;
  body?: any[];
  _uid: string;
  component: "page";
  uuid?: string;
  [k: string]: any;
}

image

export interface PageStoryblok {
  title: string;
  body?: any[];
  _uid: string;
  component: 'page';
  uuid?: string;
}

image

Type names with numbers are declared with different names than they are are referenced with

We have some blocks with numbers in their names, eg. "project_image_2p". When converting the imported JSON into types though, it generates two different type names for that block.

The type declaration in this case will output ProjectImage_2PStoryblok with underscore, where any references to that type will instead reference an undeclared ProjectImage2PStoryblok without underscore.

eg: components.json

{
  "components": [
  ...
    {
      "name": "project_image_2p",
      "display_name": "Project Image - 2 x Portrait",
      ...
    },
    {
      "name": "project",
      "schema": {
        "content_blocks": {
          "type": "bloks",
          "restrict_type": "",
          "restrict_components": true,
          "component_whitelist": [
            "project_image_2p",
          ],
        },
      },
      ...
     },
  ]
}

eg. component-types-sb.d.ts

export interface ProjectImage_2PStoryblok {
  _uid: string;
  component: "project_image_2p";
  [k: string]: any;
}

export interface ProjectStoryblok {
  content_blocks?: ProjectImage2PStoryblok[];
  _uid: string;
  component: "project";
  [k: string]: any;
}

Should numeric fields really have type string?

Hi ๐Ÿ‘‹
I noticed the recent change "Parse numbers to string type" #45 to change numeric fields to have type string in the resulting interface.
This might not be correct. For me the numeric field values have always been given as number literals from api and storyblok-js-client.

Perhaps the data you looked at was a field that had type string and was changed to numeric, and there might be some bug resulting in you continuing to get it as a string because of the previous type?

Some ideas:
Maybe a jsdoc comment could be generated that shows the storyblok type of each field.

If we can verify that the value is sometimes a number then maybe consider having string | number as generated type.
Consumers would still need to wrap each usage with Number(blok.field) but at least you see that it might already be a string.

Extra "| string" in the option list

In case of a "Multi-Options" field pointing to another story (source === "internal_stories") I think there is no way there can be a string in the option list.

Talking about | string at the end of all those types:

if (element.source === "internal_stories" && element.filter_content_type) {
if (element.type === "option") {
if(Array.isArray(element.filter_content_type)){
return {
tsType: `(${element.filter_content_type.map((type2) => getStoryTypeTitle(type2)).join(" | ")} | string )`
}
} else {
return {
tsType: `(${getStoryTypeTitle(element.filter_content_type)} | string )`,
}
}
}
if (element.type === "options") {
if(Array.isArray(element.filter_content_type)){
return {
tsType: `(${element.filter_content_type.map((type2) => getStoryTypeTitle(type2)).join(" | ")} | string )[]`
};
} else {
return {
tsType: `(${getStoryTypeTitle(element.filter_content_type)} | string )[]`
}
}
}

Throws an error when a group has no members

Although technically this shouldn't be happening in the future of my project, currently I'm just prototyping and I stumbled on the error I included below.

I suggest using never[] instead of ()[] for groups that have no items.

Type: bloks array but not whitelisted (will result in all elements): main_page_storyblok
Group has no members:  fa388a95-aec5-4dcf-9ab0-84576a4885a6
Group has no members:  9632ab38-80f6-480b-9b94-73a555ce8751
ERROR SyntaxError: '=>' expected. (9:14)
   7 | subsubtitle?: string
   8 | buttons?: any[]
>  9 | sections?: ()[]
     |              ^
  10 | _uid: string
  11 | component: "main_page"
  12 | [k: string]: any
    at Ve (/home/arduano/programming/temp/new-next-test/node_modules/prettier/parser-typescript.js:1:15607)
    at vz (/home/arduano/programming/temp/new-next-test/node_modules/prettier/parser-typescript.js:280:5919)
    at Object.yz [as parse] (/home/arduano/programming/temp/new-next-test/node_modules/prettier/parser-typescript.js:280:6242)
    at Object.parse (/home/arduano/programming/temp/new-next-test/node_modules/prettier/index.js:7334:23)
    at coreFormat (/home/arduano/programming/temp/new-next-test/node_modules/prettier/index.js:8645:18)
    at formatWithCursor2 (/home/arduano/programming/temp/new-next-test/node_modules/prettier/index.js:8837:18)
    at /home/arduano/programming/temp/new-next-test/node_modules/prettier/index.js:37229:12
    at format (/home/arduano/programming/temp/new-next-test/node_modules/prettier/index.js:37243:12)
    at format (/home/arduano/programming/temp/new-next-test/node_modules/json-schema-to-typescript/dist/src/formatter.js:20:34)
    at /home/arduano/programming/temp/new-next-test/node_modules/json-schema-to-typescript/dist/src/index.js:163:56 {
  loc: { start: { line: 9, column: 14 } },
  codeFrame: '\x1B[0m \x1B[90m  7 |\x1B[39m subsubtitle\x1B[33m?\x1B[39m\x1B[33m:\x1B[39m string\x1B[0m\n' +
    '\x1B[0m \x1B[90m  8 |\x1B[39m buttons\x1B[33m?\x1B[39m\x1B[33m:\x1B[39m any[]\x1B[0m\n' +
    '\x1B[0m\x1B[31m\x1B[1m>\x1B[22m\x1B[39m\x1B[90m  9 |\x1B[39m sections\x1B[33m?\x1B[39m\x1B[33m:\x1B[39m ()[]\x1B[0m\n' +
    '\x1B[0m \x1B[90m    |\x1B[39m              \x1B[31m\x1B[1m^\x1B[22m\x1B[39m\x1B[0m\n' +
    '\x1B[0m \x1B[90m 10 |\x1B[39m _uid\x1B[33m:\x1B[39m string\x1B[0m\n' +
    '\x1B[0m \x1B[90m 11 |\x1B[39m component\x1B[33m:\x1B[39m \x1B[32m"main_page"\x1B[39m\x1B[0m\n' +
    '\x1B[0m \x1B[90m 12 |\x1B[39m [k\x1B[33m:\x1B[39m string]\x1B[33m:\x1B[39m any\x1B[0m'
}```

[Feature Request] Generate Types of Datasources

Hi there! First of all, thanks for your work on this package. It's really great.

I started to use the Datasources Feature of Storyblok more for my Single-Option fields.
It's great because you can share field options between components, to keep them consistent.

However though, I noticed that when having "locally"/inside the component defined Single-Option fields, they are typed with a union of the possible field values. When using Datasources, I just get a string type out of them, which isn't that helpful.

If it's technically possible, it would be great to have the datasources also typed. If you like, I can offer to help with integrating this feature.

TypeError: element.filter_content_type.map is not a function

Hi there,
Error:

/Users/juan/projects/axon/axon-com-marketing-site-with-wondercraft/node_modules/storyblok-generate-ts/dist/index.js:528
            tsType: `(${element.filter_content_type.map((type2) => getStoryTypeTitle(type2)).join(" | ")} | string )[]`

I am getting the above error from a component with the following shape.

  {
      "name": "featured_products",
      "display_name": null,
      "created_at": "2021-12-28T16:40:58.257Z",
      "updated_at": "2022-07-04T02:52:55.579Z",
      "id": 2066808,
      "schema": {
        "title": {
          "type": "text",
          "translatable": true
        },
        "products": {
          "type": "options",
          "translatable": true,
          "source": "internal_stories",
          "folder_slug": "",
          "filter_content_type": "product"
        }
      }
    }

It seems that this property filter_content_type could be also a string, in fact all of mine are strings, never arrays.

https://github.com/dohomi/storyblok-generate-ts/blob/master/src/index.ts#L195-L208
I had to patch the module and do the following

          if (element.type === "options") {
                if(Array.isArray(element.filter_content_type)){
                    return {
                        tsType: `(${element.filter_content_type.map((type2) => getStoryTypeTitle(type2)).join(" | ")} | string )[]`
                    };
                } else {
                    return {
                        tsType: `(${getStoryTypeTitle(element.filter_content_type)} | string )[]`
                    }
                }
            }

Missing props in MultilinkStoryblok

Hi, thanks for this very helpful package. I think there are missing and important props in the Multilink type, e.g. anchor or url

Two examples from the API:

 {
    "id": "bd897d30-ca59-4a5f-9577-3f18ca47dc74",
    "url": "",
    "anchor": "test",
    "linktype": "story",
    "fieldtype": "multilink",
    "cached_url": "start"
 }

 {
    "id": "",
    "url": "https://www.google.de/",
    "linktype": "url",
    "fieldtype": "multilink",
    "cached_url": "https://www.google.de/"
}

Question on Relations

First off, thank you for the fantastic utility! It's been helpful as I've been learning about storyblok.

In regards to referencing components (ie global components) which are resolved by the resolve_relations query string, Is there a way to link the types based on the reference? At the moment the generated type for a reference field is a string (for the UUID)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.