diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7382bb8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.github/workflows/deptrac.yml b/.github/workflows/deptrac.yml index 5aeeafd..441c74d 100644 --- a/.github/workflows/deptrac.yml +++ b/.github/workflows/deptrac.yml @@ -61,7 +61,6 @@ jobs: - name: Install dependencies run: | - composer -q config -g github-oauth.github.com "${{ secrets.GITHUB_TOKEN }}" if [ -f composer.lock ]; then composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader else diff --git a/.github/workflows/infection.yml b/.github/workflows/infection.yml index a36c907..0d39d58 100644 --- a/.github/workflows/infection.yml +++ b/.github/workflows/infection.yml @@ -57,7 +57,6 @@ jobs: - name: Install dependencies run: | - composer -q config -g github-oauth.github.com "${{ secrets.GITHUB_TOKEN }}" if [ -f composer.lock ]; then composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader else diff --git a/.github/workflows/phpcsfixer.yml b/.github/workflows/phpcsfixer.yml index 2b3cdff..06d6453 100644 --- a/.github/workflows/phpcsfixer.yml +++ b/.github/workflows/phpcsfixer.yml @@ -50,7 +50,6 @@ jobs: - name: Install dependencies run: | - composer -q config -g github-oauth.github.com "${{ secrets.GITHUB_TOKEN }}" if [ -f composer.lock ]; then composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader else diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 4241255..6bdd00f 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -65,7 +65,6 @@ jobs: - name: Install dependencies run: | - composer -q config -g github-oauth.github.com "${{ secrets.GITHUB_TOKEN }}" if [ -f composer.lock ]; then composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader else diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 31e88af..c875e33 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -54,7 +54,6 @@ jobs: - name: Install dependencies run: | - composer -q config -g github-oauth.github.com "${{ secrets.GITHUB_TOKEN }}" if [ -f composer.lock ]; then composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader else diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml new file mode 100644 index 0000000..a56ed92 --- /dev/null +++ b/.github/workflows/psalm.yml @@ -0,0 +1,71 @@ +name: Psalm + +on: + pull_request: + branches: + - develop + paths: + - '**.php' + - 'composer.*' + - 'psalm*' + - '.github/workflows/psalm.yml' + push: + branches: + - develop + paths: + - '**.php' + - 'composer.*' + - 'psalm*' + - '.github/workflows/psalm.yml' + +jobs: + build: + name: Psalm Analysis + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[ci skip]')" + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + tools: phpstan, phpunit + extensions: intl, json, mbstring, xml + coverage: none + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Create Psalm cache directory + run: mkdir -p build/psalm + + - name: Cache Psalm results + uses: actions/cache@v3 + with: + path: build/psalm + key: ${{ runner.os }}-psalm-${{ github.sha }} + restore-keys: ${{ runner.os }}-psalm- + + - name: Install dependencies + run: | + if [ -f composer.lock ]; then + composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader + else + composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader + fi + + - name: Run Psalm analysis + run: vendor/bin/psalm diff --git a/.github/workflows/rector.yml b/.github/workflows/rector.yml index d47d834..a945743 100644 --- a/.github/workflows/rector.yml +++ b/.github/workflows/rector.yml @@ -55,7 +55,6 @@ jobs: - name: Install dependencies run: | - composer -q config -g github-oauth.github.com "${{ secrets.GITHUB_TOKEN }}" if [ -f composer.lock ]; then composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader else diff --git a/composer.json b/composer.json index fa001d6..2b6a6cd 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,8 @@ { "name": "tatter/frontend", - "type": "library", "description": "Opinionated suite of frontend tech for CodeIgniter 4", + "license": "MIT", + "type": "library", "keywords": [ "codeigniter", "codeigniter4", @@ -11,8 +12,6 @@ "javascript", "css" ], - "homepage": "https://github.com/tattersoftware/codeigniter4-frontend", - "license": "MIT", "authors": [ { "name": "Matthew Gatner", @@ -21,6 +20,7 @@ "role": "Developer" } ], + "homepage": "https://github.com/tattersoftware/codeigniter4-frontend", "require": { "php": "^7.4 || ^8.0", "almasaeed2010/adminlte": "^3.0", @@ -39,11 +39,18 @@ "codeigniter4/framework": "^4.1", "tatter/tools": "^2.0" }, - "config": { - "allow-plugins": { - "phpstan/extension-installer": true + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/DataTables/Dist-DataTables-Bootstrap4.git" + }, + { + "type": "composer", + "url": "https://asset-packagist.org" } - }, + ], + "minimum-stability": "dev", + "prefer-stable": true, "autoload": { "psr-4": { "Tatter\\Frontend\\": "src" @@ -57,20 +64,16 @@ "Tests\\Support\\": "tests/_support" } }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/DataTables/Dist-DataTables-Bootstrap4.git" - }, - { - "type": "composer", - "url": "https://asset-packagist.org" + "config": { + "allow-plugins": { + "phpstan/extension-installer": true } - ], - "minimum-stability": "dev", - "prefer-stable": true, + }, "scripts": { - "analyze": "phpstan analyze", + "analyze": [ + "phpstan analyze", + "psalm" + ], "ci": [ "Composer\\Config::disableProcessTimeout", "@deduplicate", diff --git a/depfile.yaml b/depfile.yaml deleted file mode 100644 index e5ed3df..0000000 --- a/depfile.yaml +++ /dev/null @@ -1,156 +0,0 @@ -paths: - - ./src/ - - ./vendor/codeigniter4/framework/system/ - - ./vendor/tatter/ -exclude_files: - - '#.*test.*#i' -layers: - - name: Model - collectors: - - type: bool - must: - - type: className - regex: .*[A-Za-z]+Model$ - must_not: - - type: directory - regex: vendor/.* - - name: Vendor Model - collectors: - - type: bool - must: - - type: className - regex: .*[A-Za-z]+Model$ - - type: directory - regex: vendor/.* - - name: Controller - collectors: - - type: bool - must: - - type: className - regex: .*\/Controllers\/.* - must_not: - - type: directory - regex: vendor/.* - - name: Vendor Controller - collectors: - - type: bool - must: - - type: className - regex: .*\/Controllers\/.* - - type: directory - regex: vendor/.* - - name: Config - collectors: - - type: bool - must: - - type: directory - regex: src/Config/.* - must_not: - - type: className - regex: .*Services - - type: directory - regex: vendor/.* - - name: Vendor Config - collectors: - - type: bool - must: - - type: directory - regex: vendor/.*/Config/.* - must_not: - - type: className - regex: .*Services - - name: Entity - collectors: - - type: bool - must: - - type: directory - regex: src/Entities/.* - must_not: - - type: directory - regex: vendor/.* - - name: Vendor Entity - collectors: - - type: bool - must: - - type: directory - regex: vendor/.*/Entities/.* - - name: View - collectors: - - type: bool - must: - - type: directory - regex: src/Views/.* - must_not: - - type: directory - regex: vendor/.* - - name: Vendor View - collectors: - - type: bool - must: - - type: directory - regex: vendor/.*/Views/.* - - name: Service - collectors: - - type: className - regex: .*Services.* -ruleset: - Entity: - - Config - - Model - - Service - - Vendor Config - - Vendor Entity - - Vendor Model - Config: - - Service - - Vendor Config - Model: - - Config - - Entity - - Service - - Vendor Config - - Vendor Entity - - Vendor Model - Service: - - Config - - Vendor Config - - # Ignore anything in the Vendor layers - Vendor Model: - - Config - - Service - - Vendor Config - - Vendor Controller - - Vendor Entity - - Vendor Model - - Vendor View - Vendor Controller: - - Service - - Vendor Config - - Vendor Controller - - Vendor Entity - - Vendor Model - - Vendor View - Vendor Config: - - Config - - Service - - Vendor Config - - Vendor Controller - - Vendor Entity - - Vendor Model - - Vendor View - Vendor Entity: - - Service - - Vendor Config - - Vendor Controller - - Vendor Entity - - Vendor Model - - Vendor View - Vendor View: - - Service - - Vendor Config - - Vendor Controller - - Vendor Entity - - Vendor Model - - Vendor View -skip_violations: diff --git a/deptrac.yaml b/deptrac.yaml new file mode 100644 index 0000000..7cf1eac --- /dev/null +++ b/deptrac.yaml @@ -0,0 +1,156 @@ +parameters: + paths: + - ./src/ + - ./vendor/codeigniter4/framework/system/ + exclude_files: + - '#.*test.*#i' + layers: + - name: Model + collectors: + - type: bool + must: + - type: className + regex: .*[A-Za-z]+Model$ + must_not: + - type: directory + regex: vendor/.* + - name: Vendor Model + collectors: + - type: bool + must: + - type: className + regex: .*[A-Za-z]+Model$ + - type: directory + regex: vendor/.* + - name: Controller + collectors: + - type: bool + must: + - type: className + regex: .*\/Controllers\/.* + must_not: + - type: directory + regex: vendor/.* + - name: Vendor Controller + collectors: + - type: bool + must: + - type: className + regex: .*\/Controllers\/.* + - type: directory + regex: vendor/.* + - name: Config + collectors: + - type: bool + must: + - type: directory + regex: src/Config/.* + must_not: + - type: className + regex: .*Services + - type: directory + regex: vendor/.* + - name: Vendor Config + collectors: + - type: bool + must: + - type: directory + regex: vendor/.*/Config/.* + must_not: + - type: className + regex: .*Services + - name: Entity + collectors: + - type: bool + must: + - type: directory + regex: src/Entities/.* + must_not: + - type: directory + regex: vendor/.* + - name: Vendor Entity + collectors: + - type: bool + must: + - type: directory + regex: vendor/.*/Entities/.* + - name: View + collectors: + - type: bool + must: + - type: directory + regex: src/Views/.* + must_not: + - type: directory + regex: vendor/.* + - name: Vendor View + collectors: + - type: bool + must: + - type: directory + regex: vendor/.*/Views/.* + - name: Service + collectors: + - type: className + regex: .*Services.* + ruleset: + Entity: + - Config + - Model + - Service + - Vendor Config + - Vendor Entity + - Vendor Model + Config: + - Service + - Vendor Config + Model: + - Config + - Entity + - Service + - Vendor Config + - Vendor Entity + - Vendor Model + Service: + - Config + - Vendor Config + + # Ignore anything in the Vendor layers + Vendor Model: + - Config + - Service + - Vendor Config + - Vendor Controller + - Vendor Entity + - Vendor Model + - Vendor View + Vendor Controller: + - Service + - Vendor Config + - Vendor Controller + - Vendor Entity + - Vendor Model + - Vendor View + Vendor Config: + - Config + - Service + - Vendor Config + - Vendor Controller + - Vendor Entity + - Vendor Model + - Vendor View + Vendor Entity: + - Service + - Vendor Config + - Vendor Controller + - Vendor Entity + - Vendor Model + - Vendor View + Vendor View: + - Service + - Vendor Config + - Vendor Controller + - Vendor Entity + - Vendor Model + - Vendor View + skip_violations: diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..386f4dc --- /dev/null +++ b/psalm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/psalm_autoload.php b/psalm_autoload.php new file mode 100644 index 0000000..5a25347 --- /dev/null +++ b/psalm_autoload.php @@ -0,0 +1,20 @@ +import(SetList::DEAD_CODE); - $containerConfigurator->import(LevelSetList::UP_TO_PHP_74); - $containerConfigurator->import(PHPUnitSetList::PHPUNIT_SPECIFIC_METHOD); - $containerConfigurator->import(PHPUnitSetList::PHPUNIT_80); - - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::PARALLEL, true); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->sets([SetList::DEAD_CODE, LevelSetList::UP_TO_PHP_74, PHPUnitSetList::PHPUNIT_SPECIFIC_METHOD, PHPUnitSetList::PHPUNIT_80]); + $rectorConfig->parallel(); // The paths to refactor (can also be supplied with CLI arguments) - $parameters->set(Option::PATHS, [ + $rectorConfig->paths([ __DIR__ . '/src/', __DIR__ . '/tests/', ]); // Include Composer's autoload - required for global execution, remove if running locally - $parameters->set(Option::AUTOLOAD_PATHS, [ + $rectorConfig->autoloadPaths([ __DIR__ . '/vendor/autoload.php', ]); // Do you need to include constants, class aliases, or a custom autoloader? - $parameters->set(Option::BOOTSTRAP_FILES, [ + $rectorConfig->bootstrapFiles([ realpath(getcwd()) . '/vendor/codeigniter4/framework/system/Test/bootstrap.php', ]); if (is_file(__DIR__ . '/phpstan.neon.dist')) { - $parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, __DIR__ . '/phpstan.neon.dist'); + $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon.dist'); } // Set the target version for refactoring - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_74); + $rectorConfig->phpVersion(PhpVersion::PHP_74); // Auto-import fully qualified class names - $parameters->set(Option::AUTO_IMPORT_NAMES, true); + $rectorConfig->importNames(); // Are there files or rules you need to skip? - $parameters->set(Option::SKIP, [ + $rectorConfig->skip([ __DIR__ . '/src/Views', JsonThrowOnErrorRector::class, @@ -98,33 +90,31 @@ // May be uninitialized on purpose AddDefaultValueForUndefinedVariableRector::class, ]); - - // Additional rules to apply - $services = $containerConfigurator->services(); - $services->set(SimplifyUselessVariableRector::class); - $services->set(RemoveAlwaysElseRector::class); - $services->set(CountArrayToEmptyArrayComparisonRector::class); - $services->set(ForToForeachRector::class); - $services->set(ChangeNestedForeachIfsToEarlyContinueRector::class); - $services->set(ChangeIfElseValueAssignToEarlyReturnRector::class); - $services->set(SimplifyStrposLowerRector::class); - $services->set(CombineIfRector::class); - $services->set(SimplifyIfReturnBoolRector::class); - $services->set(InlineIfToExplicitIfRector::class); - $services->set(PreparedValueToEarlyReturnRector::class); - $services->set(ShortenElseIfRector::class); - $services->set(SimplifyIfElseToTernaryRector::class); - $services->set(UnusedForeachValueToArrayKeysRector::class); - $services->set(ChangeArrayPushToArrayAssignRector::class); - $services->set(UnnecessaryTernaryExpressionRector::class); - $services->set(AddPregQuoteDelimiterRector::class); - $services->set(SimplifyRegexPatternRector::class); - $services->set(FuncGetArgsToVariadicParamRector::class); - $services->set(MakeInheritedMethodVisibilitySameAsParentRector::class); - $services->set(SimplifyEmptyArrayCheckRector::class); - $services->set(NormalizeNamespaceByPSR4ComposerAutoloadRector::class); - $services->set(TypedPropertyRector::class) - ->configure([ - TypedPropertyRector::INLINE_PUBLIC => true, + $rectorConfig->rule(SimplifyUselessVariableRector::class); + $rectorConfig->rule(RemoveAlwaysElseRector::class); + $rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class); + $rectorConfig->rule(ForToForeachRector::class); + $rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class); + $rectorConfig->rule(ChangeIfElseValueAssignToEarlyReturnRector::class); + $rectorConfig->rule(SimplifyStrposLowerRector::class); + $rectorConfig->rule(CombineIfRector::class); + $rectorConfig->rule(SimplifyIfReturnBoolRector::class); + $rectorConfig->rule(InlineIfToExplicitIfRector::class); + $rectorConfig->rule(PreparedValueToEarlyReturnRector::class); + $rectorConfig->rule(ShortenElseIfRector::class); + $rectorConfig->rule(SimplifyIfElseToTernaryRector::class); + $rectorConfig->rule(UnusedForeachValueToArrayKeysRector::class); + $rectorConfig->rule(ChangeArrayPushToArrayAssignRector::class); + $rectorConfig->rule(UnnecessaryTernaryExpressionRector::class); + $rectorConfig->rule(AddPregQuoteDelimiterRector::class); + $rectorConfig->rule(SimplifyRegexPatternRector::class); + $rectorConfig->rule(FuncGetArgsToVariadicParamRector::class); + $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class); + $rectorConfig->rule(SimplifyEmptyArrayCheckRector::class); + $rectorConfig->rule(NormalizeNamespaceByPSR4ComposerAutoloadRector::class); + $rectorConfig + ->ruleWithConfiguration(TypedPropertyRector::class, [ + // Set to false if you use in libraries, or it does create breaking changes. + TypedPropertyRector::INLINE_PUBLIC => false, ]); };