This package is a proposal to be part of @strapi packages as @strapi/types
Goal
Typescript empower javscript with static code analysis, which leads us with more robust code. Even if strapi core is not written in Typescript we can expose the Developer API declaration (Plugins / ContentType / etc...)
Implements:
- Content type definition file syntax: strapi/rfcs#28
- Plugin API RFC: strapi/rfcs#23
See also plugin example here: https://github.com/digisquad-io/strapi-plugin-typescript-template
How to test this feature
_Tested on next.11_-
Create
@strapi/types
package:- clone this repository
- run
yarn install
- run
yarn run build
- run
yarn link
-
Build
strapi-plugin-typescript-template
- clone
strapi-plugin-typescript-template
repository - run
yarn install
ANDyarn link "@strapi/types"
- code the plugin in
src/strapi-server.ts
😄 - run
yarn run build
- run
yarn link
- clone
-
Test on next.11
- create strapi app
- run
yarn install
- add
"strapi-plugin-typescript-template": "latest"
- run
yarn link "strapi-plugin-typescript-template"
in yourpackage.json
- update
loadJsFile
from@strapi/strapi
(see diff below) - yay your typescript plugin is loaded 😄
A small hack is needed in @strapi/strapi/lib/core/app-configuration/load-config-file.js
const loadJsFile = file => {
try {
const jsModule = require(file);
// call if function
if (typeof jsModule === 'function') {
return jsModule({ env });
}
+ // use export.default from ESM or Typescript
+ if (jsModule && typeof jsModule.default === 'function') {
+ return jsModule.default({ env });
+ } else if (jsModule && typeof jsModule.default === 'object') {
+ return jsModule.default
+ }
return jsModule;
} catch (error) {
throw new Error(`Could not load js config file ${file}: ${error.message}`);
}
};
// src/server/bootstrap.ts
import {
withStrapi,
} from '@strapi/types'
export function bootstrap() {
return withStrapi(async (strapi) => {
// withStrapi is an empty function,
// it's here only to decalre the type for us
const entityService = strapi.service('entityService')
const articles = await entityService.query("article").findMany({
select: ["title", "description"],
where: { title: "Hello World" },
orderBy: { title: "DESC" },
populate: { category: true },
})
})
}
// src/server/config.ts
import {
defineConfig,
} from '@strapi/types'
export interface CustomPluginOption {
mode: 'default' | 'typescript'
}
export const config = defineConfig(() => ({
default: ({ env }) => ({
mode: env('STRAPI_PLUGIN_CONFIG_MODE', 'default')
} as CustomPluginOption),
validator: () => true,
}))
// src/server/contentType/restaurants.ts
import {
defineContentType,
} from '@strapi/types'
export const restaurants = defineContentType(() => ({
schema: {
kind: 'collectionType',
collectionName: 'restaurants',
info: {
name: 'restaurant',
singularName: 'restaurant',
pluralName: 'restaurants',
displayName: 'Restaurants',
},
options: {},
attributes: {
name: {
type: "string"
}
}
},
}))
// src/strapi-server.ts
import {
defineServerPlugin,
} from '@strapi/types'
import { bootstrap } from './server/bootstrap'
import { config } from './server/bootstrap'
import { restaurants } from './server/contentType/restaurants'
export default defineServerPlugin((strapi) => ({
bootstrap,
config,
contentTypes: {
restaurants,
}
}))