From 128fad9c0bdd8162840034faff28ed174bd70bb5 Mon Sep 17 00:00:00 2001 From: Sam Maosa Date: Sat, 13 Apr 2024 00:07:06 +0300 Subject: [PATCH 1/4] Re-write of the module: Core features - Can install the module - Can publish config - Added a ModulesPlugin which must be included in the panel of choice to use the feature - Dependencies: Laravel 10 and nwidart/laravel-modules ^10|^11 - Can automatically register the modules' into the ModulesPlugin - Can optionally use clusters and configure top navigation - New Command: Generate Clusters - New Command: Filament Install in a module (generates directories and files for 1-step setup) - WIP --- .gitattributes | 37 +-- .github/CONTRIBUTING.md | 55 +++++ .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/bug.yml | 116 ++++----- .github/SECURITY.md | 3 + .github/workflows/dependabot-auto-merge.yml | 11 +- .../workflows/fix-php-code-style-issues.yml | 6 +- .github/workflows/phpstan.yml | 26 ++ .github/workflows/run-tests.yml | 12 +- .github/workflows/update-changelog.yml | 4 +- .gitignore | 6 +- .prettierrc | 5 + CHANGELOG.md | 4 + LICENSE.md | 2 +- README.md | 74 ++---- bin/build.js | 50 ++++ composer.json | 147 +++++------ config/filament-modules.php | 10 + config/modules.php | 6 - database/factories/ModelFactory.php | 19 -- .../migrations/create_modules_table.php.stub | 19 -- package.json | 26 ++ .../views/.gitkeep => phpstan-baseline.neon | 0 phpstan.neon.dist | 14 ++ pint.json | 14 ++ postcss.config.cjs | 8 + resources/css/index.css | 1 + resources/dist/.gitkeep | 0 resources/js/index.js | 0 resources/lang/en/modules.php | 6 + src/Commands/ModuleMakePanelCommand.php | 103 -------- src/Commands/ModulesCommand.php | 19 -- .../ModulesFilamentInstallCommand.php | 146 +++++++++++ src/Commands/ModulesMakeClusterCommand.php | 36 +++ src/Commands/stubs/cluster.stub | 29 +++ src/Concerns/CanManipulateFiles.php | 84 +++++++ src/Concerns/GeneratesModularFiles.php | 123 ++++++++++ .../LaravelModulesServiceProvider.php | 21 -- src/Facades/FilamentModules.php | 16 ++ src/Facades/Modules.php | 16 -- src/Modules.php | 44 +++- src/ModulesPlugin.php | 55 +++++ src/ModulesServiceProvider.php | 230 ++++++++++++++---- src/Testing/TestsModules.php | 13 + stubs/.gitkeep | 0 stubs/CustomResourcePage.stub | 13 - stubs/Page.stub | 12 - stubs/PageView.stub | 3 - stubs/PanelProvider.stub | 63 ----- stubs/RelationManager.stub | 51 ---- stubs/Resource.stub | 55 ----- stubs/ResourceEditPage.stub | 19 -- stubs/ResourceListPage.stub | 19 -- stubs/ResourceManagePage.stub | 19 -- stubs/ResourcePage.stub | 12 - stubs/ResourceViewPage.stub | 19 -- stubs/ThemeCss.stub | 3 - stubs/ThemePostcssConfig.stub | 6 - stubs/ThemeTailwindConfig.stub | 10 - tailwind.config.js | 10 + tests/Pest.php | 2 +- tests/TestCase.php | 30 ++- 62 files changed, 1191 insertions(+), 772 deletions(-) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/SECURITY.md create mode 100644 .github/workflows/phpstan.yml create mode 100644 .prettierrc create mode 100644 bin/build.js create mode 100644 config/filament-modules.php delete mode 100644 config/modules.php delete mode 100644 database/factories/ModelFactory.php delete mode 100644 database/migrations/create_modules_table.php.stub create mode 100644 package.json rename resources/views/.gitkeep => phpstan-baseline.neon (100%) create mode 100644 phpstan.neon.dist create mode 100644 pint.json create mode 100644 postcss.config.cjs create mode 100644 resources/css/index.css create mode 100644 resources/dist/.gitkeep create mode 100644 resources/js/index.js create mode 100644 resources/lang/en/modules.php delete mode 100644 src/Commands/ModuleMakePanelCommand.php delete mode 100644 src/Commands/ModulesCommand.php create mode 100644 src/Commands/ModulesFilamentInstallCommand.php create mode 100644 src/Commands/ModulesMakeClusterCommand.php create mode 100644 src/Commands/stubs/cluster.stub create mode 100644 src/Concerns/CanManipulateFiles.php create mode 100644 src/Concerns/GeneratesModularFiles.php delete mode 100644 src/Extensions/LaravelModulesServiceProvider.php create mode 100644 src/Facades/FilamentModules.php delete mode 100644 src/Facades/Modules.php mode change 100755 => 100644 src/Modules.php create mode 100644 src/ModulesPlugin.php create mode 100644 src/Testing/TestsModules.php create mode 100644 stubs/.gitkeep delete mode 100644 stubs/CustomResourcePage.stub delete mode 100644 stubs/Page.stub delete mode 100644 stubs/PageView.stub delete mode 100644 stubs/PanelProvider.stub delete mode 100644 stubs/RelationManager.stub delete mode 100644 stubs/Resource.stub delete mode 100644 stubs/ResourceEditPage.stub delete mode 100644 stubs/ResourceListPage.stub delete mode 100644 stubs/ResourceManagePage.stub delete mode 100644 stubs/ResourcePage.stub delete mode 100644 stubs/ResourceViewPage.stub delete mode 100644 stubs/ThemeCss.stub delete mode 100644 stubs/ThemePostcssConfig.stub delete mode 100644 stubs/ThemeTailwindConfig.stub create mode 100644 tailwind.config.js diff --git a/.gitattributes b/.gitattributes index 9e9519b3..9c9472bc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,18 +2,27 @@ # https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html # Ignore all test and documentation with "export-ignore". -/.github export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/phpunit.xml.dist export-ignore -/art export-ignore -/docs export-ignore -/tests export-ignore -/.editorconfig export-ignore -/.php_cs.dist.php export-ignore -/psalm.xml export-ignore -/psalm.xml.dist export-ignore -/testbench.yaml export-ignore -/UPGRADING.md export-ignore -/phpstan.neon.dist export-ignore +/.github export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.idea export-ignore +/.prettierrc export-ignore +/.package-lock.json export-ignore +/.editorconfig export-ignore +/.php_cs.dist.php export-ignore +/.vscode export-ignore +/art export-ignore +/docs export-ignore +/images export-ignore +/tests export-ignore +/package.json export-ignore /phpstan-baseline.neon export-ignore +/phpstan.neon.dist export-ignore +/postcss.config.js export-ignore +/phpunit.xml.dist export-ignore +/pint.json export-ignore +/psalm.xml export-ignore +/psalm.xml.dist export-ignore +/tailwind.config.js export-ignore +/testbench.yaml export-ignore +/UPGRADING.md export-ignore diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..b0ee5d8e --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +Please read and understand the contribution guide before creating an issue or pull request. + +## Etiquette + +This project is open source, and as such, the maintainers give their free time to build and maintain the source code +held within. They make the code freely available in the hope that it will be of use to other developers. It would be +extremely unfair for them to suffer abuse or anger for their hard work. + +Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the +world that developers are civilized and selfless people. + +It's the duty of the maintainer to ensure that all submissions to the project are of sufficient +quality to benefit the project. Many developers have different skills, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. + +## Viability + +When requesting or submitting new features, first consider whether it might be useful to others. Open +source projects are used by many developers, who may have entirely different needs to your own. Think about +whether or not your feature is likely to be used by other users of the project. + +## Procedure + +Before filing an issue: + +- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. +- Check to make sure your feature suggestion isn't already present within the project. +- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. +- Check the pull requests tab to ensure that the feature isn't already in progress. + +Before submitting a pull request: + +- Check the codebase to ensure that your feature doesn't already exist. +- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. + +## Requirements + +If the project maintainer has any additional requirements, you will find them listed here. + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + +**Happy coding**! diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..6a57d72d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: coolsam diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index fe4cfe6d..8fa85ce7 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -3,64 +3,64 @@ description: Report an Issue or Bug with the Package title: "[Bug]: " labels: ["bug"] body: - - type: markdown - attributes: - value: | - We're sorry to hear you have a problem. Can you help us solve it by providing the following details. - - type: textarea - id: what-happened - attributes: - label: What happened? - description: What did you expect to happen? - placeholder: I cannot currently do X thing because when I do, it breaks X thing. - validations: - required: true - - type: textarea - id: how-to-reproduce - attributes: - label: How to reproduce the bug - description: How did this occur, please add any config values used and provide a set of reliable steps if possible. - placeholder: When I do X I see Y. - validations: - required: true - - type: input - id: package-version - attributes: - label: Package Version - description: What version of our Package are you running? Please be as specific as possible - placeholder: 2.0.0 - validations: - required: true - - type: input - id: php-version - attributes: - label: PHP Version - description: What version of PHP are you running? Please be as specific as possible - placeholder: 8.2.0 - validations: - required: true - - type: input - id: laravel-version - attributes: - label: Laravel Version - description: What version of Laravel are you running? Please be as specific as possible - placeholder: 9.0.0 - validations: - required: true - - type: dropdown - id: operating-systems - attributes: - label: Which operating systems does with happen with? - description: You may select more than one. - multiple: true - options: + - type: markdown + attributes: + value: | + We're sorry to hear you have a problem. Can you help us solve it by providing the following details. + - type: textarea + id: what-happened + attributes: + label: What happened? + description: What did you expect to happen? + placeholder: I cannot currently do X thing because when I do, it breaks X thing. + validations: + required: true + - type: textarea + id: how-to-reproduce + attributes: + label: How to reproduce the bug + description: How did this occur, please add any config values used and provide a set of reliable steps if possible. + placeholder: When I do X I see Y. + validations: + required: true + - type: input + id: package-version + attributes: + label: Package Version + description: What version of our Package are you running? Please be as specific as possible + placeholder: 2.0.0 + validations: + required: true + - type: input + id: php-version + attributes: + label: PHP Version + description: What version of PHP are you running? Please be as specific as possible + placeholder: 8.2.0 + validations: + required: true + - type: input + id: laravel-version + attributes: + label: Laravel Version + description: What version of Laravel are you running? Please be as specific as possible + placeholder: 9.0.0 + validations: + required: true + - type: dropdown + id: operating-systems + attributes: + label: Which operating systems does with happen with? + description: You may select more than one. + multiple: true + options: - macOS - Windows - Linux - - type: textarea - id: notes - attributes: - label: Notes - description: Use this field to provide any other notes that you feel might be relevant to the issue. - validations: - required: false + - type: textarea + id: notes + attributes: + label: Notes + description: Use this field to provide any other notes that you feel might be relevant to the issue. + validations: + required: false diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000..9b6d33f8 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +If you discover any security related issues, please email maosa.sam@gmail.com instead of using the issue tracker. diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 67e66c6a..d2664188 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -1,4 +1,5 @@ -name: dependabot-auto-merge +name: "Dependabot Auto-Merge" + on: pull_request_target permissions: @@ -10,20 +11,20 @@ jobs: runs-on: ubuntu-latest if: ${{ github.actor == 'dependabot[bot]' }} steps: - + - name: Dependabot metadata id: metadata - uses: dependabot/fetch-metadata@v2.0.0 + uses: dependabot/fetch-metadata@v1.6.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - + - name: Auto-merge Dependabot PRs for semver-minor updates if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} run: gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - + - name: Auto-merge Dependabot PRs for semver-patch updates if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} run: gh pr merge --auto --merge "$PR_URL" diff --git a/.github/workflows/fix-php-code-style-issues.yml b/.github/workflows/fix-php-code-style-issues.yml index c958238f..2067a668 100644 --- a/.github/workflows/fix-php-code-style-issues.yml +++ b/.github/workflows/fix-php-code-style-issues.yml @@ -1,4 +1,4 @@ -name: Fix PHP code style issues +name: "Fix PHP Code Styling" on: push: @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.head_ref }} @@ -22,6 +22,6 @@ jobs: uses: aglipanci/laravel-pint-action@2.3.1 - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: Fix styling diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml new file mode 100644 index 00000000..3855a084 --- /dev/null +++ b/.github/workflows/phpstan.yml @@ -0,0 +1,26 @@ +name: PHPStan + +on: + push: + paths: + - '**.php' + - 'phpstan.neon.dist' + +jobs: + phpstan: + name: phpstan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + coverage: none + + - name: Install composer dependencies + uses: ramsey/composer-install@v3 + + - name: Run PHPStan + run: ./vendor/bin/phpstan --error-format=github diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 82f98f2f..7c60f1f3 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -2,13 +2,9 @@ name: run-tests on: push: - branches: - - main - - 3.x + branches: [main] pull_request: - branches: - - main - - 3.x + branches: [main] jobs: test: @@ -23,13 +19,13 @@ jobs: include: - laravel: 10.* testbench: 8.* - carbon: ^2.63 + carbon: 2.* name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml index 8c12ba9e..ec40921c 100644 --- a/.github/workflows/update-changelog.yml +++ b/.github/workflows/update-changelog.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main @@ -24,7 +24,7 @@ jobs: release-notes: ${{ github.event.release.body }} - name: Commit updated CHANGELOG - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v5 with: branch: main commit_message: Update CHANGELOG diff --git a/.gitignore b/.gitignore index a7f372d8..8271e655 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,13 @@ +.DS_Store .idea -.phpunit.cache +.phpunit.result.cache +.vscode build composer.lock coverage docs +node_modules phpunit.xml phpstan.neon testbench.yaml vendor -node_modules diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..98406c6b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b3f303a..1f71dc03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ # Changelog All notable changes to `modules` will be documented in this file. + +## 1.0.0 - 202X-XX-XX + +- initial release diff --git a/LICENSE.md b/LICENSE.md index 628f3beb..99fc78d1 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) coolsam +Copyright (c) coolsam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f580578e..ab265f78 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,55 @@ -

