Add a listing of actions and conditions in community extension reference pages (#5464)

This commit is contained in:
D8H
2023-07-07 10:50:47 +02:00
committed by GitHub
parent 5f51a5e465
commit 690ce16ab4
3 changed files with 898 additions and 647 deletions

View File

@@ -3,6 +3,7 @@
* Launch this script to generate a list (in markdown format) of all custom extensions.
*/
const initializeGDevelopJs = require('../public/libGD.js');
const fs = require('fs').promises;
const { default: axios } = require('axios');
const path = require('path');
@@ -16,8 +17,16 @@ const {
convertCommonMarkdownToPythonMarkdown,
} = require('./lib/PythonMarkdownHelper');
const shell = require('shelljs');
const {
generateExtensionReference,
generateExtensionRawText,
rawTextsToString,
} = require('./lib/ExtensionReferenceGenerator');
const { mapVector, mapFor } = require('./lib/MapFor');
/** @typedef {{ tier: 'community' | 'reviewed', shortDescription: string, authorIds: Array<string>, authors?: Array<{id: string, username: string}>, extensionNamespace: string, fullName: string, name: string, version: string, gdevelopVersion?: string, url: string, headerUrl: string, tags: Array<string>, category: string, previewIconUrl: string, eventsBasedBehaviorsCount: number, eventsFunctionsCount: number, helpPath: string, description: string, iconUrl: string}} ExtensionHeader */
/** @typedef {import("./lib/ExtensionReferenceGenerator.js").RawText} RawText */
/** @typedef {{ tier: 'community' | 'reviewed', shortDescription: string, authorIds: Array<string>, authors?: Array<{id: string, username: string}>, extensionNamespace: string, fullName: string, name: string, version: string, gdevelopVersion?: string, url: string, headerUrl: string, tags: Array<string>, category: string, previewIconUrl: string, eventsBasedBehaviorsCount: number, eventsFunctionsCount: number}} ExtensionShortHeader */
const extensionShortHeadersUrl =
'https://api.gdevelop-app.com/asset/extension-short-header';
@@ -30,22 +39,6 @@ const generateSvgImageIcon = iconUrl => {
return `<img src="${iconUrl}" class="extension-icon"></img>`;
};
/** @returns {string} */
const generateExtensionFooterText = fullName => {
return (
`
---
!!! tip
Learn [how to install new extensions](/gdevelop5/extensions/search) by following a step-by-step guide.
*This page is an auto-generated reference page about the **${fullName}** extension, made by the community of [GDevelop, the open-source, cross-platform game engine designed for everyone](https://gdevelop.io/).*` +
' ' +
'Learn more about [all GDevelop community-made extensions here](/gdevelop5/extensions).'
);
};
/**
* @param {{id: string, username: string}[]} authors
*/
@@ -63,33 +56,94 @@ const generateAuthorNamesWithLinks = authors => {
};
/**
* Return the list of all extensions and their associated short headers
* Add a serialized (JS object) events function extension to the project.
*
* (useful as containing author public profiles information).
* @returns {Promise<Array<ExtensionHeader>>} A promise to all extension headers
*
* @param {any} gd
* @param {any} project (gdProject)
*
* @returns {Promise<any>} A promise to all extensions (gdEventsFunctionsExtension)
*/
const getAllExtensionHeaders = async () => {
const addAllExtensionsToProject = async (gd, project) => {
const response = await axios.get(extensionShortHeadersUrl);
const extensionShortHeaders = response.data;
if (!extensionShortHeaders.length) {
throw new Error('Unexpected response from the extension endpoint.');
}
const extensionHeaders = await Promise.all(
return await Promise.all(
extensionShortHeaders.map(async extensionShortHeader => {
const response = await axios.get(extensionShortHeader.headerUrl);
const extensionHeader = response.data;
if (!extensionHeader) {
const response = await axios.get(extensionShortHeader.url);
const serializedExtension = response.data;
if (!serializedExtension) {
throw new Error(
`Unexpected response when fetching an extension header (${
extensionShortHeader.headerUrl
`Unexpected response when fetching an extension (${
extensionShortHeader.url
}).`
);
}
return { ...extensionHeader, ...extensionShortHeader };
const { name } = serializedExtension;
if (!name)
return Promise.reject(new Error('Malformed extension (missing name).'));
const newEventsFunctionsExtension = project.insertNewEventsFunctionsExtension(
name,
0
);
unserializeFromJSObject(
gd,
newEventsFunctionsExtension,
serializedExtension,
'unserializeFrom',
project
);
return newEventsFunctionsExtension;
})
);
};
return extensionHeaders;
/**
* Tool function to restore a serializable object from a JS object.
* Most gd.* objects are "serializable", meaning they have a serializeTo
* and unserializeFrom method.
* @param {any} serializable A gd.* object to restore (gdSerializable)
* @param {Object} object The JS object to be used to restore the serializable.
* @param {string} methodName The name of the unserialization method. "unserializeFrom" by default
* @param {?any} optionalProject The project to pass as argument for unserialization (gdProject)
*/
function unserializeFromJSObject(
gd,
serializable,
object,
methodName = 'unserializeFrom',
optionalProject = undefined
) {
const serializedElement = gd.Serializer.fromJSObject(object);
if (!optionalProject) {
serializable[methodName](serializedElement);
} else {
// It's not uncommon for unserializeFrom methods of gd.* classes
// to require the project to be passed as first argument.
serializable[methodName](optionalProject, serializedElement);
}
serializedElement.delete();
}
/**
* Return the list of all extensions and their associated short headers
* (useful as containing author public profiles information).
* @returns {Promise<Array<ExtensionShortHeader>>} A promise to all extension headers
*/
const getAllExtensionShortHeaders = async () => {
const response = await axios.get(extensionShortHeadersUrl);
const extensionShortHeaders = response.data;
if (!extensionShortHeaders.length) {
throw new Error('Unexpected response from the extension endpoint.');
}
return extensionShortHeaders;
};
const groupBy = (array, getKey) => {
@@ -116,42 +170,39 @@ const sortKeys = table => {
/**
* Create a page for an extension.
* @param {ExtensionHeader} extensionHeader The extension header
* @param {any} gd
* @param {any} project (gdProject)
* @param {any} extension The extension (gdEventsFunctionsExtension)
* @param {ExtensionShortHeader} extensionShortHeader
* @param {boolean} isCommunity The tier
*/
const createExtensionReferencePage = async (extensionHeader, isCommunity) => {
const folderName = getExtensionFolderName(extensionHeader.name);
const referencePageUrl = `${gdevelopWikiUrlRoot}/extensions/${folderName}`;
const helpPageUrl = getHelpLink(extensionHeader.helpPath) || referencePageUrl;
const authorNamesWithLinks = generateAuthorNamesWithLinks(
extensionHeader.authors || []
const createExtensionReferencePage = async (
gd,
project,
extension,
extensionShortHeader,
isCommunity
) => {
const extensionMetadata = generateEventsFunctionExtensionMetadata(
gd,
project,
extension
);
const extensionReference = generateExtensionReference(extensionMetadata);
const referencePageContent = rawTextsToString(
generateExtensionRawText(
extensionReference,
reference =>
generateExtensionHeaderText(
reference,
extensionShortHeader,
isCommunity
),
generateExtensionFooterText
)
);
const referencePageContent =
`# ${extensionHeader.fullName}` +
'\n\n' +
generateSvgImageIcon(extensionHeader.previewIconUrl) +
'\n' +
`${extensionHeader.shortDescription}\n` +
'\n' +
`**Authors and contributors** to this community extension: ${authorNamesWithLinks}.\n` +
'\n' +
(isCommunity
? `!!! warning
This is an extension made by a community member — but not reviewed
by the GDevelop extension team. As such, we can't guarantee it
meets all the quality standards of official extensions. In case of
doubt, contact the author to know more about what the extension
does or inspect its content before using it.
\n\n`
: '') +
'---\n' +
'\n' +
convertCommonMarkdownToPythonMarkdown(extensionHeader.description) +
'\n' +
(extensionHeader.helpPath ? `\n[Read more...](${helpPageUrl})\n` : ``) +
generateExtensionFooterText(extensionHeader.fullName);
const folderName = getExtensionFolderName(extension.getName());
const extensionReferenceFilePath = path.join(
extensionsRootPath,
folderName,
@@ -165,26 +216,155 @@ const createExtensionReferencePage = async (extensionHeader, isCommunity) => {
};
/**
* Generate a section for an extension.
* @param {ExtensionHeader} extensionHeader The extension header
*
* @param {{ extension: any }} extension (gdPlatformExtension)
* @param {ExtensionShortHeader} extensionShortHeader
* @param {boolean} isCommunity
* @returns {RawText}
*/
const generateExtensionSection = extensionHeader => {
const folderName = getExtensionFolderName(extensionHeader.name);
const generateExtensionHeaderText = (
{ extension },
extensionShortHeader,
isCommunity
) => {
const folderName = getExtensionFolderName(extension.getName());
const referencePageUrl = `${gdevelopWikiUrlRoot}/extensions/${folderName}`;
const helpPageUrl = getHelpLink(extensionHeader.helpPath) || referencePageUrl;
const helpPageUrl = getHelpLink(extension.getHelpPath()) || referencePageUrl;
const authorNamesWithLinks = generateAuthorNamesWithLinks(
extensionShortHeader.authors || []
);
return `|${generateSvgImageIcon(extensionHeader.previewIconUrl)}|**${
extensionHeader.fullName
}**|${extensionHeader.shortDescription}|${`[Read more...](${helpPageUrl})` +
return {
text:
`# ${extension.getFullName()}` +
'\n\n' +
generateSvgImageIcon(extensionShortHeader.previewIconUrl) +
'\n' +
`${extensionShortHeader.shortDescription}\n` +
'\n' +
`**Authors and contributors** to this community extension: ${authorNamesWithLinks}.\n` +
'\n' +
(isCommunity
? `!!! warning
This is an extension made by a community member — but not reviewed
by the GDevelop extension team. As such, we can't guarantee it
meets all the quality standards of official extensions. In case of
doubt, contact the author to know more about what the extension
does or inspect its content before using it.
\n\n`
: '') +
'---\n' +
'\n' +
convertCommonMarkdownToPythonMarkdown(extension.getDescription()) +
'\n' +
(extension.getHelpPath() ? `\n[Read more...](${helpPageUrl})\n` : ``) +
'\n' +
`!!! tip
Learn [how to install new extensions](/gdevelop5/extensions/search) by following a step-by-step guide.` +
'\n',
};
};
/** @returns {RawText} */
const generateExtensionFooterText = ({ extension }) => {
return {
text:
`
---
*This page is an auto-generated reference page about the **${extension.getFullName()}** extension, made by the community of [GDevelop, the open-source, cross-platform game engine designed for everyone](https://gdevelop.io/).*` +
' ' +
'Learn more about [all GDevelop community-made extensions here](/gdevelop5/extensions).',
};
};
/**
* Generate the metadata for the events based extension
* @param {any} project A project containing of the extensions (gdProject)
* @param {any} eventsFunctionsExtension An extension (gdEventsFunctionsExtension)
* @returns {any} the extension metadata (gdPlatformExtension)
*/
const generateEventsFunctionExtensionMetadata = (
gd,
project,
eventsFunctionsExtension
) => {
const extension = new gd.PlatformExtension();
gd.MetadataDeclarationHelper.declareExtension(
extension,
eventsFunctionsExtension
);
// Generate all behaviors and their functions
mapVector(
eventsFunctionsExtension.getEventsBasedBehaviors(),
eventsBasedBehavior => {
const behaviorMethodMangledNames = new gd.MapStringString();
gd.MetadataDeclarationHelper.generateBehaviorMetadata(
project,
extension,
eventsFunctionsExtension,
eventsBasedBehavior,
behaviorMethodMangledNames
);
behaviorMethodMangledNames.delete();
}
);
// Generate all objects and their functions
mapVector(
eventsFunctionsExtension.getEventsBasedObjects(),
eventsBasedObject => {
const objectMethodMangledNames = new gd.MapStringString();
gd.MetadataDeclarationHelper.generateObjectMetadata(
project,
extension,
eventsFunctionsExtension,
eventsBasedObject,
objectMethodMangledNames
);
objectMethodMangledNames.delete();
}
);
// Generate all free functions
const metadataDeclarationHelper = new gd.MetadataDeclarationHelper();
mapFor(0, eventsFunctionsExtension.getEventsFunctionsCount(), i => {
const eventsFunction = eventsFunctionsExtension.getEventsFunctionAt(i);
metadataDeclarationHelper.generateFreeFunctionMetadata(
project,
extension,
eventsFunctionsExtension,
eventsFunction
);
});
metadataDeclarationHelper.delete();
return extension;
};
/**
* Generate a section for an extension.
* @param {any} extension The extension (gdEventsFunctionsExtension)
*/
const generateExtensionSection = extension => {
const folderName = getExtensionFolderName(extension.getName());
const referencePageUrl = `${gdevelopWikiUrlRoot}/extensions/${folderName}`;
const helpPageUrl = getHelpLink(extension.getHelpPath()) || referencePageUrl;
return `|${generateSvgImageIcon(
extension.getPreviewIconUrl()
)}|**${extension.getFullName()}**|${extension.getShortDescription()}|${`[Read more...](${helpPageUrl})` +
(helpPageUrl !== referencePageUrl
? ` ([reference](${referencePageUrl}))`
: '')}|\n`;
};
const generateAllExtensionsSections = extensionShortHeaders => {
/**
* @param {Array<any>} extensions The extension (gdEventsFunctionsExtension)
*/
const generateAllExtensionsSections = extensions => {
let extensionSectionsContent = '';
const extensionsByCategory = sortKeys(
groupBy(extensionShortHeaders, pair => pair.category || 'General')
groupBy(extensions, pair => pair.getCategory() || 'General')
);
for (const category in extensionsByCategory) {
const extensions = extensionsByCategory[category];
@@ -193,15 +373,15 @@ const generateAllExtensionsSections = extensionShortHeaders => {
extensionSectionsContent += '||Name|Description||\n';
extensionSectionsContent += '|---|---|---|---|\n';
for (const extensionHeader of extensions) {
extensionSectionsContent += generateExtensionSection(extensionHeader);
for (const extension of extensions) {
extensionSectionsContent += generateExtensionSection(extension);
}
extensionSectionsContent += '\n';
}
return extensionSectionsContent;
};
const generateExtensionsList = async extensionShortHeaders => {
const generateExtensionsList = async gd => {
let content = `## Extensions list
Here are listed all the extensions available in GDevelop. The list is divided in [two tiers](/gdevelop5/extensions/tiers/):
@@ -210,19 +390,36 @@ Here are listed all the extensions available in GDevelop. The list is divided in
- [Community extensions](#community-extensions)
`;
const extensionHeaders = await getAllExtensionHeaders();
const reviewedExtensionHeaders = extensionHeaders.filter(
const project = new gd.ProjectHelper.createNewGDJSProject();
const extensions = await addAllExtensionsToProject(gd, project);
const extensionShortHeaders = await getAllExtensionShortHeaders();
const reviewedExtensions = extensions.filter(
pair => pair.tier !== 'community'
);
const communityExtensionHeaders = extensionHeaders.filter(
const communityExtensions = extensions.filter(
pair => pair.tier === 'community'
);
content += '## Reviewed extensions\n\n';
for (const extensionHeader of reviewedExtensionHeaders) {
await createExtensionReferencePage(extensionHeader, false);
for (const extension of reviewedExtensions) {
const extensionShortHeader = extensionShortHeaders.find(
header => header.name === extension.getName()
);
if (!extensionShortHeader) {
throw new Error(
`Could not find header for extension: ${extension.getName()}`
);
}
await createExtensionReferencePage(
gd,
project,
extension,
extensionShortHeader,
false
);
}
content += generateAllExtensionsSections(reviewedExtensionHeaders);
content += generateAllExtensionsSections(reviewedExtensions);
content += `## Community extensions
@@ -233,14 +430,29 @@ doubt, contact the author to know more about what the extension
does or inspect its content before using it.
`;
for (const extensionHeader of communityExtensionHeaders) {
await createExtensionReferencePage(extensionHeader, true);
for (const extension of communityExtensions) {
const extensionShortHeader = extensionShortHeaders.find(
header => header.name === extension.getName()
);
if (!extensionShortHeader) {
throw new Error(
`Could not find header for extension: ${extension.getName()}`
);
}
await createExtensionReferencePage(
gd,
project,
extension,
extensionShortHeader,
true
);
}
content += generateAllExtensionsSections(communityExtensionHeaders);
content += generateAllExtensionsSections(communityExtensions);
project.delete();
return content;
};
(async () => {
initializeGDevelopJs().then(async gd => {
try {
console.info(` Loading all community extensions...`);
@@ -259,7 +471,7 @@ Read more about this:
`;
indexPageContent += await generateExtensionsList();
indexPageContent += await generateExtensionsList(gd);
try {
await fs.mkdir(path.dirname(extensionsMainFilePath), { recursive: true });
@@ -282,4 +494,4 @@ Read more about this:
console.error('❌ Error while fetching data', err);
shell.exit(1);
}
})();
});

View File

@@ -27,66 +27,22 @@ const expressionsFilePath = path.join(
allFeaturesRootPath,
'expressions-reference.md'
);
const {
generateExtensionReference,
generateExtensionRawText,
rawTextsToString,
generateExpressionsTableHeader,
generateObjectHeaderText,
generateObjectNoExpressionsText,
generateBehaviorHeaderText,
generateBehaviorNoExpressionsText,
generateHeader,
} = require('./lib/ExtensionReferenceGenerator');
// Types definitions used in this script:
/**
* @typedef {Object} RawText A text to be shown on a page
* @prop {string} text The text to render (in Markdown/Dokuwiki syntax)
*/
/**
* @typedef {Object} ReferenceText A text with metadata to format/manipulate/order it.
* @prop {string} orderKey The type of the expression, instruction, or anything that help to uniquely order this text.
* @prop {string} text The text to render (in Markdown/Dokuwiki syntax)
*/
/**
* @typedef {Object} ObjectReference
* @prop {any} objectMetadata The object.
* @prop {Array<ReferenceText>} actionsReferenceTexts Reference texts for the object actions.
* @prop {Array<ReferenceText>} conditionsReferenceTexts Reference texts for the object conditions.
* @prop {Array<ReferenceText>} expressionsReferenceTexts Reference texts for the object expressions.
*/
/**
* @typedef {Object} BehaviorReference
* @prop {any} behaviorMetadata The behavior.
* @prop {Array<ReferenceText>} actionsReferenceTexts Reference texts for the behavior actions.
* @prop {Array<ReferenceText>} conditionsReferenceTexts Reference texts for the behavior conditions.
* @prop {Array<ReferenceText>} expressionsReferenceTexts Reference texts for the behavior expressions.
*/
/**
* @typedef {Object} ExtensionReference
* @prop {any} extension The extension.
* @prop {Array<ReferenceText>} freeExpressionsReferenceTexts Reference texts for free expressions.
* @prop {Array<ReferenceText>} freeActionsReferenceTexts Reference texts for free actions.
* @prop {Array<ReferenceText>} freeConditionsReferenceTexts Reference texts for free conditions.
* @prop {Array<ObjectReference>} objectReferences Reference of all extension objects.
* @prop {Array<BehaviorReference>} behaviorReferences Reference of all extension behaviors.
*/
const sanitizeExpressionDescription = str => {
return (
str
// Disallow new lines in descriptions:
.replace(/\n/g, '')
// Replace a few description parts that can conflict with DokuWiki/Markdown:
.replace(/\)\*x/, ') * x')
.replace(/x\^n/, '"x to the power n"')
);
};
const translateTypeToHumanReadableType = type => {
if (type === 'expression') return 'number';
if (type === 'objectList') return 'object';
if (type === 'objectPtr') return 'object';
if (type === 'stringWithSelector') return 'string';
return type;
};
/** @typedef {import("./lib/ExtensionReferenceGenerator.js").RawText} RawText */
/** @typedef {import("./lib/ExtensionReferenceGenerator.js").ExtensionReference} ExtensionReference */
/** @returns {RawText} */
const generateFileHeaderText = () => {
return {
text: `# Expressions reference
@@ -108,29 +64,6 @@ object or behavior they belong too. When \`Object\` is written, you should enter
};
};
/** @returns {RawText} */
const generateExtensionHeaderText = ({ extension, depth }) => {
return {
text:
generateHeader({ headerName: extension.getFullName(), depth }).text +
`
${extension.getDescription()} ${generateReadMoreLink(extension.getHelpPath())}
`,
};
};
/** @returns {RawText} */
const generateExtensionFooterText = ({ extension }) => {
return {
text:
`
---
*This page is an auto-generated reference page about the **${extension.getFullName()}** feature of [GDevelop, the open-source, cross-platform game engine designed for everyone](https://gdevelop.io/).*` +
' ' +
'Learn more about [all GDevelop features here](/gdevelop5/all-features).',
};
};
/** @returns {RawText} */
const generateExtensionSeparatorText = () => {
return {
@@ -138,258 +71,6 @@ const generateExtensionSeparatorText = () => {
};
};
/** @returns {RawText} */
const generateObjectHeaderText = ({
extension,
objectMetadata,
showExtensionName,
showHelpLink,
}) => {
// Skip the header for the base object. The "Base object" extension
// will already have an header and explanation.
if (objectMetadata.getName() === '') {
return { text: '' };
}
const additionalText =
showExtensionName &&
extension.getFullName() !== objectMetadata.getFullName()
? `(from extension ${extension.getFullName()})`
: '';
const helpPath = showHelpLink
? objectMetadata.getHelpPath() || extension.getHelpPath()
: '';
return {
text: `
## ${objectMetadata.getFullName()} ${additionalText}
${objectMetadata.getDescription()} ${generateReadMoreLink(helpPath)}
`,
};
};
/** @returns {RawText} */
const generateBehaviorHeaderText = ({
extension,
behaviorMetadata,
showExtensionName,
showHelpLink,
}) => {
const additionalText =
showExtensionName &&
extension.getFullName() !== behaviorMetadata.getFullName()
? `(from extension ${extension.getFullName()})`
: '';
const helpPath = showHelpLink
? behaviorMetadata.getHelpPath() || extension.getHelpPath()
: '';
return {
text: `
## ${behaviorMetadata.getFullName()} ${additionalText}
${behaviorMetadata.getDescription()} ${generateReadMoreLink(helpPath)}
`,
};
};
/** @returns {RawText} */
const generateObjectNoExpressionsText = () => {
return {
text: `_No expressions for this object._\n`,
};
};
/** @returns {RawText} */
const generateBehaviorNoExpressionsText = () => {
return {
text: `_No expressions for this behavior._\n`,
};
};
/**
* @param {?{ headerName: string, depth: number }} headerOptions
* @returns {RawText}
*/
const generateExpressionsTableHeader = headerOptions => {
// We don't put a header for the last column
const text =
(headerOptions ? generateHeader(headerOptions).text + '\n' : '') +
`| Expression | Description | |
|-----|-----|-----|`;
return {
text,
};
};
/**
* @param {{ headerName: string, depth: number }} headerOptions
* @returns {RawText}
*/
const generateHeader = ({ headerName, depth }) => {
const markdownHeaderMark = Array(depth)
.fill('#')
.join('');
return {
text: `${markdownHeaderMark} ${headerName}\n`,
};
};
/** @returns {ReferenceText} */
const generateInstructionReferenceRowsText = ({
instructionType,
instructionMetadata,
isCondition,
objectMetadata,
behaviorMetadata,
}) => {
return {
orderKey: instructionType,
text:
'**' +
instructionMetadata.getFullName() +
'** ' +
'\n' +
instructionMetadata.getDescription().replace(/\n/, ' \n') +
'\n',
};
};
/** @returns {ReferenceText} */
const generateExpressionReferenceRowsText = ({
expressionType,
expressionMetadata,
objectMetadata,
behaviorMetadata,
}) => {
let parameterRows = [];
let parameterStrings = [];
mapVector(expressionMetadata.getParameters(), (parameterMetadata, index) => {
if ((!!objectMetadata && index < 1) || (!!behaviorMetadata && index < 2)) {
return; // Skip the first (or first twos) parameters by convention.
}
if (parameterMetadata.isCodeOnly()) return;
const sanitizedDescription = sanitizeExpressionDescription(
[
parameterMetadata.getDescription(),
parameterMetadata.getLongDescription(),
parameterMetadata.isOptional() ? '_Optional_.' : '',
]
.filter(Boolean)
.join(' ')
);
const humanReadableType = translateTypeToHumanReadableType(
parameterMetadata.getType()
);
parameterRows.push(
`| | _${humanReadableType}_ | ${sanitizedDescription} |`
);
parameterStrings.push(humanReadableType);
});
let expressionUsage = '';
if (objectMetadata) {
expressionUsage = 'Object.' + expressionType;
} else if (behaviorMetadata) {
expressionUsage =
'Object.' + behaviorMetadata.getDefaultName() + '::' + expressionType;
} else {
expressionUsage = expressionType;
}
expressionUsage += '(' + parameterStrings.join(', ') + ')';
const sanitizedExpression = sanitizeExpressionDescription(
expressionMetadata.getDescription()
);
let text = `| \`${expressionUsage}\` | ${sanitizedExpression} ||`;
if (parameterRows.length) {
text += '\n' + parameterRows.join('\n');
}
return {
orderKey: expressionType,
text,
};
};
/**
* @param {{ instructionsMetadata?: any, areConditions: boolean, objectMetadata?: any, behaviorMetadata?: any }} metadata
* @returns {Array<ReferenceText>}
*/
const generateInstructionsReferenceRowsTexts = ({
instructionsMetadata,
areConditions,
objectMetadata,
behaviorMetadata,
}) => {
/** @type {Array<string>} */
const instructionTypes = instructionsMetadata.keys().toJSArray();
// @ts-ignore
return instructionTypes
.map(instructionType => {
const instructionMetadata = instructionsMetadata.get(instructionType);
if (instructionMetadata.isHidden()) return null;
return generateInstructionReferenceRowsText({
instructionType,
instructionMetadata,
isCondition: areConditions,
objectMetadata,
behaviorMetadata,
});
})
.filter(Boolean);
};
/**
* @param {{ expressionsMetadata?: any, objectMetadata?: any, behaviorMetadata?: any }} metadata
* @returns {Array<ReferenceText>}
*/
const generateExpressionsReferenceRowsTexts = ({
expressionsMetadata,
objectMetadata,
behaviorMetadata,
}) => {
/** @type {Array<string>} */
const expressionTypes = expressionsMetadata.keys().toJSArray();
// @ts-ignore
return expressionTypes
.map(expressionType => {
const expressionMetadata = expressionsMetadata.get(expressionType);
if (!expressionMetadata.isShown()) return null;
return generateExpressionReferenceRowsText({
expressionType,
expressionMetadata,
objectMetadata,
behaviorMetadata,
});
})
.filter(Boolean);
};
/**
* @param {ReferenceText} referenceText1
* @param {ReferenceText} referenceText2
*/
const sortReferenceTexts = (referenceText1, referenceText2) => {
if (referenceText1.orderKey > referenceText2.orderKey) {
return 1;
} else if (referenceText1.orderKey < referenceText2.orderKey) {
return -1;
}
return 0;
};
/**
* @type {any} gd
* @returns {Array<ExtensionReference>}
@@ -400,132 +81,7 @@ const generateAllExtensionReferences = gd => {
/** @type {Array<ExtensionReference>} */
const extensionReferences = mapVector(
platformExtensions,
/** @returns {ExtensionReference} */
extension => {
const extensionExpressions = extension.getAllExpressions();
const extensionStrExpressions = extension.getAllStrExpressions();
/** @type {Array<string>} */
const objectTypes = extension.getExtensionObjectsTypes().toJSArray();
/** @type {Array<string>} */
const behaviorTypes = extension.getBehaviorsTypes().toJSArray();
// Object expressions
/** @type {Array<ObjectReference>} */
let objectReferences = objectTypes.map(objectType => {
const objectMetadata = extension.getObjectMetadata(objectType);
const actionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: false,
instructionsMetadata: extension.getAllActionsForObject(objectType),
objectMetadata,
});
const conditionsReferenceTexts = generateInstructionsReferenceRowsTexts(
{
areConditions: true,
instructionsMetadata: extension.getAllConditionsForObject(
objectType
),
objectMetadata,
}
);
const expressionsReferenceTexts = [
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extension.getAllExpressionsForObject(
objectType
),
objectMetadata,
}),
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extension.getAllStrExpressionsForObject(
objectType
),
objectMetadata,
}),
];
expressionsReferenceTexts.sort(sortReferenceTexts);
return {
objectMetadata,
actionsReferenceTexts,
conditionsReferenceTexts,
expressionsReferenceTexts,
};
});
// Behavior expressions
/** @type {Array<BehaviorReference>} */
let behaviorReferences = behaviorTypes.map(behaviorType => {
const behaviorMetadata = extension.getBehaviorMetadata(behaviorType);
const actionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: false,
instructionsMetadata: extension.getAllActionsForBehavior(
behaviorType
),
behaviorMetadata,
});
const conditionsReferenceTexts = generateInstructionsReferenceRowsTexts(
{
areConditions: true,
instructionsMetadata: extension.getAllConditionsForBehavior(
behaviorType
),
behaviorMetadata,
}
);
const expressionsReferenceTexts = [
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extension.getAllExpressionsForBehavior(
behaviorType
),
behaviorMetadata,
}),
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extension.getAllStrExpressionsForBehavior(
behaviorType
),
behaviorMetadata,
}),
];
expressionsReferenceTexts.sort(sortReferenceTexts);
return {
behaviorMetadata,
actionsReferenceTexts,
conditionsReferenceTexts,
expressionsReferenceTexts,
};
});
// Free (non objects/non behaviors) actions/conditions/expressions
const freeActionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: false,
instructionsMetadata: extension.getAllActions(),
});
const freeConditionsReferenceTexts = generateInstructionsReferenceRowsTexts(
{
areConditions: true,
instructionsMetadata: extension.getAllConditions(),
}
);
const freeExpressionsReferenceTexts = [
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extensionStrExpressions,
}),
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extensionExpressions,
}),
];
freeExpressionsReferenceTexts.sort(sortReferenceTexts);
return {
extension,
freeActionsReferenceTexts,
freeConditionsReferenceTexts,
freeExpressionsReferenceTexts,
objectReferences,
behaviorReferences,
};
}
generateExtensionReference
);
return extensionReferences;
@@ -588,6 +144,29 @@ Remember that you can also [search for new features in the community extensions]
];
};
/** @returns {RawText} */
const generateExtensionHeaderText = ({ extension, depth }) => {
return {
text:
generateHeader({ headerName: extension.getFullName(), depth }).text +
`
${extension.getDescription()} ${generateReadMoreLink(extension.getHelpPath())}
`,
};
};
/** @returns {RawText} */
const generateExtensionFooterText = ({ extension }) => {
return {
text:
`
---
*This page is an auto-generated reference page about the **${extension.getFullName()}** feature of [GDevelop, the open-source, cross-platform game engine designed for everyone](https://gdevelop.io/).*` +
' ' +
'Learn more about [all GDevelop features here](/gdevelop5/all-features).',
};
};
/**
* @param {Array<ExtensionReference>} extensionReferences
* @returns {{allExtensionRawTexts: Array<{folderName: string, texts: Array<RawText>}>}}
@@ -600,106 +179,15 @@ const generateExtensionRawTexts = extensionReferences => {
);
})
.map(extensionReference => {
const {
extension,
freeActionsReferenceTexts,
freeConditionsReferenceTexts,
freeExpressionsReferenceTexts,
objectReferences,
behaviorReferences,
} = extensionReference;
const withHeaderIfNotEmpty = (texts, { headerName, depth }) => {
if (!texts.length) return [];
return [generateHeader({ headerName, depth }), ...texts];
};
return {
folderName: getExtensionFolderName(
extensionReference.extension.getName()
),
texts: [
generateExtensionHeaderText({ extension, depth: 1 }),
...withHeaderIfNotEmpty(freeActionsReferenceTexts, {
headerName: 'Actions',
depth: 2,
}),
...withHeaderIfNotEmpty(freeConditionsReferenceTexts, {
headerName: 'Conditions',
depth: 2,
}),
freeExpressionsReferenceTexts.length
? generateExpressionsTableHeader({
headerName: 'Expressions',
depth: 2,
})
: { text: '' },
...freeExpressionsReferenceTexts,
...objectReferences.flatMap(objectReference => {
const {
objectMetadata,
actionsReferenceTexts,
conditionsReferenceTexts,
expressionsReferenceTexts,
} = objectReference;
return [
generateObjectHeaderText({
extension,
objectMetadata,
showExtensionName: false,
showHelpLink: false,
}),
...withHeaderIfNotEmpty(actionsReferenceTexts, {
headerName: 'Object actions',
depth: 3,
}),
...withHeaderIfNotEmpty(conditionsReferenceTexts, {
headerName: 'Object conditions',
depth: 3,
}),
expressionsReferenceTexts.length
? generateExpressionsTableHeader({
headerName: 'Object expressions',
depth: 3,
})
: generateObjectNoExpressionsText(),
...expressionsReferenceTexts,
];
}),
...behaviorReferences.flatMap(behaviorReference => {
const {
behaviorMetadata,
actionsReferenceTexts,
conditionsReferenceTexts,
expressionsReferenceTexts,
} = behaviorReference;
return [
generateBehaviorHeaderText({
extension,
behaviorMetadata,
showExtensionName: false,
showHelpLink: false,
}),
...withHeaderIfNotEmpty(actionsReferenceTexts, {
headerName: 'Behavior actions',
depth: 3,
}),
...withHeaderIfNotEmpty(conditionsReferenceTexts, {
headerName: 'Behavior conditions',
depth: 3,
}),
expressionsReferenceTexts.length
? generateExpressionsTableHeader({
headerName: 'Behavior expressions',
depth: 3,
})
: generateBehaviorNoExpressionsText(),
...expressionsReferenceTexts,
];
}),
generateExtensionFooterText({ extension }),
].filter(Boolean),
texts: generateExtensionRawText(
extensionReference,
generateExtensionHeaderText,
generateExtensionFooterText
),
};
});
@@ -788,13 +276,6 @@ const generateAllExpressionsRawTexts = extensionReferences => {
const noopTranslationFunction = str => str;
const rawTextsToString = rawTexts =>
rawTexts
.map(({ text }) => {
return text;
})
.join('\n');
initializeGDevelopJs().then(async gd => {
try {
// @ts-ignore - not passing onFindGDJS - is it still useful?

View File

@@ -0,0 +1,558 @@
// @ts-check
const { mapVector } = require('./MapFor');
const { generateReadMoreLink } = require('./WikiHelpLink');
// Types definitions used in this script:
/**
* @typedef {Object} RawText A text to be shown on a page
* @prop {string} text The text to render (in Markdown/Dokuwiki syntax)
*/
/**
* @typedef {Object} ReferenceText A text with metadata to format/manipulate/order it.
* @prop {string} orderKey The type of the expression, instruction, or anything that help to uniquely order this text.
* @prop {string} text The text to render (in Markdown/Dokuwiki syntax)
*/
/**
* @typedef {Object} ObjectReference
* @prop {any} objectMetadata The object.
* @prop {Array<ReferenceText>} actionsReferenceTexts Reference texts for the object actions.
* @prop {Array<ReferenceText>} conditionsReferenceTexts Reference texts for the object conditions.
* @prop {Array<ReferenceText>} expressionsReferenceTexts Reference texts for the object expressions.
*/
/**
* @typedef {Object} BehaviorReference
* @prop {any} behaviorMetadata The behavior.
* @prop {Array<ReferenceText>} actionsReferenceTexts Reference texts for the behavior actions.
* @prop {Array<ReferenceText>} conditionsReferenceTexts Reference texts for the behavior conditions.
* @prop {Array<ReferenceText>} expressionsReferenceTexts Reference texts for the behavior expressions.
*/
/**
* @typedef {Object} ExtensionReference
* @prop {any} extension The extension.
* @prop {Array<ReferenceText>} freeExpressionsReferenceTexts Reference texts for free expressions.
* @prop {Array<ReferenceText>} freeActionsReferenceTexts Reference texts for free actions.
* @prop {Array<ReferenceText>} freeConditionsReferenceTexts Reference texts for free conditions.
* @prop {Array<ObjectReference>} objectReferences Reference of all extension objects.
* @prop {Array<BehaviorReference>} behaviorReferences Reference of all extension behaviors.
*/
/** @returns {RawText} */
const generateObjectNoExpressionsText = () => {
return {
text: `_No expressions for this object._\n`,
};
};
/** @returns {RawText} */
const generateBehaviorNoExpressionsText = () => {
return {
text: `_No expressions for this behavior._\n`,
};
};
/** @returns {RawText} */
const generateBehaviorHeaderText = ({
extension,
behaviorMetadata,
showExtensionName,
showHelpLink,
}) => {
const additionalText =
showExtensionName &&
extension.getFullName() !== behaviorMetadata.getFullName()
? `(from extension ${extension.getFullName()})`
: '';
const helpPath = showHelpLink
? behaviorMetadata.getHelpPath() || extension.getHelpPath()
: '';
return {
text: `
## ${behaviorMetadata.getFullName()} ${additionalText}
${behaviorMetadata.getDescription()} ${generateReadMoreLink(helpPath)}
`,
};
};
/** @returns {RawText} */
const generateObjectHeaderText = ({
extension,
objectMetadata,
showExtensionName,
showHelpLink,
}) => {
// Skip the header for the base object. The "Base object" extension
// will already have an header and explanation.
if (objectMetadata.getName() === '') {
return { text: '' };
}
const additionalText =
showExtensionName &&
extension.getFullName() !== objectMetadata.getFullName()
? `(from extension ${extension.getFullName()})`
: '';
const helpPath = showHelpLink
? objectMetadata.getHelpPath() || extension.getHelpPath()
: '';
return {
text: `
## ${objectMetadata.getFullName()} ${additionalText}
${objectMetadata.getDescription()} ${generateReadMoreLink(helpPath)}
`,
};
};
/**
* @param {?{ headerName: string, depth: number }} headerOptions
* @returns {RawText}
*/
const generateExpressionsTableHeader = headerOptions => {
// We don't put a header for the last column
const text =
(headerOptions ? generateHeader(headerOptions).text + '\n' : '') +
`| Expression | Description | |
|-----|-----|-----|`;
return {
text,
};
};
/**
* @param {{ headerName: string, depth: number }} headerOptions
* @returns {RawText}
*/
const generateHeader = ({ headerName, depth }) => {
const markdownHeaderMark = Array(depth)
.fill('#')
.join('');
return {
text: `${markdownHeaderMark} ${headerName}\n`,
};
};
const rawTextsToString = rawTexts =>
rawTexts
.map(({ text }) => {
return text;
})
.join('\n');
const sanitizeExpressionDescription = str => {
return (
str
// Disallow new lines in descriptions:
.replace(/\n/g, '')
// Replace a few description parts that can conflict with DokuWiki/Markdown:
.replace(/\)\*x/, ') * x')
.replace(/x\^n/, '"x to the power n"')
);
};
const translateTypeToHumanReadableType = type => {
if (type === 'expression') return 'number';
if (type === 'objectList') return 'object';
if (type === 'objectPtr') return 'object';
if (type === 'stringWithSelector') return 'string';
return type;
};
/** @returns {ReferenceText} */
const generateInstructionReferenceRowsText = ({
instructionType,
instructionMetadata,
isCondition,
objectMetadata,
behaviorMetadata,
}) => {
return {
orderKey: instructionType,
text:
'**' +
instructionMetadata.getFullName() +
'** ' +
'\n' +
instructionMetadata.getDescription().replace(/\n/, ' \n') +
'\n',
};
};
/** @returns {ReferenceText} */
const generateExpressionReferenceRowsText = ({
expressionType,
expressionMetadata,
objectMetadata,
behaviorMetadata,
}) => {
let parameterRows = [];
let parameterStrings = [];
mapVector(expressionMetadata.getParameters(), (parameterMetadata, index) => {
if ((!!objectMetadata && index < 1) || (!!behaviorMetadata && index < 2)) {
return; // Skip the first (or first twos) parameters by convention.
}
if (parameterMetadata.isCodeOnly()) return;
const sanitizedDescription = sanitizeExpressionDescription(
[
parameterMetadata.getDescription(),
parameterMetadata.getLongDescription(),
parameterMetadata.isOptional() ? '_Optional_.' : '',
]
.filter(Boolean)
.join(' ')
);
const humanReadableType = translateTypeToHumanReadableType(
parameterMetadata.getType()
);
parameterRows.push(
`| | _${humanReadableType}_ | ${sanitizedDescription} |`
);
parameterStrings.push(humanReadableType);
});
let expressionUsage = '';
if (objectMetadata) {
expressionUsage = 'Object.' + expressionType;
} else if (behaviorMetadata) {
expressionUsage =
'Object.' + behaviorMetadata.getDefaultName() + '::' + expressionType;
} else {
expressionUsage = expressionType;
}
expressionUsage += '(' + parameterStrings.join(', ') + ')';
const sanitizedExpression = sanitizeExpressionDescription(
expressionMetadata.getDescription()
);
let text = `| \`${expressionUsage}\` | ${sanitizedExpression} ||`;
if (parameterRows.length) {
text += '\n' + parameterRows.join('\n');
}
return {
orderKey: expressionType,
text,
};
};
/**
* @param {{ instructionsMetadata?: any, areConditions: boolean, objectMetadata?: any, behaviorMetadata?: any }} metadata
* @returns {Array<ReferenceText>}
*/
const generateInstructionsReferenceRowsTexts = ({
instructionsMetadata,
areConditions,
objectMetadata,
behaviorMetadata,
}) => {
/** @type {Array<string>} */
const instructionTypes = instructionsMetadata.keys().toJSArray();
// @ts-ignore
return instructionTypes
.map(instructionType => {
const instructionMetadata = instructionsMetadata.get(instructionType);
if (instructionMetadata.isHidden()) return null;
return generateInstructionReferenceRowsText({
instructionType,
instructionMetadata,
isCondition: areConditions,
objectMetadata,
behaviorMetadata,
});
})
.filter(Boolean);
};
/**
* @param {{ expressionsMetadata?: any, objectMetadata?: any, behaviorMetadata?: any }} metadata
* @returns {Array<ReferenceText>}
*/
const generateExpressionsReferenceRowsTexts = ({
expressionsMetadata,
objectMetadata,
behaviorMetadata,
}) => {
/** @type {Array<string>} */
const expressionTypes = expressionsMetadata.keys().toJSArray();
// @ts-ignore
return expressionTypes
.map(expressionType => {
const expressionMetadata = expressionsMetadata.get(expressionType);
if (!expressionMetadata.isShown()) return null;
return generateExpressionReferenceRowsText({
expressionType,
expressionMetadata,
objectMetadata,
behaviorMetadata,
});
})
.filter(Boolean);
};
/**
* @param {ReferenceText} referenceText1
* @param {ReferenceText} referenceText2
*/
const sortReferenceTexts = (referenceText1, referenceText2) => {
if (referenceText1.orderKey > referenceText2.orderKey) {
return 1;
} else if (referenceText1.orderKey < referenceText2.orderKey) {
return -1;
}
return 0;
};
/**
* @type {any} platformExtension
* @returns {ExtensionReference}
*/
const generateExtensionReference = extension => {
const extensionExpressions = extension.getAllExpressions();
const extensionStrExpressions = extension.getAllStrExpressions();
/** @type {Array<string>} */
const objectTypes = extension.getExtensionObjectsTypes().toJSArray();
/** @type {Array<string>} */
const behaviorTypes = extension.getBehaviorsTypes().toJSArray();
// Object expressions
/** @type {Array<ObjectReference>} */
let objectReferences = objectTypes.map(objectType => {
const objectMetadata = extension.getObjectMetadata(objectType);
const actionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: false,
instructionsMetadata: extension.getAllActionsForObject(objectType),
objectMetadata,
});
const conditionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: true,
instructionsMetadata: extension.getAllConditionsForObject(objectType),
objectMetadata,
});
const expressionsReferenceTexts = [
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extension.getAllExpressionsForObject(objectType),
objectMetadata,
}),
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extension.getAllStrExpressionsForObject(
objectType
),
objectMetadata,
}),
];
expressionsReferenceTexts.sort(sortReferenceTexts);
return {
objectMetadata,
actionsReferenceTexts,
conditionsReferenceTexts,
expressionsReferenceTexts,
};
});
// Behavior expressions
/** @type {Array<BehaviorReference>} */
let behaviorReferences = behaviorTypes.map(behaviorType => {
const behaviorMetadata = extension.getBehaviorMetadata(behaviorType);
const actionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: false,
instructionsMetadata: extension.getAllActionsForBehavior(behaviorType),
behaviorMetadata,
});
const conditionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: true,
instructionsMetadata: extension.getAllConditionsForBehavior(behaviorType),
behaviorMetadata,
});
const expressionsReferenceTexts = [
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extension.getAllExpressionsForBehavior(
behaviorType
),
behaviorMetadata,
}),
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extension.getAllStrExpressionsForBehavior(
behaviorType
),
behaviorMetadata,
}),
];
expressionsReferenceTexts.sort(sortReferenceTexts);
return {
behaviorMetadata,
actionsReferenceTexts,
conditionsReferenceTexts,
expressionsReferenceTexts,
};
});
// Free (non objects/non behaviors) actions/conditions/expressions
const freeActionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: false,
instructionsMetadata: extension.getAllActions(),
});
const freeConditionsReferenceTexts = generateInstructionsReferenceRowsTexts({
areConditions: true,
instructionsMetadata: extension.getAllConditions(),
});
const freeExpressionsReferenceTexts = [
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extensionStrExpressions,
}),
...generateExpressionsReferenceRowsTexts({
expressionsMetadata: extensionExpressions,
}),
];
freeExpressionsReferenceTexts.sort(sortReferenceTexts);
return {
extension,
freeActionsReferenceTexts,
freeConditionsReferenceTexts,
freeExpressionsReferenceTexts,
objectReferences,
behaviorReferences,
};
};
/**
* @param {ExtensionReference} extensionReference
* @param {({extension: gdPlatformExtension, depth: number}) => RawText} generateExtensionHeaderText
* @param {({extension: gdPlatformExtension}) => RawText} generateExtensionFooterText
* @returns {Array<RawText>}}}
*/
const generateExtensionRawText = (
extensionReference,
generateExtensionHeaderText,
generateExtensionFooterText
) => {
const {
extension,
freeActionsReferenceTexts,
freeConditionsReferenceTexts,
freeExpressionsReferenceTexts,
objectReferences,
behaviorReferences,
} = extensionReference;
const withHeaderIfNotEmpty = (texts, { headerName, depth }) => {
if (!texts.length) return [];
return [generateHeader({ headerName, depth }), ...texts];
};
return [
generateExtensionHeaderText({ extension, depth: 1 }),
...withHeaderIfNotEmpty(freeActionsReferenceTexts, {
headerName: 'Actions',
depth: 2,
}),
...withHeaderIfNotEmpty(freeConditionsReferenceTexts, {
headerName: 'Conditions',
depth: 2,
}),
freeExpressionsReferenceTexts.length
? generateExpressionsTableHeader({
headerName: 'Expressions',
depth: 2,
})
: { text: '' },
...freeExpressionsReferenceTexts,
...objectReferences.flatMap(objectReference => {
const {
objectMetadata,
actionsReferenceTexts,
conditionsReferenceTexts,
expressionsReferenceTexts,
} = objectReference;
return [
generateObjectHeaderText({
extension,
objectMetadata,
showExtensionName: false,
showHelpLink: false,
}),
...withHeaderIfNotEmpty(actionsReferenceTexts, {
headerName: 'Object actions',
depth: 3,
}),
...withHeaderIfNotEmpty(conditionsReferenceTexts, {
headerName: 'Object conditions',
depth: 3,
}),
expressionsReferenceTexts.length
? generateExpressionsTableHeader({
headerName: 'Object expressions',
depth: 3,
})
: generateObjectNoExpressionsText(),
...expressionsReferenceTexts,
];
}),
...behaviorReferences.flatMap(behaviorReference => {
const {
behaviorMetadata,
actionsReferenceTexts,
conditionsReferenceTexts,
expressionsReferenceTexts,
} = behaviorReference;
return [
generateBehaviorHeaderText({
extension,
behaviorMetadata,
showExtensionName: false,
showHelpLink: false,
}),
...withHeaderIfNotEmpty(actionsReferenceTexts, {
headerName: 'Behavior actions',
depth: 3,
}),
...withHeaderIfNotEmpty(conditionsReferenceTexts, {
headerName: 'Behavior conditions',
depth: 3,
}),
expressionsReferenceTexts.length
? generateExpressionsTableHeader({
headerName: 'Behavior expressions',
depth: 3,
})
: generateBehaviorNoExpressionsText(),
...expressionsReferenceTexts,
];
}),
generateExtensionFooterText({ extension }),
].filter(Boolean);
};
module.exports = {
rawTextsToString,
generateExtensionReference,
generateExtensionRawText,
generateExpressionsTableHeader,
generateObjectHeaderText,
generateObjectNoExpressionsText,
generateBehaviorHeaderText,
generateBehaviorNoExpressionsText,
generateHeader,
};