@strapi/typescript-utils
Advanced tools
Comparing version 0.0.0-8581854cb3 to 0.0.0-a5ae7a818c08e91a4950ad2483d177664ba8ab04
@@ -26,7 +26,7 @@ 'use strict'; | ||
const toPropertySignature = attribute => { | ||
const toPropertySignature = (attribute) => { | ||
return attributeToPropertySignature(schema, attributeName, attribute); | ||
}; | ||
const defaultAssertion = node => { | ||
const defaultAssertion = (node) => { | ||
expect(node.kind).toBe(ts.SyntaxKind.PropertySignature); | ||
@@ -33,0 +33,0 @@ expect(node.name.escapedText).toBe(attributeName); |
@@ -20,3 +20,3 @@ 'use strict'; | ||
const assertGlobalNodeStructure = node => { | ||
const assertGlobalNodeStructure = (node) => { | ||
// "declare global" | ||
@@ -23,0 +23,0 @@ expect(node.kind).toBe(ts.SyntaxKind.ModuleDeclaration); |
@@ -6,3 +6,3 @@ 'use strict'; | ||
module.exports = async dest => { | ||
module.exports = async (dest) => { | ||
const tsConfig = { | ||
@@ -9,0 +9,0 @@ compilerOptions: { |
@@ -13,3 +13,3 @@ 'use strict'; | ||
*/ | ||
const reportWatchStatusChanged = diagnostic => { | ||
const reportWatchStatusChanged = (diagnostic) => { | ||
console.info(ts.formatDiagnostic(diagnostic, formatHost)); | ||
@@ -22,5 +22,5 @@ }; | ||
const { fileNames, options, projectReferences, watchOptions } = resolveConfigOptions( | ||
configPath | ||
); | ||
const { fileNames, options, projectReferences, watchOptions } = | ||
resolveConfigOptions(configPath); | ||
const host = ts.createWatchCompilerHost( | ||
@@ -27,0 +27,0 @@ fileNames, |
'use strict'; | ||
const ts = require('typescript'); | ||
const { factory } = require('typescript'); | ||
@@ -9,31 +8,5 @@ const _ = require('lodash/fp'); | ||
const { getTypeNode, toTypeLiteral } = require('./utils'); | ||
const mappers = require('./mappers'); | ||
/** | ||
* Generate a property signature node for a given attribute | ||
* | ||
* @param {object} schema | ||
* @param {string} attributeName | ||
* @param {object} attribute | ||
* @returns {object} | ||
*/ | ||
const attributeToPropertySignature = (schema, attributeName, attribute) => { | ||
const baseType = getAttributeType(attributeName, attribute, schema.uid); | ||
if (baseType === null) { | ||
return null; | ||
} | ||
const modifiers = getAttributeModifiers(attribute); | ||
const nodes = [baseType, ...modifiers]; | ||
return factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier(attributeName), | ||
undefined, | ||
factory.createIntersectionTypeNode(nodes) | ||
); | ||
}; | ||
/** | ||
* Create the base type node for a given attribute | ||
@@ -68,3 +41,3 @@ * | ||
*/ | ||
const getAttributeModifiers = attribute => { | ||
const getAttributeModifiers = (attribute) => { | ||
const modifiers = []; | ||
@@ -156,123 +129,27 @@ | ||
const mappers = { | ||
string() { | ||
return ['StringAttribute']; | ||
}, | ||
text() { | ||
return ['TextAttribute']; | ||
}, | ||
richtext() { | ||
return ['RichTextAttribute']; | ||
}, | ||
password() { | ||
return ['PasswordAttribute']; | ||
}, | ||
email() { | ||
return ['EmailAttribute']; | ||
}, | ||
date() { | ||
return ['DateAttribute']; | ||
}, | ||
time() { | ||
return ['TimeAttribute']; | ||
}, | ||
datetime() { | ||
return ['DateTimeAttribute']; | ||
}, | ||
timestamp() { | ||
return ['TimestampAttribute']; | ||
}, | ||
integer() { | ||
return ['IntegerAttribute']; | ||
}, | ||
biginteger() { | ||
return ['BigIntegerAttribute']; | ||
}, | ||
float() { | ||
return ['FloatAttribute']; | ||
}, | ||
decimal() { | ||
return ['DecimalAttribute']; | ||
}, | ||
uid({ attribute, uid }) { | ||
const { targetField, options } = attribute; | ||
/** | ||
* Generate a property signature node for a given attribute | ||
* | ||
* @param {object} schema | ||
* @param {string} attributeName | ||
* @param {object} attribute | ||
* @returns {object} | ||
*/ | ||
const attributeToPropertySignature = (schema, attributeName, attribute) => { | ||
const baseType = getAttributeType(attributeName, attribute, schema.uid); | ||
// If there are no params to compute, then return the attribute type alone | ||
if (targetField === undefined && options === undefined) { | ||
return ['UIDAttribute']; | ||
} | ||
if (baseType === null) { | ||
return null; | ||
} | ||
const params = []; | ||
const modifiers = getAttributeModifiers(attribute); | ||
// If the targetField property is defined, then reference it, | ||
// otherwise, put `undefined` keyword type nodes as placeholders | ||
const targetFieldParams = _.isUndefined(targetField) | ||
? [ | ||
factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword), | ||
factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword), | ||
] | ||
: [factory.createStringLiteral(uid), factory.createStringLiteral(targetField)]; | ||
const nodes = [baseType, ...modifiers]; | ||
params.push(...targetFieldParams); | ||
// If the options property is defined, transform it to | ||
// a type literral node and add it to the params list | ||
if (_.isObject(options)) { | ||
params.push(toTypeLiteral(options)); | ||
} | ||
return ['UIDAttribute', params]; | ||
}, | ||
enumeration({ attribute }) { | ||
const { enum: enumValues } = attribute; | ||
return ['EnumerationAttribute', [toTypeLiteral(enumValues)]]; | ||
}, | ||
boolean() { | ||
return ['BooleanAttribute']; | ||
}, | ||
json() { | ||
return ['JSONAttribute']; | ||
}, | ||
media() { | ||
return ['MediaAttribute']; | ||
}, | ||
relation({ uid, attribute }) { | ||
const { relation, target } = attribute; | ||
const isMorphRelation = relation.toLowerCase().includes('morph'); | ||
if (isMorphRelation) { | ||
return [ | ||
'RelationAttribute', | ||
[factory.createStringLiteral(uid, true), factory.createStringLiteral(relation, true)], | ||
]; | ||
} | ||
return [ | ||
'RelationAttribute', | ||
[ | ||
factory.createStringLiteral(uid, true), | ||
factory.createStringLiteral(relation, true), | ||
factory.createStringLiteral(target, true), | ||
], | ||
]; | ||
}, | ||
component({ attribute }) { | ||
const target = attribute.component; | ||
const params = [factory.createStringLiteral(target, true)]; | ||
if (attribute.repeatable) { | ||
params.push(factory.createTrue()); | ||
} | ||
return ['ComponentAttribute', params]; | ||
}, | ||
dynamiczone({ attribute }) { | ||
const componentsParam = factory.createTupleTypeNode( | ||
attribute.components.map(component => factory.createStringLiteral(component)) | ||
); | ||
return ['DynamicZoneAttribute', [componentsParam]]; | ||
}, | ||
return factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier(attributeName), | ||
undefined, | ||
factory.createIntersectionTypeNode(nodes) | ||
); | ||
}; | ||
@@ -279,0 +156,0 @@ |
'use strict'; | ||
/* eslint-disable no-bitwise */ | ||
const ts = require('typescript'); | ||
@@ -9,2 +11,21 @@ const { factory } = require('typescript'); | ||
/** | ||
* | ||
* @param {object} schemaDefinition | ||
* @param {ts.InterfaceDeclaration} schemaDefinition.definition | ||
* @param {object} schemaDefinition.schema | ||
*/ | ||
const schemaDefinitionToPropertySignature = ({ schema }) => { | ||
const { uid } = schema; | ||
const interfaceTypeName = getSchemaInterfaceName(uid); | ||
return factory.createPropertySignature( | ||
undefined, | ||
factory.createStringLiteral(uid, true), | ||
undefined, | ||
factory.createTypeReferenceNode(factory.createIdentifier(interfaceTypeName)) | ||
); | ||
}; | ||
/** | ||
* Generate the global module augmentation block | ||
@@ -50,21 +71,2 @@ * | ||
/** | ||
* | ||
* @param {object} schemaDefinition | ||
* @param {ts.InterfaceDeclaration} schemaDefinition.definition | ||
* @param {object} schemaDefinition.schema | ||
*/ | ||
const schemaDefinitionToPropertySignature = ({ schema }) => { | ||
const { uid } = schema; | ||
const interfaceTypeName = getSchemaInterfaceName(uid); | ||
return factory.createPropertySignature( | ||
undefined, | ||
factory.createStringLiteral(uid, true), | ||
undefined, | ||
factory.createTypeReferenceNode(factory.createIdentifier(interfaceTypeName)) | ||
); | ||
}; | ||
module.exports = { generateGlobalDefinition }; |
@@ -21,3 +21,3 @@ 'use strict'; | ||
generateImportDefinition() { | ||
const formattedImports = imports.map(key => | ||
const formattedImports = imports.map((key) => | ||
factory.createImportSpecifier(false, undefined, factory.createIdentifier(key)) | ||
@@ -24,0 +24,0 @@ ); |
@@ -26,63 +26,3 @@ 'use strict'; | ||
/** | ||
* Generate type definitions for Strapi schemas | ||
* | ||
* @param {object} options | ||
* @param {Strapi} options.strapi | ||
* @param {{ distDir: string; appDir: string; }} options.dirs | ||
* @param {string} [options.outDir] | ||
* @param {string} [options.file] | ||
* @param {boolean} [options.verbose] | ||
*/ | ||
const generateSchemasDefinitions = async (options = {}) => { | ||
const { | ||
strapi, | ||
outDir = process.cwd(), | ||
file = DEFAULT_OUT_FILENAME, | ||
verbose = false, | ||
silent = false, | ||
} = options; | ||
const schemas = getAllStrapiSchemas(strapi); | ||
const schemasDefinitions = Object.values(schemas).map(schema => ({ | ||
schema, | ||
definition: generateSchemaDefinition(schema), | ||
})); | ||
const formattedSchemasDefinitions = schemasDefinitions.reduce((acc, def) => { | ||
acc.push( | ||
// Definition | ||
def.definition, | ||
// Add a newline between each interface declaration | ||
factory.createIdentifier('\n') | ||
); | ||
return acc; | ||
}, []); | ||
const allDefinitions = [ | ||
// Imports | ||
generateImportDefinition(), | ||
// Add a newline after the import statement | ||
factory.createIdentifier('\n'), | ||
// Schemas | ||
...formattedSchemasDefinitions, | ||
// Global | ||
generateGlobalDefinition(schemasDefinitions), | ||
]; | ||
const output = emitDefinitions(allDefinitions); | ||
const formattedOutput = await format(output); | ||
const definitionFilepath = await saveDefinitionToFileSystem(outDir, file, formattedOutput); | ||
logDebugInformation(schemasDefinitions, { filepath: definitionFilepath, verbose, silent }); | ||
}; | ||
const emitDefinitions = definitions => { | ||
const emitDefinitions = (definitions) => { | ||
const nodeArray = factory.createNodeArray(definitions); | ||
@@ -118,3 +58,3 @@ | ||
*/ | ||
const format = async content => { | ||
const format = async (content) => { | ||
const configFile = await prettier.resolveConfigFile(); | ||
@@ -149,3 +89,3 @@ const config = configFile | ||
const sortedDefinitions = definitions.map(def => ({ | ||
const sortedDefinitions = definitions.map((def) => ({ | ||
...def, | ||
@@ -188,2 +128,62 @@ attributesCount: getDefinitionAttributesCount(def.definition), | ||
/** | ||
* Generate type definitions for Strapi schemas | ||
* | ||
* @param {object} options | ||
* @param {Strapi} options.strapi | ||
* @param {{ distDir: string; appDir: string; }} options.dirs | ||
* @param {string} [options.outDir] | ||
* @param {string} [options.file] | ||
* @param {boolean} [options.verbose] | ||
*/ | ||
const generateSchemasDefinitions = async (options = {}) => { | ||
const { | ||
strapi, | ||
outDir = process.cwd(), | ||
file = DEFAULT_OUT_FILENAME, | ||
verbose = false, | ||
silent = false, | ||
} = options; | ||
const schemas = getAllStrapiSchemas(strapi); | ||
const schemasDefinitions = Object.values(schemas).map((schema) => ({ | ||
schema, | ||
definition: generateSchemaDefinition(schema), | ||
})); | ||
const formattedSchemasDefinitions = schemasDefinitions.reduce((acc, def) => { | ||
acc.push( | ||
// Definition | ||
def.definition, | ||
// Add a newline between each interface declaration | ||
factory.createIdentifier('\n') | ||
); | ||
return acc; | ||
}, []); | ||
const allDefinitions = [ | ||
// Imports | ||
generateImportDefinition(), | ||
// Add a newline after the import statement | ||
factory.createIdentifier('\n'), | ||
// Schemas | ||
...formattedSchemasDefinitions, | ||
// Global | ||
generateGlobalDefinition(schemasDefinitions), | ||
]; | ||
const output = emitDefinitions(allDefinitions); | ||
const formattedOutput = await format(output); | ||
const definitionFilepath = await saveDefinitionToFileSystem(outDir, file, formattedOutput); | ||
logDebugInformation(schemasDefinitions, { filepath: definitionFilepath, verbose, silent }); | ||
}; | ||
module.exports = generateSchemasDefinitions; |
@@ -12,2 +12,32 @@ 'use strict'; | ||
/** | ||
* Generate a property signature for the schema's `attributes` field | ||
* | ||
* @param {object} schema | ||
* @returns {ts.PropertySignature} | ||
*/ | ||
const generateAttributePropertySignature = (schema) => { | ||
const { attributes } = schema; | ||
const properties = Object.entries(attributes).map(([attributeName, attribute]) => { | ||
return attributeToPropertySignature(schema, attributeName, attribute); | ||
}); | ||
return factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier('attributes'), | ||
undefined, | ||
factory.createTypeLiteralNode(properties) | ||
); | ||
}; | ||
const generatePropertyLiteralDefinitionFactory = (schema) => (key) => { | ||
return factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier(key), | ||
undefined, | ||
toTypeLiteral(schema[key]) | ||
); | ||
}; | ||
/** | ||
* Generate an interface declaration for a given schema | ||
@@ -18,3 +48,3 @@ * | ||
*/ | ||
const generateSchemaDefinition = schema => { | ||
const generateSchemaDefinition = (schema) => { | ||
const { uid } = schema; | ||
@@ -32,3 +62,3 @@ | ||
// Ignore non-existent or empty declarations | ||
.filter(key => !isEmpty(schema[key])) | ||
.filter((key) => !isEmpty(schema[key])) | ||
// Generate literal definition for each property | ||
@@ -60,32 +90,2 @@ .map(generatePropertyLiteralDefinitionFactory(schema)); | ||
/** | ||
* Generate a property signature for the schema's `attributes` field | ||
* | ||
* @param {object} schema | ||
* @returns {ts.PropertySignature} | ||
*/ | ||
const generateAttributePropertySignature = schema => { | ||
const { attributes } = schema; | ||
const properties = Object.entries(attributes).map(([attributeName, attribute]) => { | ||
return attributeToPropertySignature(schema, attributeName, attribute); | ||
}); | ||
return factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier('attributes'), | ||
undefined, | ||
factory.createTypeLiteralNode(properties) | ||
); | ||
}; | ||
const generatePropertyLiteralDefinitionFactory = schema => key => { | ||
return factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier(key), | ||
undefined, | ||
toTypeLiteral(schema[key]) | ||
); | ||
}; | ||
module.exports = { generateSchemaDefinition }; |
@@ -26,3 +26,3 @@ 'use strict'; | ||
*/ | ||
const getAllStrapiSchemas = strapi => ({ ...strapi.contentTypes, ...strapi.components }); | ||
const getAllStrapiSchemas = (strapi) => ({ ...strapi.contentTypes, ...strapi.components }); | ||
@@ -37,15 +37,3 @@ /** | ||
/** | ||
* Get the parent type name to extend based on the schema's nature | ||
* | ||
* @param {object} schema | ||
* @returns {string} | ||
*/ | ||
const getSchemaExtendsTypeName = schema => { | ||
const base = getSchemaModelType(schema); | ||
return upperFirst(base) + 'Schema'; | ||
}; | ||
const getSchemaModelType = schema => { | ||
const getSchemaModelType = (schema) => { | ||
const { modelType, kind } = schema; | ||
@@ -59,3 +47,3 @@ | ||
// Content-Types | ||
else if (modelType === 'contentType') { | ||
if (modelType === 'contentType') { | ||
return kind; | ||
@@ -68,2 +56,14 @@ } | ||
/** | ||
* Get the parent type name to extend based on the schema's nature | ||
* | ||
* @param {object} schema | ||
* @returns {string} | ||
*/ | ||
const getSchemaExtendsTypeName = (schema) => { | ||
const base = getSchemaModelType(schema); | ||
return `${upperFirst(base)}Schema`; | ||
}; | ||
/** | ||
* Get a type node based on a type and its params | ||
@@ -84,3 +84,3 @@ * | ||
*/ | ||
const toTypeLiteral = data => { | ||
const toTypeLiteral = (data) => { | ||
if (isUndefined(data)) { | ||
@@ -107,3 +107,3 @@ return factory.createLiteralTypeNode(ts.SyntaxKind.UndefinedKeyword); | ||
if (isArray(data)) { | ||
return factory.createTupleTypeNode(data.map(item => toTypeLiteral(item))); | ||
return factory.createTupleTypeNode(data.map((item) => toTypeLiteral(item))); | ||
} | ||
@@ -148,3 +148,3 @@ | ||
*/ | ||
const getDefinitionAttributesCount = definition => { | ||
const getDefinitionAttributesCount = (definition) => { | ||
const attributesNode = definition.members.find(propEq('name.escapedText', 'attributes')); | ||
@@ -151,0 +151,0 @@ |
@@ -11,3 +11,3 @@ 'use strict'; | ||
*/ | ||
module.exports = diagnostics => { | ||
module.exports = (diagnostics) => { | ||
const formattedDiagnostics = ts.formatDiagnosticsWithColorAndContext( | ||
@@ -14,0 +14,0 @@ Array.isArray(diagnostics) ? diagnostics : [diagnostics], |
@@ -7,3 +7,3 @@ 'use strict'; | ||
module.exports = configPath => { | ||
module.exports = (configPath) => { | ||
// Parse the tsconfig.json file and resolve every file name & compiler options | ||
@@ -10,0 +10,0 @@ const { errors, ...configOptions } = ts.getParsedCommandLineOfConfigFile( |
'use strict'; | ||
const path = require('path'); | ||
@@ -3,0 +4,0 @@ const resolveConfigOptions = require('./resolve-config-options'); |
{ | ||
"name": "@strapi/typescript-utils", | ||
"version": "0.0.0-8581854cb3", | ||
"version": "0.0.0-a5ae7a818c08e91a4950ad2483d177664ba8ab04", | ||
"description": "Typescript support for Strapi", | ||
@@ -38,3 +38,3 @@ "keywords": [ | ||
}, | ||
"gitHead": "8581854cb3c817ae42b829ea686a1b0702ed9245" | ||
"gitHead": "a5ae7a818c08e91a4950ad2483d177664ba8ab04" | ||
} |
77189
140214
31
1908