- Tests - Styling - Laravel v9.x - Filament v3.x - PHP 8.1 - Packagist -

+# Organize your Filament Code into modules using nwidart/laravel-modules -Modules is a FilamentPHP Plugin to enable easy integration with `nwidart/laravel-modules` +[![Latest Version on Packagist](https://img.shields.io/packagist/v/coolsam/modules.svg?style=flat-square)](https://packagist.org/packages/coolsam/modules) +[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/coolsam/modules/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/coolsam/modules/actions?query=workflow%3Arun-tests+branch%3Amain) +[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/coolsam/modules/fix-php-code-styling.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/coolsam/modules/actions?query=workflow%3A"Fix+PHP+code+styling"+branch%3Amain) +[![Total Downloads](https://img.shields.io/packagist/dt/coolsam/modules.svg?style=flat-square)](https://packagist.org/packages/coolsam/modules) -**NB: These docs are for v3, which only supports Filament 3. If you are using Filament -v2, [see the documentation here](https://github.com/savannabits/filament-modules/tree/main#readme) to get started.** -## Installation - -Requirements: -1. Filament >= 3 -2. PHP >= 8.1 -3. Laravel >= 9.0 -4. Livewire >= 3.0 -5. nwidart/laravel-modules >=10.0 +This is where your description should go. Limit it to a paragraph or two. Consider adding a small example. ## Installation -- Ensure you have insalled and configured [Laravel Modules (follow these instructions)]() -- Ensure you have installed and configured Filamentphp (follow these instructions) -- You can now install the package via composer: +You can install the package via composer: ```bash composer require coolsam/modules ``` -## Usage +You can publish and run the migrations with: -In this guide we are going to use the `Blog module` as an example +```bash +php artisan vendor:publish --tag="modules-migrations" +php artisan migrate +``` -### Create your laravel module: -If the module that you want to work on does not exist, create it using nwidart/laravel-modules +You can publish the config file with: ```bash -php artisan module:make Blog # Create the blog module +php artisan vendor:publish --tag="modules-config" ``` -### Generate a new Panel inside your module +Optionally, you can publish the views using ```bash -php artisan module:make-filament-panel admin Blog # php artisan module:make-filament-panel [id] [module] +php artisan vendor:publish --tag="modules-views" ``` -If none of the two arguments are passed, the command will ask for each of them interactively. -In this example, if the Panel id passed is `admin` and the module is blog, the command will generate a panel with -id `blog::admin`. This ID should be used in the next step when generating resources, pages and widgets. -### Generate your resources, pages and widgets as usual, selecting the panel you just created above. -From here on, use filament as you would normally to generate `resources`, `Pages` and `Widgets`. Be sure to specify the `--panel` option as the ID generated earlier. -If the `--panel` option is not passed, the command will ask for it interactively. -```bash -# For each of these commands, the package will ask for the Model and Panel. -php artisan make:filament-resource -php artisan make:filament-page -php artisan make:filament-widget +This is the contents of the published config file: + +```php +return [ +]; ``` -```bash -# The Model and Panel arguments are passed inline -php artisan make:filament-resource Author blog::admin -php artisan make:filament-page Library blog::admin -php artisan make:filament-widget BookStats blog::admin +## Usage + +```php +$modules = new Coolsam\Modules(); +echo $modules->echoPhrase('Hello, Coolsam!'); ``` -**All Done!** For each of the panels generated, you can navigate to your `module-path/panel-path` e.g `blog/admin` to acess your panel and links to resources and pages. ## Testing ```bash @@ -82,7 +62,7 @@ Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed re ## Contributing -Please see [CONTRIBUTING](CONTRIBUTING.md) for details. +Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. ## Security Vulnerabilities diff --git a/bin/build.js b/bin/build.js new file mode 100644 index 00000000..3a3439e6 --- /dev/null +++ b/bin/build.js @@ -0,0 +1,50 @@ +import esbuild from 'esbuild' + +const isDev = process.argv.includes('--dev') + +async function compile(options) { + const context = await esbuild.context(options) + + if (isDev) { + await context.watch() + } else { + await context.rebuild() + await context.dispose() + } +} + +const defaultOptions = { + define: { + 'process.env.NODE_ENV': isDev ? `'development'` : `'production'`, + }, + bundle: true, + mainFields: ['module', 'main'], + platform: 'neutral', + sourcemap: isDev ? 'inline' : false, + sourcesContent: isDev, + treeShaking: true, + target: ['es2020'], + minify: !isDev, + plugins: [{ + name: 'watchPlugin', + setup: function (build) { + build.onStart(() => { + console.log(`Build started at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`) + }) + + build.onEnd((result) => { + if (result.errors.length > 0) { + console.log(`Build failed at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`, result.errors) + } else { + console.log(`Build finished at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`) + } + }) + } + }], +} + +compile({ + ...defaultOptions, + entryPoints: ['./resources/js/index.js'], + outfile: './resources/dist/modules.js', +}) diff --git a/composer.json b/composer.json index 27ec7f72..00235b98 100644 --- a/composer.json +++ b/composer.json @@ -1,70 +1,79 @@ { - "name": "coolsam/modules", - "description": "Support for nwidart/laravel-modules in filamentphp", - "keywords": [ - "coolsam", - "laravel", - "modules" - ], - "homepage": "https://github.com/coolsam/modules", - "license": "MIT", - "authors": [ - { - "name": "Sam Maosa", - "email": "smaosa@savannabits.com", - "role": "Developer" - } - ], - "require": { - "php": "^8.1|^8.2", - "spatie/laravel-package-tools": "^1.14.0", - "illuminate/contracts": "^9.1|^10.0|^11.0", - "filament/filament": "^3.0.0", - "nwidart/laravel-modules": "^10.0|^11.0" - }, - "require-dev": { - "laravel/pint": "^1.0", - "nunomaduro/collision": "^8.1", - "orchestra/testbench": "^8.0", - "pestphp/pest": "^2.0", - "pestphp/pest-plugin-arch": "^2.0", - "pestphp/pest-plugin-laravel": "^2.0", - "spatie/laravel-ray": "^1.26" - }, - "autoload": { - "psr-4": { - "Coolsam\\FilamentModules\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Coolsam\\FilamentModules\\Tests\\": "tests/" - } - }, - "scripts": { - "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", - "analyse": "vendor/bin/phpstan analyse", - "test": "vendor/bin/pest", - "test-coverage": "vendor/bin/pest --coverage", - "format": "vendor/bin/pint" - }, - "config": { - "sort-packages": true, - "allow-plugins": { - "pestphp/pest-plugin": true, - "phpstan/extension-installer": true - } - }, - "extra": { - "laravel": { - "providers": [ - "Coolsam\\FilamentModules\\ModulesServiceProvider" - ], - "aliases": { - "Modules": "Coolsam\\FilamentModules\\Facades\\Modules" - } - } - }, - "minimum-stability": "dev", - "prefer-stable": true -} + "name": "coolsam/modules", + "description": "Organize your Filament Code into modules using nwidart/laravel-modules", + "keywords": [ + "coolsam", + "laravel", + "FilamentModules", + "filament" + ], + "homepage": "https://github.com/savannabits/filament-modules", + "support": { + "issues": "https://github.com/savannabits/filament-modules/issues", + "source": "https://github.com/savannabits/filament-modules" + }, + "license": "MIT", + "authors": [ + { + "name": "Sam Maosa", + "email": "maosa.sam@gmail.com", + "role": "Developer" + } + ], + "require": { + "php": "^8.1", + "filament/filament": "^3.0", + "nwidart/laravel-modules": "^10.0", + "spatie/laravel-package-tools": "^1.15.0" + }, + "require-dev": { + "laravel/pint": "^1.0", + "nunomaduro/collision": "^7.9", + "nunomaduro/larastan": "^2.0.1", + "orchestra/testbench": "^8.0", + "pestphp/pest": "^2.1", + "pestphp/pest-plugin-arch": "^2.0", + "pestphp/pest-plugin-laravel": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "spatie/laravel-ray": "^1.26" + }, + "autoload": { + "psr-4": { + "Coolsam\\Modules\\": "src/", + "Coolsam\\Modules\\Database\\Factories\\": "database/factories/" + } + }, + "autoload-dev": { + "psr-4": { + "Coolsam\\Modules\\Tests\\": "tests/" + } + }, + "scripts": { + "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", + "analyse": "vendor/bin/phpstan analyse", + "test": "vendor/bin/pest", + "test-coverage": "vendor/bin/pest --coverage", + "format": "vendor/bin/pint" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "phpstan/extension-installer": true + } + }, + "extra": { + "laravel": { + "providers": [ + "Coolsam\\Modules\\ModulesServiceProvider" + ], + "aliases": { + "FilamentModules": "FilamentModules" + } + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} \ No newline at end of file diff --git a/config/filament-modules.php b/config/filament-modules.php new file mode 100644 index 00000000..008c279a --- /dev/null +++ b/config/filament-modules.php @@ -0,0 +1,10 @@ + true, // whether to auto-register plugins from various modules in the Panel + 'clusters' => [ + 'enabled' => true, // whether to enable the clusters feature which allows you to group each module's filament resources and pages into a cluster + 'use-top-navigation' => true, // display the main cluster menu in the top navigation and the sub-navigation in the side menu, which improves the UI + ], +]; diff --git a/config/modules.php b/config/modules.php deleted file mode 100644 index dcdbf6b0..00000000 --- a/config/modules.php +++ /dev/null @@ -1,6 +0,0 @@ -id(); - - // add fields - - $table->timestamps(); - }); - } -}; diff --git a/package.json b/package.json new file mode 100644 index 00000000..0e3e148c --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev:styles": "npx tailwindcss -i resources/css/index.css -o resources/dist/modules.css --postcss --watch", + "dev:scripts": "node bin/build.js --dev", + "build:styles": "npx tailwindcss -i resources/css/index.css -o resources/dist/modules.css --postcss --minify && npm run purge", + "build:scripts": "node bin/build.js", + "purge": "filament-purge -i resources/dist/modules.css -o resources/dist/modules.css -v 3.x", + "dev": "npm-run-all --parallel dev:*", + "build": "npm-run-all build:*" + }, + "devDependencies": { + "@awcodes/filament-plugin-purge": "^1.1.1", + "@tailwindcss/forms": "^0.5.4", + "@tailwindcss/typography": "^0.5.9", + "autoprefixer": "^10.4.14", + "esbuild": "^0.19.2", + "npm-run-all": "^4.1.5", + "postcss": "^8.4.26", + "postcss-import": "^15.1.0", + "prettier": "^2.7.1", + "prettier-plugin-tailwindcss": "^0.1.13", + "tailwindcss": "^3.3.3" + } +} diff --git a/resources/views/.gitkeep b/phpstan-baseline.neon similarity index 100% rename from resources/views/.gitkeep rename to phpstan-baseline.neon diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..a91953bd --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,14 @@ +includes: + - phpstan-baseline.neon + +parameters: + level: 4 + paths: + - src + - config + - database + tmpDir: build/phpstan + checkOctaneCompatibility: true + checkModelProperties: true + checkMissingIterableValueType: false + diff --git a/pint.json b/pint.json new file mode 100644 index 00000000..c6ddb494 --- /dev/null +++ b/pint.json @@ -0,0 +1,14 @@ +{ + "preset": "laravel", + "rules": { + "blank_line_before_statement": true, + "concat_space": { + "spacing": "one" + }, + "method_argument_space": true, + "single_trait_insert_per_statement": true, + "types_spaces": { + "space": "single" + } + } +} diff --git a/postcss.config.cjs b/postcss.config.cjs new file mode 100644 index 00000000..28553945 --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,8 @@ +module.exports = { + plugins: { + "postcss-import": {}, + "tailwindcss/nesting": {}, + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/resources/css/index.css b/resources/css/index.css new file mode 100644 index 00000000..b3949d56 --- /dev/null +++ b/resources/css/index.css @@ -0,0 +1 @@ +@import '../../vendor/filament/filament/resources/css/theme.css'; diff --git a/resources/dist/.gitkeep b/resources/dist/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/index.js b/resources/js/index.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/lang/en/modules.php b/resources/lang/en/modules.php new file mode 100644 index 00000000..04443e91 --- /dev/null +++ b/resources/lang/en/modules.php @@ -0,0 +1,6 @@ +argument('id') ?? $this->askRequired('ID (e.g. `app`)', 'id')); - - $class = $id - ->studly() - ->append('PanelProvider'); - $module = $this->argument('module'); - if (! $module) { - try { - $usedNow = app('modules')->getUsedNow(); - } catch (\Throwable $exception) { - $usedNow = null; - } - $module = Str::of($this->askRequired('Module Name (e.g. `Sales`)', 'module', $usedNow))->toString(); - } - $this->module = app('modules')->findOrFail($module); - - $path = str($this->module->getExtraPath("{$this->basePath}/$class")) - ->replace('\\', '/') - ->append('.php')->toString(); - - if (! $this->option('force') && $this->checkForCollision([$path])) { - return static::INVALID; - } - $namespace = Str::of($this->basePath) - ->replace('/', '\\') - ->prepend('\\') - ->prepend($this->getModuleNamespace()); - - $moduleJson = static::readModuleJson($this->module->getName()); - // $panelPath = Str::of($this->module->getLowerName()); - $panelPath = $id->prepend('/')->prepend($this->module->getLowerName()); - - if (collect($moduleJson['providers'])->filter( - fn ($provider) => Str::of($provider)->contains('PanelProvider') && ! Str::of($provider)->contains($class) - )->count()) { - $panelPath = $id->prepend('/')->prepend($this->module->getLowerName()); - } - - $this->copyStubToApp('PanelProvider', $path, [ - 'namespace' => $namespace, - 'Module' => $this->module->getStudlyName(), - 'module_namespace' => $this->getModuleNamespace(), - 'class' => $class, - 'directory' => $id->studly()->toString(), - 'panel_path' => $panelPath->toString(), - 'id' => $id->prepend('::')->prepend($this->module->getLowerName())->lcfirst(), - ]); - - $providers = collect($moduleJson['providers']); - $provider = "$namespace\\$class"; - if (! $providers->contains($provider)) { - $moduleJson['providers'][] = $provider; - static::writeToModuleJson($this->module->getName(), $moduleJson); - } - $this->components->info("Successfully created {$class}!"); - - return static::SUCCESS; - } - - public function getModuleNamespace(): string - { - return $this->laravel['modules']->config('namespace').'\\'.$this->module->getName(); - } - - public static function readModuleJson(string $moduleName) - { - return json_decode(file_get_contents(module_path($moduleName, 'module.json')), true); - } - - public static function writeToModuleJson($moduleName, array $data): bool|int - { - $jsonString = json_encode($data, JSON_PRETTY_PRINT); - - return file_put_contents(module_path($moduleName, 'module.json'), $jsonString); - } -} diff --git a/src/Commands/ModulesCommand.php b/src/Commands/ModulesCommand.php deleted file mode 100644 index 4047dd65..00000000 --- a/src/Commands/ModulesCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -comment('All done'); - - return self::SUCCESS; - } -} diff --git a/src/Commands/ModulesFilamentInstallCommand.php b/src/Commands/ModulesFilamentInstallCommand.php new file mode 100644 index 00000000..e04c6554 --- /dev/null +++ b/src/Commands/ModulesFilamentInstallCommand.php @@ -0,0 +1,146 @@ +moduleName = $this->argument('module'); + if (! $this->option('cluster')) { + $this->cluster = confirm('Do you want to organize your code into filament clusters?', true); + } + $module = $this->getModule(true); + // Ensure the Filament directories exist + $this->ensureFilamentDirectoriesExist(); + // Create Filament Plugin + $this->createDefaultFilamentPlugin(); + + if ($this->cluster && confirm('Would you like to create a default Cluster for the module?', true)) { + $this->createDefaultFilamentCluster(); + } + } + + protected function getArguments(): array + { + return [ + ['module', InputArgument::REQUIRED, 'The name of the module in which to install filament support'], + ]; + } + + protected function getOptions(): array + { + return [ + ['cluster', 'C', InputOption::VALUE_NONE], + ]; + } + + protected function promptForMissingArgumentsUsing(): array + { + return [ + 'module' => [ + 'What is the name of the module?', + 'e.g AccessControl, Blog, etc.', + ], + ]; + } + + protected function getModule(): \Nwidart\Modules\Module + { + try { + return Module::findOrFail($this->moduleName); + } catch (ModuleNotFoundException|\Throwable $exception) { + if (confirm("Module $this->moduleName does not exist. Would you like to generate it?", true)) { + $this->call('module:make', ['name' => [$this->moduleName]]); + + return $this->getModule(); + } + $this->error($exception->getMessage()); + exit(1); + } + } + + private function ensureFilamentDirectoriesExist(): void + { + if (! is_dir($dir = $this->getModule()->appPath('Filament'))) { + $this->makeDirectory($dir); + } + + if ($this->cluster) { + if (! is_dir($dir = $this->getModule()->appPath('Filament/Clusters'))) { + $this->makeDirectory($dir); + } + } else { + if (! is_dir($dir = $this->getModule()->appPath('Filament/Pages'))) { + $this->makeDirectory($dir); + } + + if (! is_dir($dir = $this->getModule()->appPath('Filament/Resources'))) { + $this->makeDirectory($dir); + } + + if (! is_dir($dir = $this->getModule()->appPath('Filament/Widgets'))) { + $this->makeDirectory($dir); + } + } + } + + private function makeDirectory(string $dir): void + { + if (! mkdir($dir, 0755, true) && ! is_dir($dir)) { + $this->error(sprintf('Directory "%s" was not created', $dir)); + exit(1); + } + } + + protected function createDefaultFilamentPlugin() + { + $plugin = $this->getModule()->appPath('Filament/Plugin.php'); + if (file_exists($plugin)) { + $this->error('Filament Plugin already exists'); + exit(1); + } + + $this->copyStubToApp('filament-plugin', $plugin); + $this->info('Filament Plugin created successfully'); + } + + protected function createDefaultFilamentCluster() + { + // TODO: Implement + } +} diff --git a/src/Commands/ModulesMakeClusterCommand.php b/src/Commands/ModulesMakeClusterCommand.php new file mode 100644 index 00000000..7580c3d8 --- /dev/null +++ b/src/Commands/ModulesMakeClusterCommand.php @@ -0,0 +1,36 @@ +resolveStubPath('/stubs/cluster.stub'); + } + + protected function stubReplacements(): array + { + return [ + 'moduleStudlyName' => $this->getModule()->getStudlyName(), + 'navigationLabel' => str($this->argument('name'))->kebab()->title()->replace('-', ' ')->toString(), + 'navigationIcon' => 'heroicon-o-squares-2x2', + ]; + } +} diff --git a/src/Commands/stubs/cluster.stub b/src/Commands/stubs/cluster.stub new file mode 100644 index 00000000..d2d4fe6f --- /dev/null +++ b/src/Commands/stubs/cluster.stub @@ -0,0 +1,29 @@ + $paths + */ + protected function checkForCollision(array $paths): bool + { + foreach ($paths as $path) { + if (! $this->fileExists($path)) { + continue; + } + + if (! confirm(basename($path).' already exists, do you want to overwrite it?')) { + $this->components->error("{$path} already exists, aborting."); + + return true; + } + + unlink($path); + } + + return false; + } + + /** + * @param array $replacements + */ + protected function copyStubToApp(string $stub, string $targetPath, array $replacements = []): void + { + $filesystem = app(Filesystem::class); + + $stubPath = $this->getDefaultStubPath()."/{$stub}.stub"; + + $stub = str($filesystem->get($stubPath)); + + foreach ($replacements as $key => $replacement) { + $stub = $stub->replace("{{ {$key} }}", $replacement); + } + + $stub = (string) $stub; + + $this->writeFile($targetPath, $stub); + } + + protected function fileExists(string $path): bool + { + $filesystem = app(Filesystem::class); + + return $filesystem->exists($path); + } + + protected function writeFile(string $path, string $contents): void + { + $filesystem = app(Filesystem::class); + + $filesystem->ensureDirectoryExists( + pathinfo($path, PATHINFO_DIRNAME), + ); + + $filesystem->put($path, $contents); + } + + protected function getDefaultStubPath(): string + { + return $this->getModule()->appPath('Commands/stubs'); + } + + protected function getModule(): Module + { + return FilamentModules::getModule($this->getModuleStudlyName()); + } + + abstract protected function getModuleStudlyName(): string; +} diff --git a/src/Concerns/GeneratesModularFiles.php b/src/Concerns/GeneratesModularFiles.php new file mode 100644 index 00000000..c0fb4e4e --- /dev/null +++ b/src/Concerns/GeneratesModularFiles.php @@ -0,0 +1,123 @@ +argument('module')); + } + + protected function getDefaultNamespace($rootNamespace): string + { + return trim($rootNamespace, '\\').'\\'.trim(Str::replace(DIRECTORY_SEPARATOR, '\\', $this->getRelativeNamespace()), '\\'); + } + + abstract protected function getRelativeNamespace(): string; + + protected function rootNamespace(): string + { + return $this->getModule()->namespace(''); + } + + protected function getPath($name): string + { + $name = Str::replaceFirst($this->rootNamespace(), '', $name); + + return $this->getModule()->getExtraPath(str_replace('\\', '/', $name).'.php'); + } + + protected function possibleModels() + { + $modelPath = $this->getModule()->appPath('Models'); + + return collect(Finder::create()->files()->depth(0)->in($modelPath)) + ->map(fn ($file) => $file->getBasename('.php')) + ->sort() + ->values() + ->all(); + } + + protected function viewPath($path = ''): string + { + $views = $this->getModule()->resourcesPath('views'); + + return $views.($path ? DIRECTORY_SEPARATOR.$path : $path); + } + + protected function buildClass($name) + { + $stub = $this->files->get($this->getStub()); + + return $this->replaceNamespace($stub, $name)->applyStubReplacements($stub)->replaceClass($stub, $name); + } + + protected function applyStubReplacements(&$stub): static + { + foreach ($this->stubReplacements() as $key => $replacement) { + $stub = str_replace(["{{ $key }}", "{{{$key}}}"], $replacement, $stub); + } + + return $this; + } + + protected function stubReplacements(): array + { + return []; + } + + protected function promptForMissingArgumentsUsing(): array + { + return [ + 'name' => [ + 'What should the '.strtolower($this->type ?: 'class').' be named?', + match ($this->type) { + 'Cast' => 'E.g. Json', + 'Channel' => 'E.g. OrderChannel', + 'Console command' => 'E.g. SendEmails', + 'Component' => 'E.g. Alert', + 'Controller' => 'E.g. UserController', + 'Event' => 'E.g. PodcastProcessed', + 'Exception' => 'E.g. InvalidOrderException', + 'Factory' => 'E.g. PostFactory', + 'Job' => 'E.g. ProcessPodcast', + 'Listener' => 'E.g. SendPodcastNotification', + 'Mailable' => 'E.g. OrderShipped', + 'Middleware' => 'E.g. EnsureTokenIsValid', + 'Model' => 'E.g. Flight', + 'Notification' => 'E.g. InvoicePaid', + 'Observer' => 'E.g. UserObserver', + 'Policy' => 'E.g. PostPolicy', + 'Provider' => 'E.g. ElasticServiceProvider', + 'Request' => 'E.g. StorePodcastRequest', + 'Resource' => 'E.g. UserResource', + 'Rule' => 'E.g. Uppercase', + 'Scope' => 'E.g. TrendingScope', + 'Seeder' => 'E.g. UserSeeder', + 'Test' => 'E.g. UserTest', + 'Cluster' => 'E.g Settings', + default => '', + }, + ], + ]; + } +} diff --git a/src/Extensions/LaravelModulesServiceProvider.php b/src/Extensions/LaravelModulesServiceProvider.php deleted file mode 100644 index 17172669..00000000 --- a/src/Extensions/LaravelModulesServiceProvider.php +++ /dev/null @@ -1,21 +0,0 @@ -registerPanels(); - parent::register(); - Log::info('Registered Modules'); - } - - public function registerPanels(): void - { - // Override this to do anything during registration - } -} diff --git a/src/Facades/FilamentModules.php b/src/Facades/FilamentModules.php new file mode 100644 index 00000000..e629ab29 --- /dev/null +++ b/src/Facades/FilamentModules.php @@ -0,0 +1,16 @@ +replace('\\', DIRECTORY_SEPARATOR)->toString(); + + return str($fullPath) + ->replace($base, $relative) + ->replace('.php', '') + ->explode(DIRECTORY_SEPARATOR) + ->map(fn ($piece) => str($piece)->studly()->toString()) + ->implode('\\'); } - public function register(Panel $panel): void + public function execCommand(string $command, ?Command $artisan = null): void { - // + $process = Process::fromShellCommandline($command); + $process->start(); + foreach ($process as $type => $data) { + if (! $artisan) { + echo $data; + } else { + $artisan->info(trim($data)); + } + } } - public function boot(Panel $panel): void + public function packagePath(string $path = ''): string { + //return the base path of this package + return dirname(__DIR__.'../').($path ? DIRECTORY_SEPARATOR.trim($path, DIRECTORY_SEPARATOR) : ''); } } diff --git a/src/ModulesPlugin.php b/src/ModulesPlugin.php new file mode 100644 index 00000000..67ae6a6e --- /dev/null +++ b/src/ModulesPlugin.php @@ -0,0 +1,55 @@ +topNavigation(config('filament-modules.clusters.enabled', false) && config('filament-modules.clusters.use-top-navigation', false)); + foreach ($this->getModulePlugins() as $modulePlugin) { + $panel->plugin($modulePlugin::make()); + } + } + + public function boot(Panel $panel): void + { + } + + public static function make(): static + { + return app(static::class); + } + + public static function get(): static + { + /** @var static $plugin */ + $plugin = filament(app(static::class)->getId()); + + return $plugin; + } + + protected function getModulePlugins(): array + { + if (! config('filament-modules.auto-register-plugins', false)) { + return []; + } + // get a glob of all Filament plugins + $basePath = str(config('modules.paths.modules', 'Modules')); + $pattern = $basePath.'/*/App/Filament/*Plugin.php'; + $pluginPaths = glob($pattern); + + return collect($pluginPaths)->map(fn ($path) => FilamentModules::convertPathToNamespace($path))->toArray(); + + } +} diff --git a/src/ModulesServiceProvider.php b/src/ModulesServiceProvider.php index 0fb3a652..1522659f 100644 --- a/src/ModulesServiceProvider.php +++ b/src/ModulesServiceProvider.php @@ -1,16 +1,24 @@ name('modules') - ->hasConfigFile('modules') - ->hasViews() - ->hasCommands([ - ModuleMakePanelCommand::class, - ]); - } - - public function register() - { - $this->app->register(LaravelModulesServiceProvider::class); - $this->app->singleton('coolsam-modules', Modules::class); - $this->app->afterResolving('filament', function () { - foreach (Filament::getPanels() as $panel) { - $id = \Str::of($panel->getId()); - if ($id->contains('::')) { - $title = $id->replace(['::', '-'], [' ', ' '])->title()->toString(); - $panel - ->renderHook( - 'panels::sidebar.nav.start', - fn () => new HtmlString("

