diff --git a/RECIPES.md b/RECIPES.md index e437b5e3..d5ed069f 100644 --- a/RECIPES.md +++ b/RECIPES.md @@ -316,6 +316,7 @@ Additional recipes can be found on the [Main Recipes Repository](https://github. | [kocal/biome-js-bundle](https://packagist.org/packages/kocal/biome-js-bundle) | [2.0](kocal/biome-js-bundle/2.0) | | [kocal/oxc-bundle](https://packagist.org/packages/kocal/oxc-bundle) | [1.0](kocal/oxc-bundle/1.0) | | [kocal/oxlint-bundle](https://packagist.org/packages/kocal/oxlint-bundle) | [1.0](kocal/oxlint-bundle/1.0) | +| [kocal/symfony-app-pack](https://packagist.org/packages/kocal/symfony-app-pack) | [1.0](kocal/symfony-app-pack/1.0) | | [koff/i18n-form-bundle](https://packagist.org/packages/koff/i18n-form-bundle) | [4.0](koff/i18n-form-bundle/4.0) | | [kriswallsmith/buzz](https://packagist.org/packages/kriswallsmith/buzz) | [1.0](kriswallsmith/buzz/1.0) | | [kunicmarko/jms-messenger-adapter](https://packagist.org/packages/kunicmarko/jms-messenger-adapter) | [0.1](kunicmarko/jms-messenger-adapter/0.1) | diff --git a/archived/kocal.symfony-app-pack/090a78cd1f770682e72243dca41b9e73ec765eb6.json b/archived/kocal.symfony-app-pack/090a78cd1f770682e72243dca41b9e73ec765eb6.json new file mode 100644 index 00000000..ccd17010 --- /dev/null +++ b/archived/kocal.symfony-app-pack/090a78cd1f770682e72243dca41b9e73ec765eb6.json @@ -0,0 +1,759 @@ +{ + "manifests": { + "kocal/symfony-app-pack": { + "manifest": { + "copy-from-recipe": { + ".github/": ".github/", + "tools/": "tools/", + ".twig-cs-fixer.php": ".twig-cs-fixer.php", + "ecs.php": "ecs.php", + "Makefile.local": "Makefile.local", + "php.ini": "php.ini", + "phpstan.dist.neon": "phpstan.dist.neon", + "rector.php": "rector.php" + }, + "gitignore": [ + "/Makefile.local" + ], + "makefile": [ + "ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))", + "include $(ROOT_DIR)/tools/make/text.mk", + "include $(ROOT_DIR)/tools/make/help.mk", + "include $(ROOT_DIR)/tools/make/os.mk", + "include $(ROOT_DIR)/tools/make/git.mk", + "", + ".DEFAULT_GOAL := help", + "", + "# Executables", + "SF := symfony", + "SF_PROXY = $(shell $(SF) local:proxy:url)", + "SF_CONSOLE := $(SF) console", + "PHP := $(SF) php", + "COMPOSER := $(SF) composer", + "", + "UID := $(shell id -u)", + "GID := $(shell id -g)", + "DC := USER_ID=$(UID) GROUP_ID=$(GID) docker compose --env-file /dev/null", + "", + "## Install everything needed to start the project", + "install:", + "\t$(SF) local:server:ca:install", + "\t$(MAKE) start", + "\t$(MAKE) app.install", + "", + "## Start the environment", + "start:", + "\t$(DC) up -d", + "\t$(SF) proxy:start", + "\t$(SF) serve", + "", + "## Stop the environment", + "stop:", + "\t$(DC) kill", + "", + "## Stop and delete the environment and project data (database, logs, etc.)", + "delete:", + "\t$(DC) down -v --remove-orphans", + "", + "## App - Install the application", + "app.install:", + "\t@$(call action, Installing PHP dependencies...)", + "\t$(COMPOSER) install --prefer-dist", + "", + "\t@$(call action, Running DB migrations...)", + "\t$(SF_CONSOLE) doctrine:migrations:migrate --no-interaction --all-or-nothing", + "", + "## App - Install the application (alias to \"make app.install\")", + "app.update: app.install", + "", + "######", + "# QA #", + "######", + "", + "## QA - Run all QA checks", + "qa: archi refactor cs lint phpstan test", + "", + "## QA - Run all QA checks and fix issues", + "qa.fix: archi refactor.fix cs.fix lint.fix phpstan test", + "", + "#########", + "# Archi #", + "#########", + "", + "## Architecture - Run architecture checks", + "archi: archi.back", + "", + "## Architecture - Run architecture checks for backend", + "archi.back:", + "\t$(PHP) vendor/bin/deptrac --report-uncovered --fail-on-uncovered", + "", + "############", + "# Refactor #", + "############", + "", + "## Refactor - Run all refactor checks", + "refactor: refactor.back", + "", + "## Refactor - Run all refactor checks and fix issues", + "refactor.fix: refactor.back.fix", + "", + "## Refactor - Run refactor checks for backend", + "refactor.back:", + "\t$(PHP) vendor/bin/rector process --dry-run", + "", + "## Refactor - Run refactor checks for backend and fix issues", + "refactor.back.fix:", + "\t$(PHP) vendor/bin/rector process", + "", + "################", + "# Coding style #", + "################", + "", + "## Coding style - Run all coding style checks", + "cs: cs.back cs.front", + "", + "## Coding style - Run all coding style checks and fix issues", + "cs.fix: cs.back.fix cs.front.fix", + "", + "## Coding style - Check backend coding style", + "cs.back:", + "\t$(PHP) vendor/bin/ecs check", + "\t$(PHP) vendor/bin/twig-cs-fixer", + "", + "## Coding style - Check backend coding style and fix issues", + "cs.back.fix:", + "\t$(PHP) vendor/bin/ecs check --fix", + "\t$(PHP) vendor/bin/twig-cs-fixer --fix", + "", + "## Coding style - Check frontend coding style", + "cs.front: bin/oxfmt", + "\tbin/oxfmt --check", + "", + "## Coding style - Check frontend coding style and fix issues", + "cs.front.fix: bin/oxfmt", + "\tbin/oxfmt", + "", + "bin/oxfmt:", + "\t@$(call action, Downloading oxfmt...)", + "\t$(SF_CONSOLE) oxc:download:oxfmt", + "\t@$(call success, oxfmt downloaded successfully)", + "", + "##########", + "# Linter #", + "##########", + "", + "## Linter - Run all linters", + "lint: lint.back lint.front", + "", + "## Linter - Run all linters and fix issues", + "lint.fix: lint.back lint.front.fix", + "", + "## Linter - Run linters for backend", + "lint.back:", + "\t$(SF_CONSOLE) lint:container", + "\t$(SF_CONSOLE) lint:xliff translations", + "\t$(SF_CONSOLE) lint:yaml --parse-tags config", + "\t$(SF_CONSOLE) lint:twig templates", + "\t#$(SF_CONSOLE) doctrine:schema:validate", + "", + "## Linter - Lint front files", + "lint.front: bin/oxlint", + "\tbin/oxlint", + "", + "## Linter - Lint front files and fix issues", + "lint.front.fix: bin/oxlint", + "\tbin/oxlint --fix", + "", + "bin/oxlint:", + "\t@$(call action, Downloading oxlint...)", + "\t$(SF_CONSOLE) oxc:download:oxlint", + "", + "###########", + "# PHPStan #", + "###########", + "", + "## PHPStan - Run PHPStan", + "phpstan:", + "\t$(PHP) vendor/bin/phpstan analyse", + "", + "## PHPStan - Run PHPStan and update the baseline", + "phpstan.generate-baseline:", + "\t$(PHP) vendor/bin/phpstan analyse --generate-baseline", + "", + "#########", + "# Tests #", + "#########", + "", + "## Tests - Run all tests", + "test: test.back", + "", + "## Tests - Run backend tests", + "test.back:", + "\t$(PHP) vendor/bin/phpunit", + "## Tests - Run backend tests with coverage", + "", + "test.back.coverage:", + "\t$(PHP) vendor/bin/phpunit --coverage-html .cache/phpunit/coverage-html", + "", + "-include $(ROOT_DIR)/Makefile.local" + ] + }, + "files": { + ".github/dependabot.yaml": { + "contents": [ + "# To get started with Dependabot version updates, you'll need to specify which", + "# package ecosystems to update and where the package manifests are located.", + "# Please see the documentation for all configuration options:", + "# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file", + "", + "version: 2", + "updates:", + " - package-ecosystem: \"composer\" # See documentation for possible values", + " directory: \"/\" # Location of package manifests", + " schedule:", + " interval: \"weekly\"", + "" + ], + "executable": false + }, + ".github/workflows/ci.yaml": { + "contents": [ + "name: CI", + "", + "defaults:", + " run:", + " shell: bash", + "", + "on:", + " pull_request:", + " types: [ opened, synchronize, reopened, ready_for_review ]", + " push:", + " branches:", + " - main", + "", + "concurrency:", + " group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}", + " cancel-in-progress: true", + "", + "jobs:", + " qa:", + " if: ${{ ! github.event.pull_request.draft }}", + " name: Quality Assurance", + " timeout-minutes: 10", + " runs-on: ubuntu-latest", + " steps:", + " - name: Checkout", + " uses: actions/checkout@v4", + "", + " - name: Setup PHP", + " uses: shivammathur/setup-php@v2", + " with:", + " extensions: ctype, iconv, mbstring", + " tools: symfony", + "", + " - name: Start Docker containers", + " run: docker compose up -d --wait", + "", + " - name: Install project", + " run: make app.install", + "", + " - name: QA", + " run: make qa", + "" + ], + "executable": false + }, + ".twig-cs-fixer.php": { + "contents": [ + "addStandard(new TwigCsFixer\\Standard\\Symfony());", + "", + "$config = new TwigCsFixer\\Config\\Config();", + "$config->setCacheFile(__DIR__.'/tools/.cache/twig-cs-fixer');", + "$config->setRuleset($ruleset);", + "", + "return $config;", + "" + ], + "executable": false + }, + "Makefile.local": { + "contents": [ + "# Local targets that should not be committed to git", + "" + ], + "executable": false + }, + "ecs.php": { + "contents": [ + "withParallel()", + " ->withCache(__DIR__ . '/tools/.cache/ecs')", + " ->withPaths([", + " __DIR__ . '/assets',", + " __DIR__ . '/config',", + " __DIR__ . '/public',", + " __DIR__ . '/src',", + " __DIR__ . '/tests',", + " __DIR__ . '/tools',", + " ])", + " ->withSkip([", + " __DIR__ . '/tools/.cache',", + " __DIR__ . '/config/bundles.php',", + " __DIR__ . '/config/reference.php',", + " ])", + " ->withPreparedSets(", + " psr12: true,", + " common: true,", + " strict: true,", + " cleanCode: true,", + " )", + ";", + "" + ], + "executable": false + }, + "php.ini": { + "contents": [ + "memory_limit = -1", + "" + ], + "executable": false + }, + "phpstan.dist.neon": { + "contents": [ + "includes:", + " - vendor/kocal/phpstan-symfony-ux/extension.neon", + " - vendor/phpstan/phpstan-symfony/extension.neon", + " - vendor/phpstan/phpstan-doctrine/extension.neon", + " - tools/phpstan/symfony-configuration.php", + "", + "parameters:", + " level: 9", + " paths:", + " - bin/", + " - config/", + " - public/", + " - src/", + " - tools/", + " - tests/", + " excludePaths:", + " - config/reference.php", + " - tools/.cache", + "", + " tmpDir: tools/.cache/phpstan", + "", + " symfony:", + " consoleApplicationLoader: tools/phpstan/console-application.php", + "", + " doctrine:", + " objectManagerLoader: tools/phpstan/object-manager.php", + "", + "rules:", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\ClassMustBeFinalRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\ClassNameMustNotEndWithComponentRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\ForbiddenAttributesPropertyRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\ForbiddenClassPropertyRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\MethodsVisibilityRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\PostMountMethodSignatureRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\PreMountMethodSignatureRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\PublicPropertiesMustBeCamelCaseRule", + "" + ], + "executable": false + }, + "rector.php": { + "contents": [ + "withParallel()", + " ->withCache(__DIR__ . '/tools/.cache/rector')", + " ->withPaths([", + " __DIR__ . '/config',", + " __DIR__ . '/public',", + " __DIR__ . '/src',", + " __DIR__ . '/tests',", + " __DIR__ . '/tools',", + " ])", + " ->withPhpSets()", + " ->withPreparedSets(", + " deadCode: true,", + " codeQuality: true,", + " codingStyle: true,", + " typeDeclarations: true,", + " privatization: true,", + " instanceOf: true,", + " earlyReturn: true,", + " phpunitCodeQuality: true,", + " doctrineCodeQuality: true,", + " symfonyCodeQuality: true,", + " )", + " ->withSkip([", + " __DIR__ . '/config/bundles.php',", + " __DIR__ . '/config/reference.php',", + " __DIR__ . '/tools/.cache',", + " ControllerMethodInjectionToConstructorRector::class,", + " ])", + ";", + "" + ], + "executable": false + }, + "tools/.gitignore": { + "contents": [ + ".cache/", + "" + ], + "executable": false + }, + "tools/make/git.mk": { + "contents": [ + "########", + "# Diff #", + "########", + "", + "# Returns the list of changed files for some given extensions and some given folders.", + "#", + "# @param $1 The file extensions of changed files", + "# @param $2 The relative folders to parse for changed files", + "#", + "# Examples:", + "#", + "# Example #1: list PHP and Javascript files changed in the src and test folders", + "#", + "# $(call git_diff, php js, src test)", + "", + "define git_diff", + "$(shell \\", + "\tfor ext in $(if $(strip $(1)),$(strip $(1)),\"\") ; \\", + "\tdo \\", + "\t\tfor dir in $(if $(strip $(2)),$(strip $(2)),\"\") ; \\", + "\t\tdo \\", + "\t\t\tgit --no-pager diff --name-status \"$$(git merge-base HEAD origin/master)\" \\", + "\t\t\t\t| grep \"$${ext}\\$$\" \\", + "\t\t\t\t| grep \"\\\\s$${dir}\" \\", + "\t\t\t\t| grep -v '^D' \\", + "\t\t\t\t| awk '{ print $$NF }' || true ; \\", + "\t\tdone ; \\", + "\tdone \\", + ")", + "endef", + "" + ], + "executable": false + }, + "tools/make/help.mk": { + "contents": [ + "########", + "# Help #", + "########", + "", + ".DEFAULT_GOAL := help", + "", + "HELP = \\", + "\tUsage: make [$(COLOR_INFO)command$(COLOR_RESET)] \\", + "\t$(call help_section, Help) \\", + "\t$(call help,help,This help)", + "", + "define help_section", + "\t\\n\\n$(COLOR_COMMENT)$(strip $(1)):$(COLOR_RESET)", + "endef", + "", + "define help", + " \\n $(COLOR_INFO)$(1)$(COLOR_RESET) $(2)", + "endef", + "", + "", + "help:", + "\t@printf \"\\n$(HELP)\"", + "\t@awk ' \\", + "\t\tBEGIN { \\", + "\t\t\tsectionsName[1] = \"Commands\" ; \\", + "\t\t\tsectionsCount = 1 ; \\", + "\t\t} \\", + "\t\t/^[-a-zA-Z0-9_.@%\\/+]+:/ { \\", + "\t\t\tif (match(lastLine, /^## (.*)/)) { \\", + "\t\t\t\tcommand = substr($$1, 1, index($$1, \":\") - 1) ; \\", + "\t\t\t\tsection = substr(lastLine, RSTART + 3, index(lastLine, \" - \") - 4) ; \\", + "\t\t\t\tif (section) { \\", + "\t\t\t\t\tmessage = substr(lastLine, index(lastLine, \" - \") + 3, RLENGTH) ; \\", + "\t\t\t\t\tsectionIndex = 0 ; \\", + "\t\t\t\t\tfor (i = 1; i <= sectionsCount; i++) { \\", + "\t\t\t\t\t\tif (sectionsName[i] == section) { \\", + "\t\t\t\t\t\t\tsectionIndex = i ; \\", + "\t\t\t\t\t\t} \\", + "\t\t\t\t\t} \\", + "\t\t\t\t\tif (!sectionIndex) { \\", + "\t\t\t\t\t\tsectionIndex = sectionsCount++ + 1 ; \\", + "\t\t\t\t\t\tsectionsName[sectionIndex] = section ; \\", + "\t\t\t\t\t} \\", + "\t\t\t\t} else { \\", + "\t\t\t\t\tmessage = substr(lastLine, RSTART + 3, RLENGTH) ; \\", + "\t\t\t\t\tsectionIndex = 1 ; \\", + "\t\t\t\t} \\", + "\t\t\t\tif (length(command) > sectionsCommandLength[sectionIndex]) { \\", + "\t\t\t\t\tsectionsCommandLength[sectionIndex] = length(command) ; \\", + "\t\t\t\t} \\", + "\t\t\t\tsectionCommandIndex = sectionsCommandCount[sectionIndex]++ + 1; \\", + "\t\t\t\thelpsCommand[sectionIndex, sectionCommandIndex] = command ; \\", + "\t\t\t\thelpsMessage[sectionIndex, sectionCommandIndex] = message ; \\", + "\t\t\t} \\", + "\t\t} \\", + "\t\t{ lastLine = $$0 } \\", + "\t\tEND { \\", + "\t\t\tfor (i = 1; i <= sectionsCount; i++) { \\", + "\t\t\t\tif (sectionsCommandCount[i]) { \\", + "\t\t\t\t\tprintf \"\\n\\n$(COLOR_COMMENT)%s:$(COLOR_RESET)\", sectionsName[i] ; \\", + "\t\t\t\t\tfor (j = 1; j <= sectionsCommandCount[i]; j++) { \\", + "\t\t\t\t\t\tprintf \"\\n $(COLOR_INFO)%-\" sectionsCommandLength[i] \"s$(COLOR_RESET) %s\", helpsCommand[i, j], helpsMessage[i, j] ; \\", + "\t\t\t\t\t} \\", + "\t\t\t\t} \\", + "\t\t\t} \\", + "\t\t} \\", + "\t' $(MAKEFILE_LIST)", + "\t@printf \"\\n\\n\"", + "", + ".PHONY: help", + "" + ], + "executable": false + }, + "tools/make/os.mk": { + "contents": [ + "######", + "# Os #", + "######", + "", + "# Os detection helpers.", + "#", + "# Examples:", + "#", + "# Example #1: conditions on linux", + "#", + "# echo $(if $(OS_LINUX),Running on Linux,*NOT* running on Linux)", + "", + "ifeq ($(OS),Windows_NT)", + "OS = windows", + "OS_WINDOWS = 1", + "else", + "# See: https://make.mad-scientist.net/deferred-simple-variable-expansion/", + "OS = $(eval OS := $$(shell uname -s | tr '[:upper:]' '[:lower:]'))$(OS)", + "OS_LINUX = $(if $(findstring linux,$(OS)),1)", + "OS_DARWIN = $(if $(findstring darwin,$(OS)),1)", + "endif", + "" + ], + "executable": false + }, + "tools/make/text.mk": { + "contents": [ + "##########", + "# Colors #", + "##########", + "", + "COLOR_RESET := \\033[0m", + "COLOR_BOLD_ITALIC := \\033[1;4m", + "COLOR_ERROR := \\033[31m", + "COLOR_INFO := \\033[32m", + "COLOR_WARNING := \\033[33m", + "COLOR_COMMENT := \\033[36m", + "", + "######################", + "# Special Characters #", + "######################", + "", + "# Usage:", + "# $(call message, Foo$(,) bar) = Foo, bar", + "", + ", := ,", + "", + "########", + "# Time #", + "########", + "", + "# Usage:", + "# $(call time) = 11:06:20", + "", + "define time", + "`date -u +%T`", + "endef", + "", + "###########", + "# Message #", + "###########", + "", + "# Usage:", + "# $(call message, Foo bar) = Foo bar", + "# $(call message_success, Foo bar) = (\u3063\u25d5\u203f\u25d5)\u3063 Foo bar", + "# $(call message_warning, Foo bar) = \u00af\\_(\u30c4)_/\u00af Foo bar", + "# $(call message_error, Foo bar) = (\u256f\u00b0\u25a1\u00b0)\u256f\ufe35 \u253b\u2501\u253b Foo bar", + "", + "define message", + "\tprintf \"$(COLOR_INFO)$(strip $(1))$(COLOR_RESET)\\n\"", + "endef", + "", + "define message_success", + "\tprintf \"$(COLOR_INFO)(\u3063\u25d5\u203f\u25d5)\u3063 $(strip $(1))$(COLOR_RESET)\\n\"", + "endef", + "", + "define message_warning", + "\tprintf \"$(COLOR_WARNING)$(strip $(1))$(COLOR_RESET)\\n\"", + "endef", + "", + "define message_error", + "\tprintf \"$(COLOR_ERROR)(\u256f\u00b0\u25a1\u00b0)\u256f\ufe35 \u253b\u2501\u253b $(strip $(1))$(COLOR_RESET)\\n\"", + "endef", + "", + "###########", + "# Confirm #", + "###########", + "", + "# Usage:", + "# $(call confirm, Foo bar) = \u0f3c \u3064 \u25d5_\u25d5 \u0f3d\u3064 Foo bar (y/N):", + "", + "define confirm", + "\t$(if $(CONFIRM),, \\", + "\t\tprintf \"$(COLOR_INFO) \u0f3c \u3064 \u25d5_\u25d5 \u0f3d\u3064 $(COLOR_WARNING)$(strip $(1)) $(COLOR_RESET)$(COLOR_WARNING)(y/N)$(COLOR_RESET): \"; \\", + "\t\tread CONFIRM ; if [ \"$$CONFIRM\" != \"y\" ]; then printf \"\\n\"; exit 1; fi; \\", + "\t)", + "endef", + "", + "#######", + "# Log #", + "#######", + "", + "# Usage:", + "# $(call log, Foo bar) = [11:06:20] [target] Foo bar", + "# $(call log_warning, Foo bar) = [11:06:20] [target] \u00af\\_(\u30c4)_/\u00af Foo bar", + "# $(call log_error, Foo bar) = [11:06:20] [target] (\u256f\u00b0\u25a1\u00b0)\u256f\ufe35 \u253b\u2501\u253b Foo bar", + "# $(call log_and_call, echo 'Message') = [11:06:20] [target] echo 'Message' then execute the command", + "", + "define log", + "\tprintf \"[$(COLOR_COMMENT)$(call time)$(COLOR_RESET)] [$(COLOR_COMMENT)$(@)$(COLOR_RESET)] \" ; $(call message, $(1))", + "endef", + "", + "define log_warning", + "\tprintf \"[$(COLOR_COMMENT)$(call time)$(COLOR_RESET)] [$(COLOR_COMMENT)$(@)$(COLOR_RESET)] \" ; $(call message_warning, $(1))", + "endef", + "", + "define log_error", + "\tprintf \"[$(COLOR_COMMENT)$(call time)$(COLOR_RESET)] [$(COLOR_COMMENT)$(@)$(COLOR_RESET)] \" ; $(call message_error, $(1))", + "endef", + "", + "##########", + "# Action #", + "##########", + "", + "# Usage:", + "# $(call action, Foo bar) = > Foo bar (in bold)", + "", + "define action", + "\tprintf \"\\n$(COLOR_BOLD_ITALIC)>>> $(strip $(1))$(COLOR_RESET)\\n\\n\"", + "endef", + "" + ], + "executable": false + }, + "tools/phpstan/console-application.php": { + "contents": [ + "bootEnv(__DIR__ . '/../../.env');", + "", + "// @phpstan-ignore-next-line argument.type", + "$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);", + "", + "return new Application($kernel);", + "" + ], + "executable": false + }, + "tools/phpstan/object-manager.php": { + "contents": [ + "bootEnv(__DIR__ . '/../../.env');", + "", + "// @phpstan-ignore-next-line argument.type", + "$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);", + "$kernel->boot();", + "", + "/** @var EntityManager $entityManager */", + "$entityManager = $kernel->getContainer()->get('doctrine')->getManager();", + "", + "return $entityManager;", + "" + ], + "executable": false + }, + "tools/phpstan/symfony-configuration.php": { + "contents": [ + " [", + " 'symfony' => [", + " 'containerXmlPath' => $xmlContainerFile,", + " ],", + " ],", + "];", + "" + ], + "executable": false + } + }, + "ref": "090a78cd1f770682e72243dca41b9e73ec765eb6" + } + } +} diff --git a/index.json b/index.json index da5a4778..3068c895 100644 --- a/index.json +++ b/index.json @@ -1002,6 +1002,9 @@ "kocal/oxlint-bundle": [ "1.0" ], + "kocal/symfony-app-pack": [ + "1.0" + ], "koff/i18n-form-bundle": [ "4.0" ], diff --git a/kocal.symfony-app-pack.1.0.json b/kocal.symfony-app-pack.1.0.json new file mode 100644 index 00000000..ccd17010 --- /dev/null +++ b/kocal.symfony-app-pack.1.0.json @@ -0,0 +1,759 @@ +{ + "manifests": { + "kocal/symfony-app-pack": { + "manifest": { + "copy-from-recipe": { + ".github/": ".github/", + "tools/": "tools/", + ".twig-cs-fixer.php": ".twig-cs-fixer.php", + "ecs.php": "ecs.php", + "Makefile.local": "Makefile.local", + "php.ini": "php.ini", + "phpstan.dist.neon": "phpstan.dist.neon", + "rector.php": "rector.php" + }, + "gitignore": [ + "/Makefile.local" + ], + "makefile": [ + "ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))", + "include $(ROOT_DIR)/tools/make/text.mk", + "include $(ROOT_DIR)/tools/make/help.mk", + "include $(ROOT_DIR)/tools/make/os.mk", + "include $(ROOT_DIR)/tools/make/git.mk", + "", + ".DEFAULT_GOAL := help", + "", + "# Executables", + "SF := symfony", + "SF_PROXY = $(shell $(SF) local:proxy:url)", + "SF_CONSOLE := $(SF) console", + "PHP := $(SF) php", + "COMPOSER := $(SF) composer", + "", + "UID := $(shell id -u)", + "GID := $(shell id -g)", + "DC := USER_ID=$(UID) GROUP_ID=$(GID) docker compose --env-file /dev/null", + "", + "## Install everything needed to start the project", + "install:", + "\t$(SF) local:server:ca:install", + "\t$(MAKE) start", + "\t$(MAKE) app.install", + "", + "## Start the environment", + "start:", + "\t$(DC) up -d", + "\t$(SF) proxy:start", + "\t$(SF) serve", + "", + "## Stop the environment", + "stop:", + "\t$(DC) kill", + "", + "## Stop and delete the environment and project data (database, logs, etc.)", + "delete:", + "\t$(DC) down -v --remove-orphans", + "", + "## App - Install the application", + "app.install:", + "\t@$(call action, Installing PHP dependencies...)", + "\t$(COMPOSER) install --prefer-dist", + "", + "\t@$(call action, Running DB migrations...)", + "\t$(SF_CONSOLE) doctrine:migrations:migrate --no-interaction --all-or-nothing", + "", + "## App - Install the application (alias to \"make app.install\")", + "app.update: app.install", + "", + "######", + "# QA #", + "######", + "", + "## QA - Run all QA checks", + "qa: archi refactor cs lint phpstan test", + "", + "## QA - Run all QA checks and fix issues", + "qa.fix: archi refactor.fix cs.fix lint.fix phpstan test", + "", + "#########", + "# Archi #", + "#########", + "", + "## Architecture - Run architecture checks", + "archi: archi.back", + "", + "## Architecture - Run architecture checks for backend", + "archi.back:", + "\t$(PHP) vendor/bin/deptrac --report-uncovered --fail-on-uncovered", + "", + "############", + "# Refactor #", + "############", + "", + "## Refactor - Run all refactor checks", + "refactor: refactor.back", + "", + "## Refactor - Run all refactor checks and fix issues", + "refactor.fix: refactor.back.fix", + "", + "## Refactor - Run refactor checks for backend", + "refactor.back:", + "\t$(PHP) vendor/bin/rector process --dry-run", + "", + "## Refactor - Run refactor checks for backend and fix issues", + "refactor.back.fix:", + "\t$(PHP) vendor/bin/rector process", + "", + "################", + "# Coding style #", + "################", + "", + "## Coding style - Run all coding style checks", + "cs: cs.back cs.front", + "", + "## Coding style - Run all coding style checks and fix issues", + "cs.fix: cs.back.fix cs.front.fix", + "", + "## Coding style - Check backend coding style", + "cs.back:", + "\t$(PHP) vendor/bin/ecs check", + "\t$(PHP) vendor/bin/twig-cs-fixer", + "", + "## Coding style - Check backend coding style and fix issues", + "cs.back.fix:", + "\t$(PHP) vendor/bin/ecs check --fix", + "\t$(PHP) vendor/bin/twig-cs-fixer --fix", + "", + "## Coding style - Check frontend coding style", + "cs.front: bin/oxfmt", + "\tbin/oxfmt --check", + "", + "## Coding style - Check frontend coding style and fix issues", + "cs.front.fix: bin/oxfmt", + "\tbin/oxfmt", + "", + "bin/oxfmt:", + "\t@$(call action, Downloading oxfmt...)", + "\t$(SF_CONSOLE) oxc:download:oxfmt", + "\t@$(call success, oxfmt downloaded successfully)", + "", + "##########", + "# Linter #", + "##########", + "", + "## Linter - Run all linters", + "lint: lint.back lint.front", + "", + "## Linter - Run all linters and fix issues", + "lint.fix: lint.back lint.front.fix", + "", + "## Linter - Run linters for backend", + "lint.back:", + "\t$(SF_CONSOLE) lint:container", + "\t$(SF_CONSOLE) lint:xliff translations", + "\t$(SF_CONSOLE) lint:yaml --parse-tags config", + "\t$(SF_CONSOLE) lint:twig templates", + "\t#$(SF_CONSOLE) doctrine:schema:validate", + "", + "## Linter - Lint front files", + "lint.front: bin/oxlint", + "\tbin/oxlint", + "", + "## Linter - Lint front files and fix issues", + "lint.front.fix: bin/oxlint", + "\tbin/oxlint --fix", + "", + "bin/oxlint:", + "\t@$(call action, Downloading oxlint...)", + "\t$(SF_CONSOLE) oxc:download:oxlint", + "", + "###########", + "# PHPStan #", + "###########", + "", + "## PHPStan - Run PHPStan", + "phpstan:", + "\t$(PHP) vendor/bin/phpstan analyse", + "", + "## PHPStan - Run PHPStan and update the baseline", + "phpstan.generate-baseline:", + "\t$(PHP) vendor/bin/phpstan analyse --generate-baseline", + "", + "#########", + "# Tests #", + "#########", + "", + "## Tests - Run all tests", + "test: test.back", + "", + "## Tests - Run backend tests", + "test.back:", + "\t$(PHP) vendor/bin/phpunit", + "## Tests - Run backend tests with coverage", + "", + "test.back.coverage:", + "\t$(PHP) vendor/bin/phpunit --coverage-html .cache/phpunit/coverage-html", + "", + "-include $(ROOT_DIR)/Makefile.local" + ] + }, + "files": { + ".github/dependabot.yaml": { + "contents": [ + "# To get started with Dependabot version updates, you'll need to specify which", + "# package ecosystems to update and where the package manifests are located.", + "# Please see the documentation for all configuration options:", + "# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file", + "", + "version: 2", + "updates:", + " - package-ecosystem: \"composer\" # See documentation for possible values", + " directory: \"/\" # Location of package manifests", + " schedule:", + " interval: \"weekly\"", + "" + ], + "executable": false + }, + ".github/workflows/ci.yaml": { + "contents": [ + "name: CI", + "", + "defaults:", + " run:", + " shell: bash", + "", + "on:", + " pull_request:", + " types: [ opened, synchronize, reopened, ready_for_review ]", + " push:", + " branches:", + " - main", + "", + "concurrency:", + " group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}", + " cancel-in-progress: true", + "", + "jobs:", + " qa:", + " if: ${{ ! github.event.pull_request.draft }}", + " name: Quality Assurance", + " timeout-minutes: 10", + " runs-on: ubuntu-latest", + " steps:", + " - name: Checkout", + " uses: actions/checkout@v4", + "", + " - name: Setup PHP", + " uses: shivammathur/setup-php@v2", + " with:", + " extensions: ctype, iconv, mbstring", + " tools: symfony", + "", + " - name: Start Docker containers", + " run: docker compose up -d --wait", + "", + " - name: Install project", + " run: make app.install", + "", + " - name: QA", + " run: make qa", + "" + ], + "executable": false + }, + ".twig-cs-fixer.php": { + "contents": [ + "addStandard(new TwigCsFixer\\Standard\\Symfony());", + "", + "$config = new TwigCsFixer\\Config\\Config();", + "$config->setCacheFile(__DIR__.'/tools/.cache/twig-cs-fixer');", + "$config->setRuleset($ruleset);", + "", + "return $config;", + "" + ], + "executable": false + }, + "Makefile.local": { + "contents": [ + "# Local targets that should not be committed to git", + "" + ], + "executable": false + }, + "ecs.php": { + "contents": [ + "withParallel()", + " ->withCache(__DIR__ . '/tools/.cache/ecs')", + " ->withPaths([", + " __DIR__ . '/assets',", + " __DIR__ . '/config',", + " __DIR__ . '/public',", + " __DIR__ . '/src',", + " __DIR__ . '/tests',", + " __DIR__ . '/tools',", + " ])", + " ->withSkip([", + " __DIR__ . '/tools/.cache',", + " __DIR__ . '/config/bundles.php',", + " __DIR__ . '/config/reference.php',", + " ])", + " ->withPreparedSets(", + " psr12: true,", + " common: true,", + " strict: true,", + " cleanCode: true,", + " )", + ";", + "" + ], + "executable": false + }, + "php.ini": { + "contents": [ + "memory_limit = -1", + "" + ], + "executable": false + }, + "phpstan.dist.neon": { + "contents": [ + "includes:", + " - vendor/kocal/phpstan-symfony-ux/extension.neon", + " - vendor/phpstan/phpstan-symfony/extension.neon", + " - vendor/phpstan/phpstan-doctrine/extension.neon", + " - tools/phpstan/symfony-configuration.php", + "", + "parameters:", + " level: 9", + " paths:", + " - bin/", + " - config/", + " - public/", + " - src/", + " - tools/", + " - tests/", + " excludePaths:", + " - config/reference.php", + " - tools/.cache", + "", + " tmpDir: tools/.cache/phpstan", + "", + " symfony:", + " consoleApplicationLoader: tools/phpstan/console-application.php", + "", + " doctrine:", + " objectManagerLoader: tools/phpstan/object-manager.php", + "", + "rules:", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\ClassMustBeFinalRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\ClassNameMustNotEndWithComponentRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\ForbiddenAttributesPropertyRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\ForbiddenClassPropertyRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\MethodsVisibilityRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\PostMountMethodSignatureRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\PreMountMethodSignatureRule", + " - Kocal\\PHPStanSymfonyUX\\Rules\\TwigComponent\\PublicPropertiesMustBeCamelCaseRule", + "" + ], + "executable": false + }, + "rector.php": { + "contents": [ + "withParallel()", + " ->withCache(__DIR__ . '/tools/.cache/rector')", + " ->withPaths([", + " __DIR__ . '/config',", + " __DIR__ . '/public',", + " __DIR__ . '/src',", + " __DIR__ . '/tests',", + " __DIR__ . '/tools',", + " ])", + " ->withPhpSets()", + " ->withPreparedSets(", + " deadCode: true,", + " codeQuality: true,", + " codingStyle: true,", + " typeDeclarations: true,", + " privatization: true,", + " instanceOf: true,", + " earlyReturn: true,", + " phpunitCodeQuality: true,", + " doctrineCodeQuality: true,", + " symfonyCodeQuality: true,", + " )", + " ->withSkip([", + " __DIR__ . '/config/bundles.php',", + " __DIR__ . '/config/reference.php',", + " __DIR__ . '/tools/.cache',", + " ControllerMethodInjectionToConstructorRector::class,", + " ])", + ";", + "" + ], + "executable": false + }, + "tools/.gitignore": { + "contents": [ + ".cache/", + "" + ], + "executable": false + }, + "tools/make/git.mk": { + "contents": [ + "########", + "# Diff #", + "########", + "", + "# Returns the list of changed files for some given extensions and some given folders.", + "#", + "# @param $1 The file extensions of changed files", + "# @param $2 The relative folders to parse for changed files", + "#", + "# Examples:", + "#", + "# Example #1: list PHP and Javascript files changed in the src and test folders", + "#", + "# $(call git_diff, php js, src test)", + "", + "define git_diff", + "$(shell \\", + "\tfor ext in $(if $(strip $(1)),$(strip $(1)),\"\") ; \\", + "\tdo \\", + "\t\tfor dir in $(if $(strip $(2)),$(strip $(2)),\"\") ; \\", + "\t\tdo \\", + "\t\t\tgit --no-pager diff --name-status \"$$(git merge-base HEAD origin/master)\" \\", + "\t\t\t\t| grep \"$${ext}\\$$\" \\", + "\t\t\t\t| grep \"\\\\s$${dir}\" \\", + "\t\t\t\t| grep -v '^D' \\", + "\t\t\t\t| awk '{ print $$NF }' || true ; \\", + "\t\tdone ; \\", + "\tdone \\", + ")", + "endef", + "" + ], + "executable": false + }, + "tools/make/help.mk": { + "contents": [ + "########", + "# Help #", + "########", + "", + ".DEFAULT_GOAL := help", + "", + "HELP = \\", + "\tUsage: make [$(COLOR_INFO)command$(COLOR_RESET)] \\", + "\t$(call help_section, Help) \\", + "\t$(call help,help,This help)", + "", + "define help_section", + "\t\\n\\n$(COLOR_COMMENT)$(strip $(1)):$(COLOR_RESET)", + "endef", + "", + "define help", + " \\n $(COLOR_INFO)$(1)$(COLOR_RESET) $(2)", + "endef", + "", + "", + "help:", + "\t@printf \"\\n$(HELP)\"", + "\t@awk ' \\", + "\t\tBEGIN { \\", + "\t\t\tsectionsName[1] = \"Commands\" ; \\", + "\t\t\tsectionsCount = 1 ; \\", + "\t\t} \\", + "\t\t/^[-a-zA-Z0-9_.@%\\/+]+:/ { \\", + "\t\t\tif (match(lastLine, /^## (.*)/)) { \\", + "\t\t\t\tcommand = substr($$1, 1, index($$1, \":\") - 1) ; \\", + "\t\t\t\tsection = substr(lastLine, RSTART + 3, index(lastLine, \" - \") - 4) ; \\", + "\t\t\t\tif (section) { \\", + "\t\t\t\t\tmessage = substr(lastLine, index(lastLine, \" - \") + 3, RLENGTH) ; \\", + "\t\t\t\t\tsectionIndex = 0 ; \\", + "\t\t\t\t\tfor (i = 1; i <= sectionsCount; i++) { \\", + "\t\t\t\t\t\tif (sectionsName[i] == section) { \\", + "\t\t\t\t\t\t\tsectionIndex = i ; \\", + "\t\t\t\t\t\t} \\", + "\t\t\t\t\t} \\", + "\t\t\t\t\tif (!sectionIndex) { \\", + "\t\t\t\t\t\tsectionIndex = sectionsCount++ + 1 ; \\", + "\t\t\t\t\t\tsectionsName[sectionIndex] = section ; \\", + "\t\t\t\t\t} \\", + "\t\t\t\t} else { \\", + "\t\t\t\t\tmessage = substr(lastLine, RSTART + 3, RLENGTH) ; \\", + "\t\t\t\t\tsectionIndex = 1 ; \\", + "\t\t\t\t} \\", + "\t\t\t\tif (length(command) > sectionsCommandLength[sectionIndex]) { \\", + "\t\t\t\t\tsectionsCommandLength[sectionIndex] = length(command) ; \\", + "\t\t\t\t} \\", + "\t\t\t\tsectionCommandIndex = sectionsCommandCount[sectionIndex]++ + 1; \\", + "\t\t\t\thelpsCommand[sectionIndex, sectionCommandIndex] = command ; \\", + "\t\t\t\thelpsMessage[sectionIndex, sectionCommandIndex] = message ; \\", + "\t\t\t} \\", + "\t\t} \\", + "\t\t{ lastLine = $$0 } \\", + "\t\tEND { \\", + "\t\t\tfor (i = 1; i <= sectionsCount; i++) { \\", + "\t\t\t\tif (sectionsCommandCount[i]) { \\", + "\t\t\t\t\tprintf \"\\n\\n$(COLOR_COMMENT)%s:$(COLOR_RESET)\", sectionsName[i] ; \\", + "\t\t\t\t\tfor (j = 1; j <= sectionsCommandCount[i]; j++) { \\", + "\t\t\t\t\t\tprintf \"\\n $(COLOR_INFO)%-\" sectionsCommandLength[i] \"s$(COLOR_RESET) %s\", helpsCommand[i, j], helpsMessage[i, j] ; \\", + "\t\t\t\t\t} \\", + "\t\t\t\t} \\", + "\t\t\t} \\", + "\t\t} \\", + "\t' $(MAKEFILE_LIST)", + "\t@printf \"\\n\\n\"", + "", + ".PHONY: help", + "" + ], + "executable": false + }, + "tools/make/os.mk": { + "contents": [ + "######", + "# Os #", + "######", + "", + "# Os detection helpers.", + "#", + "# Examples:", + "#", + "# Example #1: conditions on linux", + "#", + "# echo $(if $(OS_LINUX),Running on Linux,*NOT* running on Linux)", + "", + "ifeq ($(OS),Windows_NT)", + "OS = windows", + "OS_WINDOWS = 1", + "else", + "# See: https://make.mad-scientist.net/deferred-simple-variable-expansion/", + "OS = $(eval OS := $$(shell uname -s | tr '[:upper:]' '[:lower:]'))$(OS)", + "OS_LINUX = $(if $(findstring linux,$(OS)),1)", + "OS_DARWIN = $(if $(findstring darwin,$(OS)),1)", + "endif", + "" + ], + "executable": false + }, + "tools/make/text.mk": { + "contents": [ + "##########", + "# Colors #", + "##########", + "", + "COLOR_RESET := \\033[0m", + "COLOR_BOLD_ITALIC := \\033[1;4m", + "COLOR_ERROR := \\033[31m", + "COLOR_INFO := \\033[32m", + "COLOR_WARNING := \\033[33m", + "COLOR_COMMENT := \\033[36m", + "", + "######################", + "# Special Characters #", + "######################", + "", + "# Usage:", + "# $(call message, Foo$(,) bar) = Foo, bar", + "", + ", := ,", + "", + "########", + "# Time #", + "########", + "", + "# Usage:", + "# $(call time) = 11:06:20", + "", + "define time", + "`date -u +%T`", + "endef", + "", + "###########", + "# Message #", + "###########", + "", + "# Usage:", + "# $(call message, Foo bar) = Foo bar", + "# $(call message_success, Foo bar) = (\u3063\u25d5\u203f\u25d5)\u3063 Foo bar", + "# $(call message_warning, Foo bar) = \u00af\\_(\u30c4)_/\u00af Foo bar", + "# $(call message_error, Foo bar) = (\u256f\u00b0\u25a1\u00b0)\u256f\ufe35 \u253b\u2501\u253b Foo bar", + "", + "define message", + "\tprintf \"$(COLOR_INFO)$(strip $(1))$(COLOR_RESET)\\n\"", + "endef", + "", + "define message_success", + "\tprintf \"$(COLOR_INFO)(\u3063\u25d5\u203f\u25d5)\u3063 $(strip $(1))$(COLOR_RESET)\\n\"", + "endef", + "", + "define message_warning", + "\tprintf \"$(COLOR_WARNING)$(strip $(1))$(COLOR_RESET)\\n\"", + "endef", + "", + "define message_error", + "\tprintf \"$(COLOR_ERROR)(\u256f\u00b0\u25a1\u00b0)\u256f\ufe35 \u253b\u2501\u253b $(strip $(1))$(COLOR_RESET)\\n\"", + "endef", + "", + "###########", + "# Confirm #", + "###########", + "", + "# Usage:", + "# $(call confirm, Foo bar) = \u0f3c \u3064 \u25d5_\u25d5 \u0f3d\u3064 Foo bar (y/N):", + "", + "define confirm", + "\t$(if $(CONFIRM),, \\", + "\t\tprintf \"$(COLOR_INFO) \u0f3c \u3064 \u25d5_\u25d5 \u0f3d\u3064 $(COLOR_WARNING)$(strip $(1)) $(COLOR_RESET)$(COLOR_WARNING)(y/N)$(COLOR_RESET): \"; \\", + "\t\tread CONFIRM ; if [ \"$$CONFIRM\" != \"y\" ]; then printf \"\\n\"; exit 1; fi; \\", + "\t)", + "endef", + "", + "#######", + "# Log #", + "#######", + "", + "# Usage:", + "# $(call log, Foo bar) = [11:06:20] [target] Foo bar", + "# $(call log_warning, Foo bar) = [11:06:20] [target] \u00af\\_(\u30c4)_/\u00af Foo bar", + "# $(call log_error, Foo bar) = [11:06:20] [target] (\u256f\u00b0\u25a1\u00b0)\u256f\ufe35 \u253b\u2501\u253b Foo bar", + "# $(call log_and_call, echo 'Message') = [11:06:20] [target] echo 'Message' then execute the command", + "", + "define log", + "\tprintf \"[$(COLOR_COMMENT)$(call time)$(COLOR_RESET)] [$(COLOR_COMMENT)$(@)$(COLOR_RESET)] \" ; $(call message, $(1))", + "endef", + "", + "define log_warning", + "\tprintf \"[$(COLOR_COMMENT)$(call time)$(COLOR_RESET)] [$(COLOR_COMMENT)$(@)$(COLOR_RESET)] \" ; $(call message_warning, $(1))", + "endef", + "", + "define log_error", + "\tprintf \"[$(COLOR_COMMENT)$(call time)$(COLOR_RESET)] [$(COLOR_COMMENT)$(@)$(COLOR_RESET)] \" ; $(call message_error, $(1))", + "endef", + "", + "##########", + "# Action #", + "##########", + "", + "# Usage:", + "# $(call action, Foo bar) = > Foo bar (in bold)", + "", + "define action", + "\tprintf \"\\n$(COLOR_BOLD_ITALIC)>>> $(strip $(1))$(COLOR_RESET)\\n\\n\"", + "endef", + "" + ], + "executable": false + }, + "tools/phpstan/console-application.php": { + "contents": [ + "bootEnv(__DIR__ . '/../../.env');", + "", + "// @phpstan-ignore-next-line argument.type", + "$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);", + "", + "return new Application($kernel);", + "" + ], + "executable": false + }, + "tools/phpstan/object-manager.php": { + "contents": [ + "bootEnv(__DIR__ . '/../../.env');", + "", + "// @phpstan-ignore-next-line argument.type", + "$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);", + "$kernel->boot();", + "", + "/** @var EntityManager $entityManager */", + "$entityManager = $kernel->getContainer()->get('doctrine')->getManager();", + "", + "return $entityManager;", + "" + ], + "executable": false + }, + "tools/phpstan/symfony-configuration.php": { + "contents": [ + " [", + " 'symfony' => [", + " 'containerXmlPath' => $xmlContainerFile,", + " ],", + " ],", + "];", + "" + ], + "executable": false + } + }, + "ref": "090a78cd1f770682e72243dca41b9e73ec765eb6" + } + } +}