diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8dc712c7..188c6280 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ - + + +Make sure to follow the PR preparation steps in [CONTRIBUTING.md](../CONTRIBUTING.md#preparing-your-pr) before submitting your PR: + +- [ ] format the codebase: from the root, run `bash ./clang_format.sh`. diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index d19a2a14..5fefb111 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -6,7 +6,6 @@ on: required: true type: string - concurrency: group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-static cancel-in-progress: true @@ -30,7 +29,9 @@ jobs: path: tmp - name: Checkout workaround - run: mv tmp/misc/scripts misc/scripts + run: | + mv tmp/misc/scripts misc/scripts + cp clang_format.sh misc/scripts/clang_format.sh - name: Install APT dependencies uses: awalsh128/cache-apt-pkgs-action@latest diff --git a/.gitignore b/.gitignore index b1580d74..f1ec0ff7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ *.gen.json .vscode .idea +/site diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..a52df983 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,16 @@ +# Local Development + +## Project Structure + +TODO + +## Documentation + +This project uses [MKDocs](https://www.mkdocs.org/) to serve all docs. You can change the documentation inside the `docs` folder. +To add new pages you need to update `mkdocs.yml`. + +## Preparing your PR + +The project is using [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) to format most files. You need to run `bash ./clang_format.sh` before your PR for a successful pipeline. + +Furthermore, there is an `utf-8` and `LF` checker to fix file formats. Additionally, some spellchecks run inside the [pipeline](.github/workflows/static_checks.yml). diff --git a/README.md b/README.md index 29585d49..c2e6b220 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,7 @@ Read this [documentation](https://geequlim.github.io/ECMAScript/getting-started) - [godot-ECMAScript-cookbook](https://github.com/why-try313/godot-ECMAScript-cookbook/wiki) - Tutorial - [godot-typescript-starter](https://github.com/citizenll/godot-typescript-starter) - Template - [godot-js-template](https://github.com/fukaraadam-workspace/godot-js-template) - Template + +## Contributing + +If you like to contribute to this project check the [CONTRIBUTING.md](https://github.com/Geequlim/ECMAScript/blob/master/CONTRIBUTING.md) file. diff --git a/clang_format.sh b/clang_format.sh new file mode 100644 index 00000000..63022373 --- /dev/null +++ b/clang_format.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# This script runs clang-format and fixes copyright headers on all relevant files in the repo. +# This is the primary script responsible for fixing style violations. + +set -uo pipefail + +if [ $# -eq 0 ]; then + # Loop through all code files tracked by Git. + files=$(git ls-files -- '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' '*.java' '*.glsl' \ + ':!:.git/*' ':!:thirdparty/*' ':!:*/thirdparty/*' ':!:platform/android/java/lib/src/com/google/*' \ + ':!:*-so_wrap.*' ':!:tests/python_build/*') +else + # $1 should be a file listing file paths to process. Used in CI. + files=$(cat "$1" | grep -v "thirdparty/" | grep -E "\.(c|h|cpp|hpp|cc|hh|cxx|m|mm|inc|java|glsl)$" | grep -v "platform/android/java/lib/src/com/google/" | grep -v "\-so_wrap\." | grep -v "tests/python_build/") +fi + +if [ ! -z "$files" ]; then + clang-format --Wno-error=unknown -i $files +fi + +diff=$(git diff --color) + +# If no diff has been generated all is OK, clean up, and exit. +if [ -z "$diff" ] ; then + printf "\e[1;32m*** Files in this commit comply with the clang-format style rules.\e[0m\n" + exit 0 +fi + +# A diff has been created, notify the user, clean up, and exit. +printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" +# Perl commands replace trailing spaces with `·` and tabs with ``. +printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="" x length($2); sprintf("$1$tabs$3")/ge' + +printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i '\e[0m\n" +exit 1 diff --git a/docs/examples/load-json-in-singleton.md b/docs/examples/load-json-in-singleton.md new file mode 100644 index 00000000..ae05b9db --- /dev/null +++ b/docs/examples/load-json-in-singleton.md @@ -0,0 +1,77 @@ +# Load json in singleton + +This example shows how to load a file like a ``config`` inside a singleton to access it everywhere. +We use ``TypeScript`` for this example all `.ts` files will be compiled as `.mjs` to the folder `scripts/generated/**`. If you use `TypeScript` you need to set `"resolveJsonModule": true` inside your `tsconfig.json`. + +## 1. Create file + +We create a new folder named ``config`` and a new file `test.json`. + +Next we write this to the ``test.json``: + +````json title="test.json" +{ + "test": true +} +```` + +## 2. Create the singleton + +We create a new file inside our `src` folder like ``read-config.ts`` and add this code to it: + +````ts title="read-config.ts" +// @ts-ignore +import TestJson from "res://config/test.json"; + +type TestType = { + test: boolean; +}; + +export default class ReadConfig extends godot.Node { + static _singleton: ReadConfig; + + static get singleton() { + return ReadConfig._singleton; + } + + constructor() { + super(); + if (!ReadConfig._singleton) { + ReadConfig._singleton = this; + } + } + + // This property is available for other classes + config: TestType = TestJson as TestType; +} +```` + +## 3. Autoload singleton in project + +We need to update the ``[autoload]`` inside `project.godot`: + +````text title="project.godot" +... +[autoload] + +; Use the generated `.mjs` file instead of `.ts` +ReadConfig="*res://scripts/generated/read-config.mjs" +... +```` + +## 4. Use the singleton in other class + +In another class e.g. ``main.ts`` you need to import the `ReadConfig` then you can access every public property and method from `ReadConfig` via `ReadConfig.singleton`: + +````ts title="main.ts" +import ReadConfig from "./read-config"; + +export default class Main extends godot.Node { + _ready(): void { + console.log(ReadConfig.singleton.config.test); // prints "true" + } +} + +```` + + diff --git a/docs/examples/read-file-local.md b/docs/examples/read-file-local.md new file mode 100644 index 00000000..6191bea3 --- /dev/null +++ b/docs/examples/read-file-local.md @@ -0,0 +1,37 @@ +# Read file local + +This example shows how to load any file as string from a local folder. + +We use ``TypeScript`` for this example all `.ts` files will be compiled as `.mjs`. + +## 1. Create the file you want to read + +In this example we try to read a ``.csv`` file, but it should work with any other file as well. + +We create a folder ``resources`` and add a new file `test.csv`: + +````csv title="test.csv" +keys,en,de +HELLO,hello,hallo +```` + +## 2. Read the file in class + +We use [FileAccess](https://docs.godotengine.org/en/stable/classes/class_fileaccess.html) to read the file. + +We create a new file ``read-local-file.ts``: + +````ts title="read-local-file.ts" +export default class ReadLocalFile extends godot.Node { + _ready(): void { + const file = new godot.FileAccess(); + file.open("res://resources/test.csv", godot.FileAccess.ModeFlags.READ); + let fileContent: string = ""; + while (!file.eof_reached()) { + fileContent += `${file.get_line()}\n`; + } + console.log(fileContent); + } +} +```` + diff --git a/docs/examples/use-gdscript-in-ts.md b/docs/examples/use-gdscript-in-ts.md new file mode 100644 index 00000000..c3d69d18 --- /dev/null +++ b/docs/examples/use-gdscript-in-ts.md @@ -0,0 +1,55 @@ +# Use GDScript in TypeScript + +This example shows how to use a class written in GDScript in another TS class. + +We use ``TypeScript`` for this example all `.ts` files will be compiled as `.mjs` to the folder `scripts/generated/**`. + +## 1. Create the GDScript file + +First we create a simple class with GDScript inside a new file `scripts/gd/GDTest.gd`: + +````gdscript title="GDTest.gd" +extends Node + +func _ready(): + pass + +func print_in_gd_test(): + print("bla") + +```` + +## 2. Create a declaration (*.d.ts) for GDScript + +For proper TypeScript support we need to add a ``gdtest.d.ts`` file: + +````ts title="gdtest.d.ts" +declare module "res://scripts/gd/GDTest.gd" { + class GDTest { + call(func: "print_in_gd_test"): void; + + static new() { + return this; + } + } + export = GDTest; +} +```` + +## 3. Use the class inside your TS file + +In the end we need to call the ``GDTest.gd`` from another `.ts` file, like `main.ts`: + +````ts title="main.ts" +import GDTest from "res://scripts/gd/GDTest.gd"; + +export default class Main extends godot.Node { + _ready(): void { + const bla: Bla = Bla.new(); + bla.call("run_in_bla"); + } +} + +```` + +> **Note:** The important thing here is that you use `new()` to instantiate and `call` to execute the function diff --git a/docs/getting-started.md b/docs/getting-started.md index a7f7c579..4b9dbcec 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,7 +2,7 @@ 1. Define your JavaScript class and inherit from a Godot class, then export it as the **default** entry: -```js +``` // The default export entry is treated as an exported class to Godot export default class MySprite extends godot.Sprite { // this is _init() in GDScript @@ -10,7 +10,7 @@ export default class MySprite extends godot.Sprite { super(); } - _ready() {} + _reajavascript title="my-sprite.mjs"dy() {} _process(delta) {} } @@ -21,7 +21,7 @@ export default class MySprite extends godot.Sprite { ### How to export signals -```js +```javascript title="my-sprite.mjs" export default class MySprite extends godot.Sprite {} // register game_over signal to MySprite class godot.register_signal(MySprite, "game_over"); @@ -29,7 +29,7 @@ godot.register_signal(MySprite, "game_over"); ### How to export properties -```js +```javascript title="my-sprite.mjs" export default class MySprite extends godot.Sprite { _process(delta) { // Yes! We can use operators in JavaScript like GDScript diff --git a/docs/gotchas.md b/docs/gotchas.md new file mode 100644 index 00000000..d08e4189 --- /dev/null +++ b/docs/gotchas.md @@ -0,0 +1,60 @@ +# Gotchas and limitations + +Some common mistakes and limitations. + +## Import dependency from `node_modules` + +If you use `TypeScript` you may encounter the problem where dependencies from `node_modules` are not bundled correctly. + +As a workaround you can create a new file `npm-modules.bundle.ts`: + +```ts title="npm-modules.bundle.ts" +import { default as dayjs } from "dayjs"; +export default { dayjs }; +``` + +In your class you can use the dependency like this: + +```ts title="main.ts" +import npm from "./npm-modules.bundle"; + +export default class Main extends godot.Node { + _ready(): void { + console.log(npm.dayjs().toString()); + } +} +``` + +With a bundler like `esbuild` you should build the `npm-modules.bundle.ts` with the `--bundle` option, but all the other classes like `main.ts` without it. + +## Position.x is immutable + +You cannot change `this.position.x` try to change `this.position`: + +```javascript title="player.mjs" +export default class Player extends godot.KinematicBody2D { + constructor() { + super(); + this.direction = new godot.Vector2(1, 0); + } + _ready() {} + _process(delta) { + this.position.x += this.direction.x; // <- breaks + this.position += this.direction; // <- works + } +} +godot.register_property(Player, "direction", new godot.Vector2(1, 0)); +``` + +## ``register_property`` has to be a target + +You cannot change `this.position.x` try to change `this.position`: + +```javascript title="player.mjs" +export default class Player extends godot.KinematicBody2D { +} +// This works +godot.register_property(Player, "directionWorks", new godot.Vector2(1, 0)); +// This breaks because `player` isn't a correct target +godot.register_property(player, "directionBreaks", new godot.Vector2(1, 0)); +``` diff --git a/editor/workarounds/ignore-methods.cpp b/editor/workarounds/ignore-methods.cpp index 38496496..78b5a291 100644 --- a/editor/workarounds/ignore-methods.cpp +++ b/editor/workarounds/ignore-methods.cpp @@ -1,32 +1,6 @@ /**************************************************************************/ /* ignore-methods.cpp */ /**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ /* All types are generated in editor_tools, but constructors are missing we need to add them manually. */ diff --git a/mkdocs.yml b/mkdocs.yml index 7d37e3d3..d3964c07 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,6 +7,11 @@ nav: - Getting Started: getting-started.md - API: api.md - TypeScript: typescript.md + - Examples: + - Load JSON in Singleton: examples/load-json-in-singleton.md + - Read file local: examples/read-file-local.md + - Use GDScript in TS: examples/use-gdscript-in-ts.md + - Gotchas: gotchas.md theme: name: material icon: @@ -18,3 +23,12 @@ theme: - navigation.top - search.suggest - search.highlight + - content.code.copy +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences