Files
GDevelop/GDevelop.js/scripts/generate-types.js
2026-03-18 15:42:36 +01:00

582 lines
21 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* This uses the IDL definitions (Bindings.idl),
* to generate types that can be consumed in the IDE codebase.
*
* Bindings.idl is also used by Emscripten
* to generate the JS interface to the WebAssembly compiled C++ code of GDCore/GDJS.
* The parsers and code generation have a few hacks (see postjs.js), so
* we need to also do some hacks/manipulation on the input and some resulting files.
*/
const shell = require('shelljs');
const path = require('path');
const fs = require('fs');
shell.cd(path.join(__dirname, '..'));
const idlTempFolder = fs.mkdtempSync('gdevelopjs-cleaned-bindings-idl');
// Remove some "extended attributes" from the webIDL definitions as they
// are not supported by the webidl2.js parser used by "webidl-tools".
shell.cp('-f', 'Bindings/Bindings.idl', idlTempFolder);
const idlFile = path.join(idlTempFolder, 'Bindings.idl');
shell.sed('-i', /\[Prefix="gdjs::"\]/, '//Removed gdjs prefix', idlFile);
shell.sed(
'-i',
/\[Prefix="gd::InstructionMetadata::"\]/,
'//Removed gd::InstructionMetadata prefix',
idlFile
);
// Don't remove the "types" folder because then Flow
// is not loading the files. This means a removed class must
// have its typing file be removed manually.
// shell.rm('-rf', 'types/');
// Run "webidl-tools flow" that will take care of converting
// the webIDL declarations to Flow classes.
const webidlToolsFlowResult = shell.exec(
`node node_modules/webidl-tools/bin/webidl-tools-flow --out types` +
` --module-name libGDevelop` +
// Prefix all classes by "gd", to easily recognise them
// in the IDE codebase:
` --prefix-interfaces gd` +
// Add "delete" method (see postjs.js) and "ptr"
// (as it's used in the codebase)
` --add-delete-operation` +
` --add-emscripten-ptr-attribute` +
// Transform functions from UpperCamelCase to lowerCamelCase.
` --uncapitalize-operations` +
// Static functions are prefixed
` --static-operation-prefix STATIC_` +
// Functions starting by WRAPPED_, MAP_, FREE_ will be
// stripped of their prefix (see postjs.js):
` --rename "s/WRAPPED_//" --rename "s/MAP_//" --rename "s/FREE_//"` +
// Functions starting by CLONE_ will be renamed to clone
// (see postjs.js and update-bindings.js)
` --rename "s/CLONE_.*/clone/"` +
` ${idlFile}`,
{},
(code, stdout, stderr) => {
fs.unlinkSync(idlFile);
fs.rmdirSync(idlTempFolder);
if (code !== 0 || stdout.length > 1000) {
shell.echo(
'❌ The output of "webidl-tools flow" is suspiciously long or errored. Considering as an error.'
);
shell.echo(
'️ Is Bindings.idl improperly formatted, or using a syntax not understood by "webidl-tools flow"?'
);
shell.exit(1);
}
fs.writeFileSync(
'types/other-adhoc-types.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
declare type gdSerializable = any;
declare type gdEmscriptenObject = {
ptr: number;
};
`
);
// Fix enums, that are numbers and can be accessed from their enclosing class.
fs.writeFileSync(
'types/eventsfunction_functiontype.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type EventsFunction_FunctionType = 0 | 1 | 2 | 3 | 4`
);
fs.writeFileSync(
'types/eventsfunctionscontainer_functionowner.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type EventsFunctionsContainer_FunctionOwner = 0 | 1 | 2`
);
shell.sed(
'-i',
'declare class gdEventsFunction {',
[
'declare class gdEventsFunction {',
' static Action: 0;',
' static Condition: 1;',
' static Expression: 2;',
' static ExpressionAndCondition: 3;',
' static ActionWithOperator: 4;',
].join('\n'),
'types/gdeventsfunction.js'
);
fs.writeFileSync(
'types/variable_type.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type Variable_Type = 0 | 1 | 2 | 3 | 4 | 5 | 6`
);
shell.sed(
'-i',
'declare class gdVariable {',
[
'declare class gdVariable {',
' static Unknown: 0;',
' static MixedTypes: 1;',
' static String: 2;',
' static Number: 3;',
' static Boolean: 4;',
' static Structure: 5;',
' static Array: 6;',
].join('\n'),
'types/gdvariable.js'
);
fs.writeFileSync(
'types/variablescontainer_sourcetype.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type VariablesContainer_SourceType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8`
);
shell.sed(
'-i',
'declare class gdVariablesContainer {',
[
'declare class gdVariablesContainer {',
' static Unknown: 0;',
' static Global: 1;',
' static Scene: 2;',
' static Object: 3;',
' static Local: 4;',
' static ExtensionGlobal: 5;',
' static ExtensionScene: 6;',
' static Parameters: 7;',
' static Properties: 8;',
].join('\n'),
'types/gdvariablescontainer.js'
);
fs.writeFileSync(
'types/resourcescontainer_sourcetype.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type ResourcesContainer_SourceType = 0 | 1 | 2 | 3`
);
shell.sed(
'-i',
'declare class gdResourcesContainer {',
[
'declare class gdResourcesContainer {',
' static Unknown: 0;',
' static Global: 1;',
' static Parameters: 2;',
' static Properties: 3;',
].join('\n'),
'types/gdresourcescontainer.js'
);
fs.writeFileSync(
'types/objectscontainer_sourcetype.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type ObjectsContainer_SourceType = 0 | 1 | 2 | 3 | 4`
);
shell.sed(
'-i',
'declare class gdObjectsContainer {',
[
'declare class gdObjectsContainer {',
' static Unknown: 0;',
' static Global: 1;',
' static Scene: 2;',
' static Object: 3;',
' static Function: 4;',
].join('\n'),
'types/gdobjectscontainer.js'
);
shell.sed(
'-i',
'declare class gdObjectsContainersList {',
[
'declare class gdObjectsContainersList {',
' static DoesNotExist: 0;',
' static Exists: 1;',
' static Number: 2;',
' static GroupIsEmpty: 3;',
' static ExistsOnlyOnSomeObjectsOfTheGroup: 4;',
].join('\n'),
'types/gdobjectscontainerslist.js'
);
fs.writeFileSync(
'types/projectdiagnostic_errortype.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type ProjectDiagnostic_ErrorType = 0 | 1 | 2 | 3`
);
shell.sed(
'-i',
'declare class gdProjectDiagnostic {',
[
'declare class gdProjectDiagnostic {',
' static UndeclaredVariable: 0;',
' static MissingBehavior: 1;',
' static UnknownObject: 2;',
' static MismatchedObjectType: 3;',
].join('\n'),
'types/gdprojectdiagnostic.js'
);
fs.writeFileSync(
'types/expressioncompletiondescription_completionkind.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type ExpressionCompletionDescription_CompletionKind = 0 | 1 | 2 | 3 | 4 | 5 | 6`
);
shell.sed(
'-i',
'declare class gdExpressionCompletionDescription {',
[
'declare class gdExpressionCompletionDescription {',
' static Object: 0;',
' static BehaviorWithPrefix: 1;',
' static ExpressionWithPrefix: 2;',
' static Variable: 3;',
' static TextWithPrefix: 4;',
' static Property: 5;',
' static Parameter: 6;',
].join('\n'),
'types/gdexpressioncompletiondescription.js'
);
fs.writeFileSync(
'types/expressionparsererror_errortype.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type ExpressionParserError_ErrorType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14
`
);
shell.sed(
'-i',
'declare class gdExpressionParserError {',
[
'declare class gdExpressionParserError {',
' static SyntaxError: 0;',
' static InvalidOperator: 1;',
' static MismatchedType: 2;',
' static UndeclaredVariable: 3;',
' static UnknownIdentifier: 4;',
' static BracketsNotAllowedForObjects: 5;',
' static TooFewParameters: 6;',
' static TooManyParameters: 7;',
' static InvalidFunctionName: 8;',
' static MalformedVariableParameter: 9;',
' static MalformedObjectParameter: 10;',
' static UnknownParameterType: 11;',
' static MissingBehavior: 12;',
' static VariableNameCollision: 13;',
' static DeprecatedExpression: 14;',
].join('\n'),
'types/gdexpressionparsererror.js'
);
fs.writeFileSync(
'types/particleemitterobject_renderertype.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type ParticleEmitterObject_RendererType = 0 | 1 | 2`
);
shell.sed(
'-i',
'declare class gdParticleEmitterObject {',
[
'declare class gdParticleEmitterObject {',
' static Point: 0;',
' static Line: 1;',
' static Quad: 2;',
].join('\n'),
'types/gdparticleemitterobject.js'
);
fs.writeFileSync(
'types/quickcustomization_visibility.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type QuickCustomization_Visibility = 0 | 1 | 2`
);
shell.sed(
'-i',
'declare class gdQuickCustomization {',
[
'declare class gdQuickCustomization {',
' static Default: 0;',
' static Visible: 1;',
' static Hidden: 2;',
].join('\n'),
'types/gdquickcustomization.js'
);
fs.writeFileSync(
'types/customobjectconfiguration_edgeanchor.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type CustomObjectConfiguration_EdgeAnchor = 0 | 1 | 2 | 3 | 4`
);
shell.sed(
'-i',
'declare class gdCustomObjectConfiguration extends gdObjectConfiguration {',
[
'declare class gdCustomObjectConfiguration extends gdObjectConfiguration {',
' static NoAnchor: 0;',
' static MinEdge: 1;',
' static MaxEdge: 2;',
' static Proportional: 3;',
' static Center: 4;',
].join('\n'),
'types/gdcustomobjectconfiguration.js'
);
// Add convenience methods that are manually added (see postjs.js):
shell.sed(
'-i',
'declare class libGDevelop {',
[
'declare class libGDevelop {',
' assertObjectAlive(gdEmscriptenObject): void;',
' getPointer(gdEmscriptenObject): number;',
' castObject<T>(gdEmscriptenObject, Class<T>): T;',
' wrapPointer<T>(ptr: number, objectClass: Class<T>): T;',
' compare(gdEmscriptenObject, gdEmscriptenObject): boolean;',
'',
' _malloc(size: number): number;',
' _free(ptr: number): void;',
' HEAPU8: Uint8Array;',
'',
' getTypeOfObject(globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectName: string, searchInGroups: boolean): string;',
' getTypeOfBehavior(globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectName: string, searchInGroups: boolean): string;',
' getBehaviorsOfObject(globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectName: string, searchInGroups: boolean): gdVectorString;',
' isDefaultBehavior(globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectName: string, behaviorName: string, searchInGroups: boolean): boolean;',
' getTypeOfBehaviorInObjectOrGroup(globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectName: string, behaviorName: string, searchInGroups: boolean): string;',
' getBehaviorNamesInObjectOrGroup(globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectName: string, behaviorName: string, searchInGroups: boolean): gdVectorString;',
'',
' // $FlowFixMe[cannot-resolve-name]',
' removeFromVectorParameterMetadata(gdVectorParameterMetadata, index: number): void;',
' // $FlowFixMe[cannot-resolve-name]',
' swapInVectorParameterMetadata(gdVectorParameterMetadata, oldIndex: number, newIndex: number): void;',
'',
` asStandardEvent(gdBaseEvent): gdStandardEvent;`,
` asElseEvent(gdBaseEvent): gdStandardEvent;`,
` asRepeatEvent(gdBaseEvent): gdRepeatEvent;`,
` asWhileEvent(gdBaseEvent): gdWhileEvent;`,
` asForEachEvent(gdBaseEvent): gdForEachEvent;`,
` asForEachChildVariableEvent(gdBaseEvent): gdForEachChildVariableEvent;`,
` asCommentEvent(gdBaseEvent): gdCommentEvent;`,
` asGroupEvent(gdBaseEvent): gdGroupEvent;`,
` asLinkEvent(gdBaseEvent): gdLinkEvent;`,
` asJsCodeEvent(gdBaseEvent): gdJsCodeEvent;`,
` asPlatform(gdPlatform): gdPlatform;`,
'',
` asSpriteConfiguration(gdObjectConfiguration): gdSpriteObject;`,
` asTiledSpriteConfiguration(gdObjectConfiguration): gdTiledSpriteObject;`,
` asPanelSpriteConfiguration(gdObjectConfiguration): gdPanelSpriteObject;`,
` asTextObjectConfiguration(gdObjectConfiguration): gdTextObject;`,
` asShapePainterConfiguration(gdObjectConfiguration): gdShapePainterObject;`,
` // $FlowFixMe[cannot-resolve-name]`,
` asAdMobConfiguration(gdObjectConfiguration): gdAdMobObject;`,
` asTextEntryConfiguration(gdObjectConfiguration): gdTextEntryObject;`,
` asParticleEmitterConfiguration(gdObjectConfiguration): gdParticleEmitterObject;`,
` asObjectJsImplementation(gdObjectConfiguration): gdObjectJsImplementation;`,
` asCustomObjectConfiguration(gdObjectConfiguration): gdCustomObjectConfiguration;`,
` asModel3DConfiguration(gdObjectConfiguration): gdModel3DObjectConfiguration;`,
` asSpineConfiguration(gdObjectConfiguration): gdSpineObjectConfiguration;`,
'',
` asImageResource(gdResource): gdImageResource;`,
'',
].join('\n'),
'types/libgdevelop.js'
);
shell.sed(
'-i',
'declare class gdVectorString {',
'declare class gdVectorString {\n toJSArray(): Array<string>;',
'types/gdvectorstring.js'
);
shell.sed(
'-i',
'declare class gdVectorInt {',
'declare class gdVectorInt {\n toJSArray(): Array<number>;',
'types/gdvectorint.js'
);
shell.sed(
'-i',
'declare class gdSerializer {',
`declare class gdSerializer {
static fromJSObject(object: Object): gdSerializerElement;
static toJSObject(element: gdSerializerElement): any;
`,
'types/gdserializer.js'
);
shell.sed(
'-i',
'declare class gdInstructionsList {',
'declare class gdInstructionsList {\n push_back(gdInstruction): void;',
'types/gdinstructionslist.js'
);
// Add inheritance not expressed in Bindings.idl.
// TODO: these should be expressed in Bindings.idl using "implements".
shell.sed(
'-i',
'declare class gdObjectJsImplementation {',
'declare class gdObjectJsImplementation extends gdObjectConfiguration {',
'types/gdobjectjsimplementation.js'
);
shell.sed(
'-i',
'declare class gdCustomObjectConfiguration {',
'declare class gdCustomObjectConfiguration extends gdObjectConfiguration {',
'types/gdcustomobjectconfiguration.js'
);
shell.sed(
'-i',
'declare class gdBehaviorJsImplementation {',
'declare class gdBehaviorJsImplementation extends gdBehavior {',
'types/gdbehaviorjsimplementation.js'
);
shell.sed(
'-i',
'declare class gdBehaviorSharedDataJsImplementation {',
'declare class gdBehaviorSharedDataJsImplementation extends gdBehaviorsSharedData {',
'types/gdbehaviorshareddatajsimplementation.js'
);
shell.sed(
'-i',
'declare class gdJsPlatform {',
'declare class gdJsPlatform extends gdPlatform {',
'types/gdjsplatform.js'
);
shell.sed(
'-i',
'declare class gdExpressionValidator {',
'declare class gdExpressionValidator extends gdExpressionParser2NodeWorker {',
'types/gdexpressionvalidator.js'
);
shell.sed(
'-i',
'declare class gdHighestZOrderFinder {',
'declare class gdHighestZOrderFinder extends gdInitialInstanceFunctor {',
'types/gdhighestzorderfinder.js'
);
shell.sed(
'-i',
'declare class gdGroupEvent {',
'declare class gdGroupEvent extends gdBaseEvent {',
'types/gdgroupevent.js'
);
shell.sed(
'-i',
'declare class gdAbstractFileSystemJS {',
'declare class gdAbstractFileSystemJS extends gdAbstractFileSystem {',
'types/gdabstractfilesystemjs.js'
);
// Add $FlowFixMe comments to suppress Flow errors for methods with
// incompatible types (overriding parent class methods) or unresolvable names.
['getProperties', 'updateProperty', 'initializeContent'].forEach(
(method) => {
shell.sed(
'-i',
new RegExp(` ${method}\\(`),
` // $FlowFixMe[incompatible-type]\n ${method}(`,
'types/gdbehaviorjsimplementation.js'
);
shell.sed(
'-i',
new RegExp(` ${method}\\(`),
` // $FlowFixMe[incompatible-type]\n ${method}(`,
'types/gdbehaviorshareddatajsimplementation.js'
);
}
);
shell.sed(
'-i',
/ ObjectCodeGenerator:/,
' // $FlowFixMe[cannot-resolve-name]\n ObjectCodeGenerator:',
'types/libgdevelop.js'
);
shell.sed(
'-i',
/ Exporter:/,
' // $FlowFixMe[cannot-resolve-name]\n Exporter:',
'types/libgdevelop.js'
);
[
'BaseEvent',
'StandardEvent',
'RepeatEvent',
'WhileEvent',
'ForEachEvent',
'CommentEvent',
'GroupEvent',
'LinkEvent',
'JsCodeEvent',
].forEach((eventClassName) => {
shell.sed(
'-i',
`declare class gd${eventClassName} {`,
`declare class gd${eventClassName} extends gdBaseEvent {`,
`types/gd${eventClassName.toLowerCase()}.js`
);
});
// Rename classes from GDJS:
shell.sed(
'-i',
'declare class gdExporter {',
'declare class gdjsExporter {',
'types/gdexporter.js'
);
// Rename classes from GDJS:
shell.sed(
'-i',
'declare class gdExporterHelper {',
'declare class gdjsExporterHelper {',
'types/gdexporter.js'
);
// Improve typing of resources kind.
shell.sed(
'-i',
/setKind\(kind: string\): void/,
"setKind(kind: 'image' | 'audio' | 'font' | 'video' | 'json' | 'tilemap' | 'tileset' | 'model3D' | 'atlas' | 'spine' | 'bitmapFont'): void",
'types/gdresource.js'
);
shell.sed(
'-i',
/getKind\(\): string/,
"getKind(): 'image' | 'audio' | 'font' | 'video' | 'json' | 'tilemap' | 'tileset' | 'model3D' | 'atlas' | 'spine' | 'bitmapFont'",
'types/gdresource.js'
);
// Add missing declaration of set_/get_ functions, automatically added by Emscripten
// for attributes:
shell.sed(
'-i',
'x: number;',
'x: number;\n set_x(number): void;\n get_x(): number;',
'types/gdvector2f.js'
);
shell.sed(
'-i',
'y: number;',
'y: number;\n set_y(number): void;\n get_y(): number;',
'types/gdvector2f.js'
);
// Set a few parameters as optionals. No parameter is ever optional when compiled in Emscripten,
// but passing undefined is tolerated for these and it's convenient:
shell.sed(
'-i',
'type: string, description: string, optionalObjectType: string, parameterIsOptional: boolean',
'type: string, description: string, optionalObjectType?: string, parameterIsOptional?: boolean',
'types/gdinstructionmetadata.js'
);
shell.sed(
'-i',
'type: string, description: string, optionalObjectType: string, parameterIsOptional: boolean',
'type: string, description: string, optionalObjectType?: string, parameterIsOptional?: boolean',
'types/gdexpressionmetadata.js'
);
shell.sed(
'-i',
'type: string, description: string, optionalObjectType: string, parameterIsOptional: boolean',
'type: string, description: string, optionalObjectType?: string, parameterIsOptional?: boolean',
'types/gdmultipleinstructionmetadata.js'
);
// Add a notice that the file is auto-generated.
shell.sed(
'-i',
'declare class',
'// Automatically generated by GDevelop.js/scripts/generate-types.js\ndeclare class',
'types/*.js'
);
shell.echo('✅ Properly generated GDevelop.js types.');
}
);