$title

"), - ) - ->renderHook( - 'panels::sidebar.nav.end', - fn () => new HtmlString( - ' - - - - Main Panel - ' - ), - ); - } + $package->name(static::$name) + ->hasCommands($this->getCommands()) + ->hasInstallCommand(function (InstallCommand $command) { + $command + ->publishConfigFile() + ->endWith(function (InstallCommand $command) { + $command->askToStarRepoOnGitHub('savannabits/filament-modules'); + }); + }); + + $configFileName = 'filament-modules'; + + if (file_exists($package->basePath("/../config/{$configFileName}.php"))) { + $package->hasConfigFile($configFileName); + } + + if (file_exists($package->basePath('/../database/migrations'))) { + $package->hasMigrations($this->getMigrations()); + } + + if (file_exists($package->basePath('/../resources/lang'))) { + $package->hasTranslations(); + } + + if (file_exists($package->basePath('/../resources/views'))) { + $package->hasViews(static::$viewNamespace); + } + } + + public function packageRegistered(): void + { + $this->registerModuleMacros(); + } + + public function packageBooted(): void + { + // Asset Registration + FilamentAsset::register( + $this->getAssets(), + $this->getAssetPackageName() + ); + + FilamentAsset::registerScriptData( + $this->getScriptData(), + $this->getAssetPackageName() + ); + + // Icon Registration + FilamentIcon::register($this->getIcons()); + + // Handle Stubs + if (app()->runningInConsole()) { + foreach (app(Filesystem::class)->files(__DIR__.'/../stubs/') as $file) { + $this->publishes([ + $file->getRealPath() => base_path("stubs/modules/{$file->getFilename()}"), + ], 'modules-stubs'); } + } + + // Testing + Testable::mixin(new TestsModules()); + } + + protected function getAssetPackageName(): ?string + { + return 'coolsam/modules'; + } + + /** + * @return array + */ + protected function getAssets(): array + { + return []; + } + + /** + * @return array + */ + protected function getCommands(): array + { + return [ + Commands\ModulesFilamentInstallCommand::class, + Commands\ModulesMakeClusterCommand::class, + ]; + } + + /** + * @return array + */ + protected function getIcons(): array + { + return []; + } + + /** + * @return array + */ + protected function getRoutes(): array + { + return []; + } + + /** + * @return array + */ + protected function getScriptData(): array + { + return []; + } + + /** + * @return array + */ + protected function getMigrations(): array + { + return [ + // 'create_modules_table', + ]; + } + + protected function registerModuleMacros(): void + { + Module::macro('namespace', function (string $relativeNamespace = '') { + $base = trim($this->app['config']->get('modules.namespace', 'Modules'), '\\'); + $relativeNamespace = trim($relativeNamespace, '\\'); + $studlyName = $this->getStudlyName(); + + return trim("{$base}\\{$studlyName}\\{$relativeNamespace}", '\\'); + }); + + Module::macro('getTitle', function () { + return str($this->getStudlyName())->kebab()->title()->replace('-', ' ')->toString(); }); - return parent::register(); + Module::macro('appNamespace', function (string $relativeNamespace = '') { + $relativeNamespace = trim($relativeNamespace, '\\'); + $relativeNamespace = str_replace('App\\', '', $relativeNamespace); + $relativeNamespace = str_replace('App', '', $relativeNamespace); + $relativeNamespace = trim($relativeNamespace, '\\'); + $relativeNamespace = 'App\\'.$relativeNamespace; + + return $this->namespace($relativeNamespace); + }); + Module::macro('appPath', function (string $relativePath = '') { + $appPath = $this->getExtraPath('App'); + + return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + }); + + Module::macro('databasePath', function (string $relativePath = '') { + $appPath = $this->getExtraPath('Database'); + + return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + }); + + Module::macro('resourcesPath', function (string $relativePath = '') { + $appPath = $this->getExtraPath('resources'); + + return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + }); + + Module::macro('migrationsPath', function (string $relativePath = '') { + $appPath = $this->databasePath('migrations'); + + return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + }); + + Module::macro('seedersPath', function (string $relativePath = '') { + $appPath = $this->databasePath('Seeders'); + + return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + }); + + Module::macro('factoriesPath', function (string $relativePath = '') { + $appPath = $this->databasePath('Factories'); + + return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + }); } } diff --git a/src/Testing/TestsModules.php b/src/Testing/TestsModules.php new file mode 100644 index 00000000..63da6ea9 --- /dev/null +++ b/src/Testing/TestsModules.php @@ -0,0 +1,13 @@ + - - diff --git a/stubs/PanelProvider.stub b/stubs/PanelProvider.stub deleted file mode 100644 index 58054289..00000000 --- a/stubs/PanelProvider.stub +++ /dev/null @@ -1,63 +0,0 @@ -getModuleNamespace(); - return $panel - ->id('{{ id }}') - ->path('{{ panel_path }}') - ->colors([ - 'primary' => Color::Teal, - ]) - ->discoverResources(in: module_path($this->module, 'Filament/{{ directory }}/Resources'), for: "$moduleNamespace\\Filament\\{{ directory }}\\Resources") - ->discoverPages(in: module_path($this->module, 'Filament/{{ directory }}/Pages'), for: "$moduleNamespace\\Filament\\{{ directory }}\\Pages") - ->pages([ - Pages\Dashboard::class, - ]) - ->discoverWidgets(in: module_path($this->module, 'Filament/{{ directory }}/Widgets'), for: "$moduleNamespace\\Filament\\{{ directory }}\\Widgets") - ->widgets([ - Widgets\AccountWidget::class, - Widgets\FilamentInfoWidget::class, - ]) - ->middleware([ - EncryptCookies::class, - AddQueuedCookiesToResponse::class, - StartSession::class, - AuthenticateSession::class, - ShareErrorsFromSession::class, - VerifyCsrfToken::class, - SubstituteBindings::class, - DisableBladeIconComponents::class, - DispatchServingFilamentEvent::class, - ]) - ->authMiddleware([ - Authenticate::class, - ]); - } - - protected function getModuleNamespace(): string - { - return config('modules.namespace').'\\'.$this->module; - } -} diff --git a/stubs/RelationManager.stub b/stubs/RelationManager.stub deleted file mode 100644 index 0146dd07..00000000 --- a/stubs/RelationManager.stub +++ /dev/null @@ -1,51 +0,0 @@ -schema([ - Forms\Components\TextInput::make('{{ recordTitleAttribute }}') - ->required() - ->maxLength(255), - ]); - } - - public function table(Table $table): Table - { - return $table -{{ modifyQueryUsing }} - ->columns([ - Tables\Columns\TextColumn::make('{{ recordTitleAttribute }}'), - ]) - ->filters([ -{{ tableFilters }} - ]) - ->headerActions([ -{{ tableHeaderActions }} - ]) - ->actions([ -{{ tableActions }} - ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ -{{ tableBulkActions }} - ]), - ]); - } -} diff --git a/stubs/Resource.stub b/stubs/Resource.stub deleted file mode 100644 index 887d42db..00000000 --- a/stubs/Resource.stub +++ /dev/null @@ -1,55 +0,0 @@ -schema([ -{{ formSchema }} - ]); - } - - public static function table(Table $table): Table - { - return $table - ->columns([ -{{ tableColumns }} - ]) - ->filters([ -{{ tableFilters }} - ]) - ->actions([ -{{ tableActions }} - ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ -{{ tableBulkActions }} - ]), - ]); - } -{{ relations }} - public static function getPages(): array - { - return [ -{{ pages }} - ]; - }{{ eloquentQuery }} -} diff --git a/stubs/ResourceEditPage.stub b/stubs/ResourceEditPage.stub deleted file mode 100644 index 753de152..00000000 --- a/stubs/ResourceEditPage.stub +++ /dev/null @@ -1,19 +0,0 @@ -in(__DIR__); diff --git a/tests/TestCase.php b/tests/TestCase.php index 416fc5c6..80436566 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,10 +1,22 @@ 'Coolsam\\Modules\\Database\\Factories\\'.class_basename($modelName).'Factory' + fn (string $modelName) => 'Coolsam\\Modules\\Database\\Factories\\' . class_basename($modelName) . 'Factory' ); } protected function getPackageProviders($app) { return [ + ActionsServiceProvider::class, + BladeCaptureDirectiveServiceProvider::class, + BladeHeroiconsServiceProvider::class, + BladeIconsServiceProvider::class, + FilamentServiceProvider::class, + FormsServiceProvider::class, + InfolistsServiceProvider::class, + LivewireServiceProvider::class, + NotificationsServiceProvider::class, + SupportServiceProvider::class, + TablesServiceProvider::class, + WidgetsServiceProvider::class, ModulesServiceProvider::class, ]; } From 2561e68726bb7fc3d9cbc90f0808907b036bda1b Mon Sep 17 00:00:00 2001 From: Sam Maosa Date: Sat, 13 Apr 2024 00:26:14 +0300 Subject: [PATCH 2/4] New Feature: Generate a Filament Plugin for a specified Module --- ...p => ModuleMakeFilamentClusterCommand.php} | 6 +- .../ModuleMakeFilamentPluginCommand.php | 34 +++++++++ .../ModulesFilamentInstallCommand.php | 8 +- .../{cluster.stub => filament-cluster.stub} | 0 src/Commands/stubs/filament-plugin.stub | 76 +++++++++++++++++++ src/Concerns/GeneratesModularFiles.php | 3 +- src/ModulesServiceProvider.php | 3 +- 7 files changed, 123 insertions(+), 7 deletions(-) rename src/Commands/{ModulesMakeClusterCommand.php => ModuleMakeFilamentClusterCommand.php} (81%) create mode 100644 src/Commands/ModuleMakeFilamentPluginCommand.php rename src/Commands/stubs/{cluster.stub => filament-cluster.stub} (100%) create mode 100644 src/Commands/stubs/filament-plugin.stub diff --git a/src/Commands/ModulesMakeClusterCommand.php b/src/Commands/ModuleMakeFilamentClusterCommand.php similarity index 81% rename from src/Commands/ModulesMakeClusterCommand.php rename to src/Commands/ModuleMakeFilamentClusterCommand.php index 7580c3d8..fb419667 100644 --- a/src/Commands/ModulesMakeClusterCommand.php +++ b/src/Commands/ModuleMakeFilamentClusterCommand.php @@ -5,7 +5,7 @@ use Coolsam\Modules\Concerns\GeneratesModularFiles; use Illuminate\Console\GeneratorCommand; -class ModulesMakeClusterCommand extends GeneratorCommand +class ModuleMakeFilamentClusterCommand extends GeneratorCommand { use GeneratesModularFiles; @@ -13,7 +13,7 @@ class ModulesMakeClusterCommand extends GeneratorCommand protected $description = 'Create a new Filament cluster class in the module'; - protected $type = 'Cluster'; + protected $type = 'Filament Cluster'; protected function getRelativeNamespace(): string { @@ -22,7 +22,7 @@ protected function getRelativeNamespace(): string protected function getStub(): string { - return $this->resolveStubPath('/stubs/cluster.stub'); + return $this->resolveStubPath('/stubs/filament-cluster.stub'); } protected function stubReplacements(): array diff --git a/src/Commands/ModuleMakeFilamentPluginCommand.php b/src/Commands/ModuleMakeFilamentPluginCommand.php new file mode 100644 index 00000000..aa56a5eb --- /dev/null +++ b/src/Commands/ModuleMakeFilamentPluginCommand.php @@ -0,0 +1,34 @@ +resolveStubPath('/stubs/filament-plugin.stub'); + } + + protected function stubReplacements(): array + { + return [ + 'moduleStudlyName' => $this->getModule()->getStudlyName(), + ]; + } +} diff --git a/src/Commands/ModulesFilamentInstallCommand.php b/src/Commands/ModulesFilamentInstallCommand.php index e04c6554..f334fb37 100644 --- a/src/Commands/ModulesFilamentInstallCommand.php +++ b/src/Commands/ModulesFilamentInstallCommand.php @@ -139,8 +139,12 @@ protected function createDefaultFilamentPlugin() $this->info('Filament Plugin created successfully'); } - protected function createDefaultFilamentCluster() + protected function createDefaultFilamentCluster(): void { - // TODO: Implement + $module = $this->getModule(); + $this->call('module:make:filament-cluster', [ + 'name' => $module->getStudlyName(), + 'module' => $module->getStudlyName(), + ]); } } diff --git a/src/Commands/stubs/cluster.stub b/src/Commands/stubs/filament-cluster.stub similarity index 100% rename from src/Commands/stubs/cluster.stub rename to src/Commands/stubs/filament-cluster.stub diff --git a/src/Commands/stubs/filament-plugin.stub b/src/Commands/stubs/filament-plugin.stub new file mode 100644 index 00000000..de66b90c --- /dev/null +++ b/src/Commands/stubs/filament-plugin.stub @@ -0,0 +1,76 @@ +getModuleName())->lower()->append('-module')->toString(); + } + + public function getModule(): \Nwidart\Modules\Module + { + return Module::findOrFail($this->getModuleName()); + } + + public function register(Panel $panel): void + { + $module = $this->getModule(); + $useClusters = config('filament-modules.clusters.enabled', false); + $panel->discoverPages( + in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Pages'), + for: $module->appNamespace('\\Filament\\Pages') + ); + $panel->discoverResources( + in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Resources'), + for: $module->appNamespace('\\Filament\\Resources') + ); + $panel->discoverWidgets( + in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Widgets'), + for: $module->appNamespace('\\Filament\\Widgets') + ); + + $panel->discoverLivewireComponents( + in: $module->appPath('Livewire'), + for: $module->appNamespace('\\Livewire') + ); + + if ($useClusters) { + $path = $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Clusters'); + $namespace = $module->appNamespace('\\Filament\\Clusters'); + $panel->discoverClusters( + in: $path, + for: $namespace, + ); + } + } + + public function boot(Panel $panel): void + { + // TODO: Implement boot() method. + } + + public static function make(): static + { + return app(static::class); + } + + public static function get(): static + { + /** @var static $plugin */ + $plugin = filament(app(static::class)->getId()); + + return $plugin; + } +} diff --git a/src/Concerns/GeneratesModularFiles.php b/src/Concerns/GeneratesModularFiles.php index c0fb4e4e..30e15e27 100644 --- a/src/Concerns/GeneratesModularFiles.php +++ b/src/Concerns/GeneratesModularFiles.php @@ -114,7 +114,8 @@ protected function promptForMissingArgumentsUsing(): array 'Scope' => 'E.g. TrendingScope', 'Seeder' => 'E.g. UserSeeder', 'Test' => 'E.g. UserTest', - 'Cluster' => 'E.g Settings', + 'Filament Cluster' => 'E.g Settings', + 'Filament Plugin' => 'e.g AccessControlPlugin', default => '', }, ], diff --git a/src/ModulesServiceProvider.php b/src/ModulesServiceProvider.php index 1522659f..01315025 100644 --- a/src/ModulesServiceProvider.php +++ b/src/ModulesServiceProvider.php @@ -109,7 +109,8 @@ protected function getCommands(): array { return [ Commands\ModulesFilamentInstallCommand::class, - Commands\ModulesMakeClusterCommand::class, + Commands\ModuleMakeFilamentClusterCommand::class, + Commands\ModuleMakeFilamentPluginCommand::class, ]; } From 2e437b37ac9b94fa614cd51f9ff24dab18fd6b42 Mon Sep 17 00:00:00 2001 From: Sam Maosa Date: Sat, 13 Apr 2024 20:09:35 +0300 Subject: [PATCH 3/4] New Features: filament:install, module:make:filament-cluster and module:make-filament-plugin commands - Added the Filament Installation command to add filament support to a module - Added a command to generate a Filament plugin in a module - Added a command to generate a Filament cluster in a module - Modified phpstan and removed database as it is not needed --- phpstan.neon.dist | 1 - .../ModuleMakeFilamentPluginCommand.php | 1 + .../ModulesFilamentInstallCommand.php | 15 ++--- src/Commands/stubs/filament-plugin.stub | 57 ++--------------- src/Concerns/ModuleFilamentPlugin.php | 61 +++++++++++++++++++ 5 files changed, 72 insertions(+), 63 deletions(-) create mode 100644 src/Concerns/ModuleFilamentPlugin.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a91953bd..e005ac7d 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -6,7 +6,6 @@ parameters: paths: - src - config - - database tmpDir: build/phpstan checkOctaneCompatibility: true checkModelProperties: true diff --git a/src/Commands/ModuleMakeFilamentPluginCommand.php b/src/Commands/ModuleMakeFilamentPluginCommand.php index aa56a5eb..ee74a59a 100644 --- a/src/Commands/ModuleMakeFilamentPluginCommand.php +++ b/src/Commands/ModuleMakeFilamentPluginCommand.php @@ -29,6 +29,7 @@ protected function stubReplacements(): array { return [ 'moduleStudlyName' => $this->getModule()->getStudlyName(), + 'pluginId' => str($this->argument('name'))->replace('Plugin', '')->studly()->lower()->toString(), ]; } } diff --git a/src/Commands/ModulesFilamentInstallCommand.php b/src/Commands/ModulesFilamentInstallCommand.php index f334fb37..d9ee910e 100644 --- a/src/Commands/ModulesFilamentInstallCommand.php +++ b/src/Commands/ModulesFilamentInstallCommand.php @@ -127,16 +127,13 @@ private function makeDirectory(string $dir): void } } - protected function createDefaultFilamentPlugin() + protected function createDefaultFilamentPlugin(): void { - $plugin = $this->getModule()->appPath('Filament/Plugin.php'); - if (file_exists($plugin)) { - $this->error('Filament Plugin already exists'); - exit(1); - } - - $this->copyStubToApp('filament-plugin', $plugin); - $this->info('Filament Plugin created successfully'); + $module = $this->getModule(); + $this->call('module:make:filament-plugin', [ + 'name' => $module->getStudlyName().'Plugin', + 'module' => $module->getStudlyName(), + ]); } protected function createDefaultFilamentCluster(): void diff --git a/src/Commands/stubs/filament-plugin.stub b/src/Commands/stubs/filament-plugin.stub index de66b90c..1ba03c67 100644 --- a/src/Commands/stubs/filament-plugin.stub +++ b/src/Commands/stubs/filament-plugin.stub @@ -2,13 +2,14 @@ namespace {{ namespace }}; +use Coolsam\Modules\Concerns\ModuleFilamentPlugin; use Filament\Contracts\Plugin; use Filament\Panel; -use Illuminate\Support\Str; -use Nwidart\Modules\Facades\Module; class {{ class }} implements Plugin { + use ModuleFilamentPlugin; + public function getModuleName(): string { return '{{ moduleStudlyName }}'; @@ -16,61 +17,11 @@ class {{ class }} implements Plugin public function getId(): string { - return Str::of($this->getModuleName())->lower()->append('-module')->toString(); - } - - public function getModule(): \Nwidart\Modules\Module - { - return Module::findOrFail($this->getModuleName()); - } - - public function register(Panel $panel): void - { - $module = $this->getModule(); - $useClusters = config('filament-modules.clusters.enabled', false); - $panel->discoverPages( - in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Pages'), - for: $module->appNamespace('\\Filament\\Pages') - ); - $panel->discoverResources( - in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Resources'), - for: $module->appNamespace('\\Filament\\Resources') - ); - $panel->discoverWidgets( - in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Widgets'), - for: $module->appNamespace('\\Filament\\Widgets') - ); - - $panel->discoverLivewireComponents( - in: $module->appPath('Livewire'), - for: $module->appNamespace('\\Livewire') - ); - - if ($useClusters) { - $path = $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Clusters'); - $namespace = $module->appNamespace('\\Filament\\Clusters'); - $panel->discoverClusters( - in: $path, - for: $namespace, - ); - } + return '{{ pluginId }}'; } public function boot(Panel $panel): void { // TODO: Implement boot() method. } - - public static function make(): static - { - return app(static::class); - } - - public static function get(): static - { - /** @var static $plugin */ - $plugin = filament(app(static::class)->getId()); - - return $plugin; - } } diff --git a/src/Concerns/ModuleFilamentPlugin.php b/src/Concerns/ModuleFilamentPlugin.php new file mode 100644 index 00000000..2e22889a --- /dev/null +++ b/src/Concerns/ModuleFilamentPlugin.php @@ -0,0 +1,61 @@ +getModuleName()); + } + + public function register(Panel $panel): void + { + $module = $this->getModule(); + $useClusters = config('filament-modules.clusters.enabled', false); + $panel->discoverPages( + in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Pages'), + for: $module->appNamespace('\\Filament\\Pages') + ); + $panel->discoverResources( + in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Resources'), + for: $module->appNamespace('\\Filament\\Resources') + ); + $panel->discoverWidgets( + in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Widgets'), + for: $module->appNamespace('\\Filament\\Widgets') + ); + + $panel->discoverLivewireComponents( + in: $module->appPath('Livewire'), + for: $module->appNamespace('\\Livewire') + ); + + if ($useClusters) { + $path = $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Clusters'); + $namespace = $module->appNamespace('\\Filament\\Clusters'); + $panel->discoverClusters( + in: $path, + for: $namespace, + ); + } + } + + public static function make(): static + { + return app(static::class); + } + + public static function get(): static + { + /** @var static $plugin */ + $plugin = filament(app(static::class)->getId()); + + return $plugin; + } +} From 3d3580df014b9e6e52090109350c30dba7887bc7 Mon Sep 17 00:00:00 2001 From: coolsam726 Date: Sat, 13 Apr 2024 17:11:42 +0000 Subject: [PATCH 4/4] Fix styling --- src/Commands/ModulesFilamentInstallCommand.php | 4 ++-- src/Concerns/CanManipulateFiles.php | 4 ++-- src/Concerns/GeneratesModularFiles.php | 10 +++++----- src/Concerns/ModuleFilamentPlugin.php | 8 ++++---- src/Modules.php | 2 +- src/ModulesPlugin.php | 2 +- src/ModulesServiceProvider.php | 16 ++++++++-------- tests/TestCase.php | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Commands/ModulesFilamentInstallCommand.php b/src/Commands/ModulesFilamentInstallCommand.php index d9ee910e..3906a4fd 100644 --- a/src/Commands/ModulesFilamentInstallCommand.php +++ b/src/Commands/ModulesFilamentInstallCommand.php @@ -83,7 +83,7 @@ protected function getModule(): \Nwidart\Modules\Module { try { return Module::findOrFail($this->moduleName); - } catch (ModuleNotFoundException|\Throwable $exception) { + } catch (ModuleNotFoundException | \Throwable $exception) { if (confirm("Module $this->moduleName does not exist. Would you like to generate it?", true)) { $this->call('module:make', ['name' => [$this->moduleName]]); @@ -131,7 +131,7 @@ protected function createDefaultFilamentPlugin(): void { $module = $this->getModule(); $this->call('module:make:filament-plugin', [ - 'name' => $module->getStudlyName().'Plugin', + 'name' => $module->getStudlyName() . 'Plugin', 'module' => $module->getStudlyName(), ]); } diff --git a/src/Concerns/CanManipulateFiles.php b/src/Concerns/CanManipulateFiles.php index 5ccc56a2..5dfda8f0 100644 --- a/src/Concerns/CanManipulateFiles.php +++ b/src/Concerns/CanManipulateFiles.php @@ -20,7 +20,7 @@ protected function checkForCollision(array $paths): bool continue; } - if (! confirm(basename($path).' already exists, do you want to overwrite it?')) { + if (! confirm(basename($path) . ' already exists, do you want to overwrite it?')) { $this->components->error("{$path} already exists, aborting."); return true; @@ -39,7 +39,7 @@ protected function copyStubToApp(string $stub, string $targetPath, array $replac { $filesystem = app(Filesystem::class); - $stubPath = $this->getDefaultStubPath()."/{$stub}.stub"; + $stubPath = $this->getDefaultStubPath() . "/{$stub}.stub"; $stub = str($filesystem->get($stubPath)); diff --git a/src/Concerns/GeneratesModularFiles.php b/src/Concerns/GeneratesModularFiles.php index 30e15e27..529ec945 100644 --- a/src/Concerns/GeneratesModularFiles.php +++ b/src/Concerns/GeneratesModularFiles.php @@ -19,7 +19,7 @@ protected function getArguments(): array protected function resolveStubPath($stub): string { - return FilamentModules::packagePath('src/Commands/'.trim($stub, DIRECTORY_SEPARATOR)); + return FilamentModules::packagePath('src/Commands/' . trim($stub, DIRECTORY_SEPARATOR)); } public function getModule(): Module @@ -29,7 +29,7 @@ public function getModule(): Module protected function getDefaultNamespace($rootNamespace): string { - return trim($rootNamespace, '\\').'\\'.trim(Str::replace(DIRECTORY_SEPARATOR, '\\', $this->getRelativeNamespace()), '\\'); + return trim($rootNamespace, '\\') . '\\' . trim(Str::replace(DIRECTORY_SEPARATOR, '\\', $this->getRelativeNamespace()), '\\'); } abstract protected function getRelativeNamespace(): string; @@ -43,7 +43,7 @@ protected function getPath($name): string { $name = Str::replaceFirst($this->rootNamespace(), '', $name); - return $this->getModule()->getExtraPath(str_replace('\\', '/', $name).'.php'); + return $this->getModule()->getExtraPath(str_replace('\\', '/', $name) . '.php'); } protected function possibleModels() @@ -61,7 +61,7 @@ protected function viewPath($path = ''): string { $views = $this->getModule()->resourcesPath('views'); - return $views.($path ? DIRECTORY_SEPARATOR.$path : $path); + return $views . ($path ? DIRECTORY_SEPARATOR . $path : $path); } protected function buildClass($name) @@ -89,7 +89,7 @@ protected function promptForMissingArgumentsUsing(): array { return [ 'name' => [ - 'What should the '.strtolower($this->type ?: 'class').' be named?', + 'What should the ' . strtolower($this->type ?: 'class') . ' be named?', match ($this->type) { 'Cast' => 'E.g. Json', 'Channel' => 'E.g. OrderChannel', diff --git a/src/Concerns/ModuleFilamentPlugin.php b/src/Concerns/ModuleFilamentPlugin.php index 2e22889a..792a8053 100644 --- a/src/Concerns/ModuleFilamentPlugin.php +++ b/src/Concerns/ModuleFilamentPlugin.php @@ -19,15 +19,15 @@ public function register(Panel $panel): void $module = $this->getModule(); $useClusters = config('filament-modules.clusters.enabled', false); $panel->discoverPages( - in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Pages'), + in: $module->appPath('Filament' . DIRECTORY_SEPARATOR . 'Pages'), for: $module->appNamespace('\\Filament\\Pages') ); $panel->discoverResources( - in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Resources'), + in: $module->appPath('Filament' . DIRECTORY_SEPARATOR . 'Resources'), for: $module->appNamespace('\\Filament\\Resources') ); $panel->discoverWidgets( - in: $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Widgets'), + in: $module->appPath('Filament' . DIRECTORY_SEPARATOR . 'Widgets'), for: $module->appNamespace('\\Filament\\Widgets') ); @@ -37,7 +37,7 @@ public function register(Panel $panel): void ); if ($useClusters) { - $path = $module->appPath('Filament'.DIRECTORY_SEPARATOR.'Clusters'); + $path = $module->appPath('Filament' . DIRECTORY_SEPARATOR . 'Clusters'); $namespace = $module->appNamespace('\\Filament\\Clusters'); $panel->discoverClusters( in: $path, diff --git a/src/Modules.php b/src/Modules.php index 22fb65d6..bc43c7df 100644 --- a/src/Modules.php +++ b/src/Modules.php @@ -45,6 +45,6 @@ public function execCommand(string $command, ?Command $artisan = null): void public function packagePath(string $path = ''): string { //return the base path of this package - return dirname(__DIR__.'../').($path ? DIRECTORY_SEPARATOR.trim($path, DIRECTORY_SEPARATOR) : ''); + return dirname(__DIR__ . '../') . ($path ? DIRECTORY_SEPARATOR . trim($path, DIRECTORY_SEPARATOR) : ''); } } diff --git a/src/ModulesPlugin.php b/src/ModulesPlugin.php index 67ae6a6e..7de25762 100644 --- a/src/ModulesPlugin.php +++ b/src/ModulesPlugin.php @@ -46,7 +46,7 @@ protected function getModulePlugins(): array } // get a glob of all Filament plugins $basePath = str(config('modules.paths.modules', 'Modules')); - $pattern = $basePath.'/*/App/Filament/*Plugin.php'; + $pattern = $basePath . '/*/App/Filament/*Plugin.php'; $pluginPaths = glob($pattern); return collect($pluginPaths)->map(fn ($path) => FilamentModules::convertPathToNamespace($path))->toArray(); diff --git a/src/ModulesServiceProvider.php b/src/ModulesServiceProvider.php index 01315025..0f1f0d12 100644 --- a/src/ModulesServiceProvider.php +++ b/src/ModulesServiceProvider.php @@ -78,7 +78,7 @@ public function packageBooted(): void // Handle Stubs if (app()->runningInConsole()) { - foreach (app(Filesystem::class)->files(__DIR__.'/../stubs/') as $file) { + foreach (app(Filesystem::class)->files(__DIR__ . '/../stubs/') as $file) { $this->publishes([ $file->getRealPath() => base_path("stubs/modules/{$file->getFilename()}"), ], 'modules-stubs'); @@ -167,44 +167,44 @@ protected function registerModuleMacros(): void $relativeNamespace = str_replace('App\\', '', $relativeNamespace); $relativeNamespace = str_replace('App', '', $relativeNamespace); $relativeNamespace = trim($relativeNamespace, '\\'); - $relativeNamespace = 'App\\'.$relativeNamespace; + $relativeNamespace = 'App\\' . $relativeNamespace; return $this->namespace($relativeNamespace); }); Module::macro('appPath', function (string $relativePath = '') { $appPath = $this->getExtraPath('App'); - return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + return $appPath . ($relativePath ? DIRECTORY_SEPARATOR . $relativePath : ''); }); Module::macro('databasePath', function (string $relativePath = '') { $appPath = $this->getExtraPath('Database'); - return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + return $appPath . ($relativePath ? DIRECTORY_SEPARATOR . $relativePath : ''); }); Module::macro('resourcesPath', function (string $relativePath = '') { $appPath = $this->getExtraPath('resources'); - return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + return $appPath . ($relativePath ? DIRECTORY_SEPARATOR . $relativePath : ''); }); Module::macro('migrationsPath', function (string $relativePath = '') { $appPath = $this->databasePath('migrations'); - return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + return $appPath . ($relativePath ? DIRECTORY_SEPARATOR . $relativePath : ''); }); Module::macro('seedersPath', function (string $relativePath = '') { $appPath = $this->databasePath('Seeders'); - return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + return $appPath . ($relativePath ? DIRECTORY_SEPARATOR . $relativePath : ''); }); Module::macro('factoriesPath', function (string $relativePath = '') { $appPath = $this->databasePath('Factories'); - return $appPath.($relativePath ? DIRECTORY_SEPARATOR.$relativePath : ''); + return $appPath . ($relativePath ? DIRECTORY_SEPARATOR . $relativePath : ''); }); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 80436566..c6b35099 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,6 +4,7 @@ use BladeUI\Heroicons\BladeHeroiconsServiceProvider; use BladeUI\Icons\BladeIconsServiceProvider; +use Coolsam\Modules\ModulesServiceProvider; use Filament\Actions\ActionsServiceProvider; use Filament\FilamentServiceProvider; use Filament\Forms\FormsServiceProvider; @@ -16,7 +17,6 @@ use Livewire\LivewireServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; use RyanChandler\BladeCaptureDirective\BladeCaptureDirectiveServiceProvider; -use Coolsam\Modules\ModulesServiceProvider; class TestCase extends Orchestra {