Skip to content

Commit

Permalink
Merge pull request #88 from storyblok/revert-85-revert-79-WEB-790
Browse files Browse the repository at this point in the history
Revert "Revert "Add command for Typescript typedefs generation""
  • Loading branch information
christianzoppi committed Mar 18, 2024
2 parents 26e9605 + d376b1d commit 35c1d3b
Show file tree
Hide file tree
Showing 74 changed files with 5,102 additions and 2,886 deletions.
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
module.exports = {
env: {
commonjs: true,
es6: true,
node: true,
'jest/globals': true
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Build
run: yarn build
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,8 @@ dist

# IntelliJ
.idea/

# CLI generated files
components.*.json
presets.*.json
storyblok-component-types.d.ts
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"editor.formatOnSave": false,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
},
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
86 changes: 86 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,92 @@ module.exports = function (block) {
}
```

## Typescript
It is possible to generate Typescript type definitions for your Storyblok components. The type definitions are based on the components' JSON Schema that can be retrieved with the [pull-components](#pull-components) command.

### generate-typescript-typedefs

Generate a file with the type definitions for the specified components' JSON Schemas.

```sh
$ storyblok generate-typescript-typedefs
--sourceFilePaths <PATHS>
--destinationFilePath <PATH>
--typeNamesPrefix <STRING>
--typeNamesSuffix <STRING>
--JSONSchemaToTSOptionsPath <PATH>
--customFieldTypesParserPath <PATH>
```

#### Options

* `sourceFilePaths` <sub>(alias `source`)</sub> : Path(s) to the components JSON file(s) as comma separated values
* `destinationFilePath` <sub>(alias `target`) *optional*</sub> : Path to the Typescript file that will be generated (*default*: `storyblok-component-types.d.ts`)
* `typeNamesPrefix` <sub>(alias `titlePrefix`) *optional*</sub> : A prefix that will be prepended to all the names of the generated types
* `typeNamesSuffix` <sub>(alias `titleSuffix`) *optional*</sub> : A suffix that will be appended to all the names of the generated types (*default*: `Storyblok`)
* `JSONSchemaToTSOptionsPath` <sub>(alias `compilerOptions`) *optional*</sub> : Path to a JSON file with a list of options supported by `json-schema-to-typescript`
* `customFieldTypesParserPath` <sub>(alias `customTypeParser`) *optional*</sub> : Path to the parser file for Custom Field Types

#### Examples

```sh
# Generate typedefs for the components retrieved for the space `12345` via the `storyblok pull-components` command
$ storyblok generate-typescript-typedefs --sourceFilePaths ./components.12345.json

# Generate typedefs for multiple components sources
$ storyblok generate-typescript-typedefs --sourceFilePaths ./fooComponent-12345.json,./barComponent-12345.json

# Custom path for the typedefs file
$ storyblok generate-typescript-typedefs --sourceFilePaths ./components.12345.json --destinationFilePath ./types/my-custom-type-file.d.ts

# Provide customized options for the JSON-schema-to-typescript lib
$ storyblok generate-typescript-typedefs --sourceFilePaths ./components.12345.json --JSONSchemaToTSOptionsPath ./PathToJSONFileWithCustomOptions.json

# Provide a custom field types parser
$ storyblok generate-typescript-typedefs --sourceFilePaths ./components.12345.json --customFieldTypesParserPath ./customFieldTypesParser.js

```

##### JSON Schema to Typescript options
This script uses the `json-schema-to-typescript` library under the hood. Values of the [JSON Schema to Typescript options](https://www.npmjs.com/package/json-schema-to-typescript#options) can be customized providing a JSON file to the `JSONSchemaToTSOptionsPath`.

The default values used for the `storyblok generate-typescript-typedefs` command are the same defaults for the library except for two properties:
* `bannerComment` - The default value is `""` to remove noise from the generated Typedefs file
* `unknownAny` - The default value is `false` because it can help a smoother Typescript adoption on a JS project

Example `JSONSchemaToTSOptions` JSON file to remove `additionalProperties` from the generated type definitions:

```json
{
"additionalProperties": false,
}
```

##### Custom Field Types parser
Storyblok [Custom Field Types](https://www.storyblok.com/docs/plugins/field-plugins/introduction) do not have inherent JSONSchema definitions. To overcome this issue, you can provide a path to a script exporting a parser function that should render a [JSONSchema Node](https://json-schema.org/learn/getting-started-step-by-step#define-properties) for each of your Custom Field Types. The parser function should be exported as a default export, like in the following example:
```js
export default function (key, obj) {
switch (obj.field_type) {
case 'my-custom-field-type-name':
return {
[key]: {
type: 'object',
properties: {
color: { type: 'string' }
},
required: ['color']
}
}
default:
return {}
}
}
```





## You're looking for a headstart?

Check out our guides for client side apps (VueJS, Angular, React, ...), static site (Jekyll, NuxtJs, ...), dynamic site examples (Node, PHP, Python, Laravel, ...) on our [Getting Started](https://www.storyblok.com/getting-started) page.
24 changes: 13 additions & 11 deletions __mocks__/fs-extra.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const fs = jest.genMockFromModule('fs-extra')
import { jest } from '@jest/globals'

const fs = jest.createMockFromModule('fs-extra')

let mockFiles = Object.create(null)

Expand All @@ -19,15 +21,15 @@ const readFile = jest.fn((path) => {
mockFiles = path
return Promise.resolve(JSON.stringify([
{
"id": 0,
"full_slug": "another-post",
"content": {
"_uid": "5647c21f-8813-4f8a-ad38-b9f74e0e7c89",
"text": "Donec tortor mauris, mollis vel pretium vitae, lacinia nec sapien. Donec erat neque, ullamcorper tincidunt iaculis sit amet, pharetra bibendum ipsum. Nunc mattis risus ac ante consequat nec pulvinar neque molestie. Etiam interdum nunc at metus lacinia non varius erat dignissim. Integer elementum, felis id facilisis vulputate, ipsum tellus venenatis dui, at blandit nibh massa in dolor. Cras a ultricies sapien. Vivamus adipiscing feugiat pharetra.",
"image": "https://a.storyblok.com/f/51376/884x750/3bff01d851/international.svg",
"title": "test",
"category": "news",
"component": "Product"
id: 0,
full_slug: 'another-post',
content: {
_uid: '5647c21f-8813-4f8a-ad38-b9f74e0e7c89',
text: 'Donec tortor mauris, mollis vel pretium vitae, lacinia nec sapien. Donec erat neque, ullamcorper tincidunt iaculis sit amet, pharetra bibendum ipsum. Nunc mattis risus ac ante consequat nec pulvinar neque molestie. Etiam interdum nunc at metus lacinia non varius erat dignissim. Integer elementum, felis id facilisis vulputate, ipsum tellus venenatis dui, at blandit nibh massa in dolor. Cras a ultricies sapien. Vivamus adipiscing feugiat pharetra.',
image: 'https://a.storyblok.com/f/51376/884x750/3bff01d851/international.svg',
title: 'test',
category: 'news',
component: 'Product'
}
}
]))
Expand Down Expand Up @@ -60,4 +62,4 @@ fs.__clearMockFiles = __clearMockFiles

fs.__setMockFiles = __setMockFiles

module.exports = fs
export default fs
23 changes: 23 additions & 0 deletions build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineBuildConfig } from "unbuild";

export default defineBuildConfig({
declaration: true,
rollup: {
inlineDependencies: true,
resolve: {
exportConditions: ["production", "node"] as any,
},
},
entries: ["src/cli"],
externals: [
"@nuxt/test-utils",
"fsevents",
"node:url",
"node:buffer",
"node:path",
"node:child_process",
"node:process",
"node:path",
"node:os",
],
});
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default { transform: {} }
24 changes: 18 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,21 @@
"node",
"javascript"
],
"main": "src/cli.js",
"main": "./dist/cli.mjs",
"files": [
"dist/**"
],
"bin": {
"storyblok": "src/cli.js"
"storyblok": "./dist/cli.mjs"
},
"type": "module",
"scripts": {
"build": "unbuild",
"dev": "npm run build && ./dist/cli.mjs",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"test:unit": "jest --silent",
"test:coverage": "jest --coverage"
"test:unit": "node --experimental-vm-modules ./node_modules/.bin/jest --silent",
"test:coverage": "node --experimental-vm-modules ./node_modules/.bin/jest --coverage"
},
"author": "Dominik Angerer <dominikangerer1@gmail.com>, Alexander Feiglstorfer <delooks@gmail.com>",
"license": "MIT",
Expand All @@ -36,14 +42,15 @@
"fs-extra": "^9.0.1",
"git-clone": "^0.1.0",
"inquirer": "^7.3.2",
"json-schema-to-typescript": "^13.1.2",
"lodash": "^4.17.21",
"netrc": "0.1.4",
"on-change": "^2.0.1",
"open": "^6.0.0",
"p-series": "^2.1.0",
"path": "^0.12.7",
"simple-uuid": "^0.0.1",
"storyblok-js-client": "^5.14.0",
"storyblok-js-client": "^6.7.1",
"update-notifier": "^5.1.0",
"xml-js": "^1.6.11"
},
Expand All @@ -59,11 +66,16 @@
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"jest": "^26.1.0"
"jest": "^29.7.0",
"typescript": "^5.3.3",
"unbuild": "^2.0.0"
},
"release": {
"branches": [
"master"
]
},
"prettier": {
"printWidth": 120
}
}
64 changes: 50 additions & 14 deletions src/cli.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
#!/usr/bin/env node

const commander = require('commander')
import commander from 'commander'
import chalk from 'chalk'
import clear from 'clear'
import figlet from 'figlet'
import inquirer from 'inquirer'
import { ALL_REGIONS, EU_CODE, isRegion } from '@storyblok/region-helper'
import updateNotifier from 'update-notifier'
import fs from 'fs'
import tasks from './tasks'
import { getQuestions, lastStep, api, creds } from './utils'
import { SYNC_TYPES, COMMANDS } from './constants'

const rawPkg = fs.readFileSync('./package.json')
const pkg = JSON.parse(rawPkg)
const program = new commander.Command()

const chalk = require('chalk')
const clear = require('clear')
const figlet = require('figlet')
const inquirer = require('inquirer')
const { ALL_REGIONS, EU_CODE, isRegion } = require('@storyblok/region-helper')

const updateNotifier = require('update-notifier')
const pkg = require('../package.json')

const tasks = require('./tasks')
const { getQuestions, lastStep, api, creds } = require('./utils')
const { SYNC_TYPES, COMMANDS } = require('./constants')
const allRegionsText = ALL_REGIONS.join(', ')

clear()
Expand Down Expand Up @@ -523,6 +523,42 @@ program
}
})

// Generate Typescript type definitions
program
.command(COMMANDS.GENERATE_TYPESCRIPT_TYPEDEFS)
// Providing backward-compatible flags with Storyblok Generate TS https://github.com/dohomi/storyblok-generate-ts
.requiredOption('--source, --sourceFilePaths <PATHS>', 'Path(s) to the components JSON file(s) as comma separated values', (paths, _previous) => paths.split(','))
.option('--target, --destinationFilePath <PATH>', 'Path to the Typescript file that will be generated (default: `storyblok-component-types.d.ts`)')
.option('--titlePrefix, --typeNamesPrefix <STRING>', 'A prefix that will be prepended to all the names of the generated types')
.option('--titleSuffix, --typeNamesSuffix <STRING>', 'A suffix that will be appended to all the names of the generated types (*default*: `Storyblok`)')
.option('--compilerOptions, --JSONSchemaToTSOptionsPath <PATH>', 'Path to a JSON file with a list of options supported by json-schema-to-typescript')
.option('--customTypeParser, --customFieldTypesParserPath <PATH>', 'Path to the parser file for Custom Field Types')
.action((options) => {
console.log(`${chalk.blue('-')} Executing ${COMMANDS.GENERATE_TYPESCRIPT_TYPEDEFS} task`)

const {
sourceFilePaths,
destinationFilePath,
typeNamesPrefix,
typeNamesSuffix,
customFieldTypesParserPath,
JSONSchemaToTSOptionsPath
} = options

try {
tasks.generateTypescriptTypedefs({
sourceFilePaths,
destinationFilePath,
typeNamesPrefix,
typeNamesSuffix,
customFieldTypesParserPath,
JSONSchemaToTSOptionsPath
})
} catch (e) {
errorHandler(e, COMMANDS.GENERATE_TYPESCRIPT_TYPEDEFS)
}
})

program.parse(process.argv)

if (program.rawArgs.length <= 2) {
Expand Down

0 comments on commit 35c1d3b

Please sign in to comment.