diff --git a/.editorconfig b/.editorconfig index a7c44dd..dd9a2b5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,9 +2,9 @@ root = true [*] charset = utf-8 +end_of_line = lf indent_size = 4 indent_style = space -end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true diff --git a/.gitattributes b/.gitattributes index 9e9519b..9c9472b 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 index b4ae1c4..b0ee5d8 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -14,7 +14,7 @@ Please be considerate towards maintainers when raising issues or presenting pull 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 skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. +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 diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..8fa85ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,66 @@ +name: Bug Report +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: + - 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 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 80a788c..96701be 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -9,6 +9,3 @@ contact_links: - name: Report a security issue url: https://github.com/:vendor_name/:package_name/security/policy about: Learn how to notify us for sensitive bugs - - name: Report a bug - url: https://github.com/:vendor_name/:package_name/issues/new - about: Report a reproducable bug diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 78b1967..d266418 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: @@ -29,4 +30,4 @@ jobs: run: gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/fix-php-code-style-issues.yml b/.github/workflows/fix-php-code-style-issues.yml deleted file mode 100644 index 4ecdb9f..0000000 --- a/.github/workflows/fix-php-code-style-issues.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Fix PHP code style issues - -on: [push] - -jobs: - php-code-styling: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} - - - name: Fix PHP code style issues - uses: aglipanci/laravel-pint-action@1.0.0 - - - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Fix styling \ No newline at end of file diff --git a/.github/workflows/fix-php-code-styling.yml b/.github/workflows/fix-php-code-styling.yml new file mode 100644 index 0000000..a17569a --- /dev/null +++ b/.github/workflows/fix-php-code-styling.yml @@ -0,0 +1,27 @@ +name: "Fix PHP Code Styling" + +on: + push: + paths: + - '**.php' + +permissions: + contents: write + +jobs: + php-code-styling: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Fix PHP code style issues + uses: aglipanci/laravel-pint-action@2.3.0 + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Fix styling diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 10f7697..ccfa9d9 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -11,7 +11,7 @@ jobs: name: phpstan runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -20,7 +20,7 @@ jobs: coverage: none - name: Install composer dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v2 - name: Run PHPStan - run: ./vendor/bin/phpstan --error-format=github \ No newline at end of file + run: ./vendor/bin/phpstan --error-format=github diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3efc226..7fefe35 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,18 +13,19 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - php: [8.1] - laravel: [9.*] + php: [8.2, 8.1] + laravel: [10.*] stability: [prefer-lowest, prefer-stable] include: - - laravel: 9.* - testbench: 7.* + - laravel: 10.* + testbench: 8.* + carbon: ^2.63 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 @@ -40,8 +41,11 @@ jobs: - name: Install dependencies run: | - composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:${{ matrix.carbon }}" --no-interaction --no-update composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: List Installed Dependencies + run: composer show -D + - name: Execute tests - run: vendor/bin/pest \ No newline at end of file + run: vendor/bin/pest --ci diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml index 7787ca1..ec40921 100644 --- a/.github/workflows/update-changelog.yml +++ b/.github/workflows/update-changelog.yml @@ -4,13 +4,16 @@ on: release: types: [released] +permissions: + contents: write + jobs: update: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main @@ -21,8 +24,8 @@ 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 - file_pattern: CHANGELOG.md \ No newline at end of file + file_pattern: CHANGELOG.md diff --git a/.gitignore b/.gitignore index 9a43686..8271e65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,13 @@ +.DS_Store .idea -.php_cs -.php_cs.cache .phpunit.result.cache +.vscode build composer.lock coverage docs +node_modules phpunit.xml phpstan.neon testbench.yaml vendor -node_modules -.php-cs-fixer.cache diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..98406c6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/README.md b/README.md index 6bb3391..02d882d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # :package_description [![Latest Version on Packagist](https://img.shields.io/packagist/v/:vendor_slug/:package_slug.svg?style=flat-square)](https://packagist.org/packages/:vendor_slug/:package_slug) -[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/:vendor_slug/:package_slug/run-tests?label=tests)](https://github.com/:vendor_slug/:package_slug/actions?query=workflow%3Arun-tests+branch%3Amain) -[![GitHub Code Style Action Status](https://img.shields.io/github/workflow/status/:vendor_slug/:package_slug/Check%20&%20fix%20styling?label=code%20style)](https://github.com/:vendor_slug/:package_slug/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amain) +[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/:vendor_slug/:package_slug/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/:vendor_slug/:package_slug/actions?query=workflow%3Arun-tests+branch%3Amain) +[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/:vendor_slug/:package_slug/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/:vendor_slug/:package_slug/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) [![Total Downloads](https://img.shields.io/packagist/dt/:vendor_slug/:package_slug.svg?style=flat-square)](https://packagist.org/packages/:vendor_slug/:package_slug) @@ -54,8 +54,8 @@ return [ ## Usage ```php -$skeleton = new VendorName\Skeleton(); -echo $skeleton->echoPhrase('Hello, VendorName!'); +$variable = new VendorName\Skeleton(); +echo $variable->echoPhrase('Hello, VendorName!'); ``` ## Testing diff --git a/bin/build.js b/bin/build.js new file mode 100644 index 0000000..913baf4 --- /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/skeleton.js', +}) diff --git a/composer.json b/composer.json index b3dc5b9..53946f2 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,10 @@ ":package_slug" ], "homepage": "https://github.com/:vendor_slug/:package_slug", + "support": { + "issues": "https://github.com/:vendor_slug/:package_slug/issues", + "source": "https://github.com/:vendor_slug/:package_slug" + }, "license": "MIT", "authors": [ { @@ -16,50 +20,47 @@ } ], "require": { - "php": "^8.0", - "filament/filament": "^2.0", - "spatie/laravel-package-tools": "^1.9.2", - "illuminate/contracts": "^9.0" + "php": "^8.1", + "filament/filament": "^3.0", + "filament/forms": "^3.0", + "filament/tables": "^3.0", + "spatie/laravel-package-tools": "^1.15.0", + "illuminate/contracts": "^10.0" }, "require-dev": { "laravel/pint": "^1.0", - "nunomaduro/collision": "^6.0", + "nunomaduro/collision": "^7.9", "nunomaduro/larastan": "^2.0.1", - "orchestra/testbench": "^7.0", - "pestphp/pest": "^1.21", - "pestphp/pest-plugin-laravel": "^1.1", - "pestphp/pest-plugin-livewire": "^1.0", - "pestphp/pest-plugin-parallel": "^0.3", + "orchestra/testbench": "^8.0", + "pestphp/pest": "^2.0", + "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", - "phpunit/phpunit": "^9.5", "spatie/laravel-ray": "^1.26" }, "autoload": { "psr-4": { - "VendorName\\Skeleton\\": "src", - "VendorName\\Skeleton\\Database\\Factories\\": "database/factories" + "VendorName\\Skeleton\\": "src/", + "VendorName\\Skeleton\\Database\\Factories\\": "database/factories/" } }, "autoload-dev": { "psr-4": { - "VendorName\\Skeleton\\Tests\\": "tests" + "VendorName\\Skeleton\\Tests\\": "tests/" } }, "scripts": { - "pint": "vendor/bin/pint", - "test:pest": "vendor/bin/pest --parallel", - "test:phpstan": "vendor/bin/phpstan analyse", - "test": [ - "@test:pest", - "@test:phpstan" - ] + "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": { - "composer/package-versions-deprecated": true, "pestphp/pest-plugin": true, "phpstan/extension-installer": true } @@ -76,4 +77,4 @@ }, "minimum-stability": "dev", "prefer-stable": true -} \ No newline at end of file +} diff --git a/configure.php b/configure.php index 6bf01ac..a3540d8 100644 --- a/configure.php +++ b/configure.php @@ -7,14 +7,16 @@ $gitEmail = run('git config user.email'); $authorEmail = ask('Author email', $gitEmail); -$usernameGuess = explode(':', run('git config remote.origin.url'))[1]; -$usernameGuess = dirname($usernameGuess); -$usernameGuess = basename($usernameGuess); +$usernameGuess = explode(':', run('git config remote.origin.url'))[1] ?? ''; +if ($usernameGuess !== '') { + $usernameGuess = dirname($usernameGuess); + $usernameGuess = basename($usernameGuess); +} $authorUsername = ask('Author username', $usernameGuess); $vendorName = ask('Vendor name', $authorUsername); $vendorSlug = slugify($vendorName); -$vendorNamespace = ucwords($vendorName); +$vendorNamespace = str_replace('-', '', ucwords($vendorName)); $vendorNamespace = ask('Vendor namespace', $vendorNamespace); $currentDirectory = getcwd(); @@ -22,40 +24,101 @@ $packageName = ask('Package name', $folderName); $packageSlug = slugify($packageName); -$packageSlugWithoutPrefix = remove_prefix('laravel-', $packageSlug); +$packageSlugWithoutPrefix = removePrefix('filament-', $packageSlug); -$className = title_case($packageName); +$className = titleCase($packageName); $className = ask('Class name', $className); -$description = ask('Package description', "This is my package {$packageSlug}"); +$variableName = lcfirst($className); +$description = ask('Package description', "This is my package $packageSlug"); $usePhpStan = confirm('Enable PhpStan?', true); $usePint = confirm('Enable Pint?', true); +$useDependabot = confirm('Enable Dependabot?', true); +$useLaravelRay = confirm('Enable Ray?', true); $useUpdateChangelogWorkflow = confirm('Use automatic changelog updater workflow?', true); +$isTheme = confirm('Is this a custom theme?'); +$formsOnly = ! $isTheme && confirm('Is this for Forms only?'); +$tablesOnly = ! ($isTheme || $formsOnly) && confirm('Is this for Tables only?'); + +writeln("\r"); writeln('------'); -writeln("Author : {$authorName} ({$authorUsername}, {$authorEmail})"); -writeln("Vendor : {$vendorName} ({$vendorSlug})"); -writeln("Package : {$packageSlug} <{$description}>"); -writeln("Namespace : {$vendorNamespace}\\{$className}"); -writeln("Class name : {$className}"); +writeln("Author : \e[0;36m$authorName ($authorUsername, $authorEmail)\e[0m"); +writeln("Vendor : \e[0;36m$vendorName ($vendorSlug)\e[0m"); +writeln('Package : ' . "\e[0;36m" . $packageSlug . ($description ? " <{$description}>" : '') . "\e[0m"); +writeln("Namespace : \e[0;36m$vendorNamespace\\$className\e[0m"); +writeln("Class name : \e[0;36m$className\e[0m"); writeln('---'); -writeln('Packages & Utilities'); -writeln('Use PhpCsFixer : ' . ($usePhpCsFixer ? 'yes' : 'no')); -writeln('Use Larastan/PhpStan : ' . ($usePhpStan ? 'yes' : 'no')); -writeln('Use Pint : ' . ($usePint ? 'yes' : 'no')); -writeln('Use Auto-Changelog : ' . ($useUpdateChangelogWorkflow ? 'yes' : 'no')); +writeln("\e[1;37mPackages & Utilities\e[0m"); +writeln('Larastan/PhpStan : ' . ($usePhpStan ? "\e[0;32mYes" : "\e[0;31mNo") . "\e[0m"); +writeln('Pint : ' . ($usePint ? "\e[0;32mYes" : "\e[0;31mNo") . "\e[0m"); +writeln('Use Dependabot : ' . ($useDependabot ? "\e[0;32mYes" : "\e[0;31mNo") . "\e[0m"); +writeln('Use Ray : ' . ($useLaravelRay ? "\e[0;32mYes" : "\e[0;31mNo") . "\e[0m"); +writeln('Auto-Changelog : ' . ($useUpdateChangelogWorkflow ? "\e[0;32mYes" : "\e[0;31mNo") . "\e[0m"); +if ($formsOnly) { + writeln("Filament/Forms : \e[0;32mYes\e[0m"); +} elseif ($tablesOnly) { + writeln("Filament/Tables : \e[0;32mYes\e[0m"); +} else { + writeln("Filament/Filament : \e[0;32mYes\e[0m"); +} writeln('------'); - +writeln("\r"); writeln('This script will replace the above values in all relevant files in the project directory.'); +writeln("\r"); if (! confirm('Modify files?', true)) { exit(1); } +if ($formsOnly) { + safeUnlink(__DIR__ . '/src/SkeletonTheme.php'); + safeUnlink(__DIR__ . '/src/SkeletonPlugin.php'); + + removeComposerDeps([ + 'filament/filament', + 'filament/tables', + ], 'require'); +} elseif ($tablesOnly) { + safeUnlink(__DIR__ . '/src/SkeletonTheme.php'); + safeUnlink(__DIR__ . '/src/SkeletonPlugin.php'); + + removeComposerDeps([ + 'filament/filament', + 'filament/forms', + ], 'require'); +} else { + if ($isTheme) { + safeUnlink(__DIR__ . '/src/SkeletonServiceProvider.php'); + safeUnlink(__DIR__ . '/src/SkeletonPlugin.php'); + safeUnlink(__DIR__ . '/src/Skeleton.php'); + removeDirectory(__DIR__ . '/bin'); + removeDirectory(__DIR__ . '/config'); + removeDirectory(__DIR__ . '/database'); + removeDirectory(__DIR__ . '/stubs'); + removeDirectory(__DIR__ . '/resources/js'); + removeDirectory(__DIR__ . '/resources/lang'); + removeDirectory(__DIR__ . '/resources/views'); + removeDirectory(__DIR__ . '/src/Commands'); + removeDirectory(__DIR__ . '/src/Facades'); + removeDirectory(__DIR__ . '/src/Testing'); + + setupPackageJsonForTheme(); + + } else { + safeUnlink(__DIR__ . '/src/SkeletonTheme.php'); + } + + removeComposerDeps([ + 'filament/forms', + 'filament/tables', + ], 'require'); +} + $files = (str_starts_with(strtoupper(PHP_OS), 'WIN') ? replaceForWindows() : replaceForAllOtherOSes()); foreach ($files as $file) { - replace_in_file($file, [ + replaceInFile($file, [ ':author_name' => $authorName, ':author_username' => $authorUsername, 'author@domain.com' => $authorEmail, @@ -67,30 +130,34 @@ ':package_slug_without_prefix' => $packageSlugWithoutPrefix, 'Skeleton' => $className, 'skeleton' => $packageSlug, + 'migration_table_name' => titleSnake($packageSlug), + 'variable' => $variableName, ':package_description' => $description, ]); match (true) { str_contains($file, determineSeparator('src/Skeleton.php')) => rename($file, determineSeparator('./src/' . $className . '.php')), str_contains($file, determineSeparator('src/SkeletonServiceProvider.php')) => rename($file, determineSeparator('./src/' . $className . 'ServiceProvider.php')), + str_contains($file, determineSeparator('src/SkeletonTheme.php')) => rename($file, determineSeparator('./src/' . $className . 'Theme.php')), + str_contains($file, determineSeparator('src/SkeletonPlugin.php')) => rename($file, determineSeparator('./src/' . $className . 'Plugin.php')), str_contains($file, determineSeparator('src/Facades/Skeleton.php')) => rename($file, determineSeparator('./src/Facades/' . $className . '.php')), str_contains($file, determineSeparator('src/Commands/SkeletonCommand.php')) => rename($file, determineSeparator('./src/Commands/' . $className . 'Command.php')), - str_contains($file, determineSeparator('database/migrations/create_skeleton_table.php.stub')) => rename($file, determineSeparator('./database/migrations/create_' . $packageSlugWithoutPrefix . '_table.php.stub')), + str_contains($file, determineSeparator('src/Testing/TestsSkeleton.php')) => rename($file, determineSeparator('./src/Testing/Tests' . $className . '.php')), + str_contains($file, determineSeparator('database/migrations/create_skeleton_table.php.stub')) => rename($file, determineSeparator('./database/migrations/create_' . titleSnake($packageSlugWithoutPrefix) . '_table.php.stub')), str_contains($file, determineSeparator('config/skeleton.php')) => rename($file, determineSeparator('./config/' . $packageSlugWithoutPrefix . '.php')), - str_contains($file, 'README.md') => remove_readme_paragraphs($file), + str_contains($file, determineSeparator('resources/lang/en/skeleton.php')) => rename($file, determineSeparator('./resources/lang/en/' . $packageSlugWithoutPrefix . '.php')), + str_contains($file, 'README.md') => removeTag($file, 'delete'), default => [], }; } -if (! $usePint) { - safeUnlink(__DIR__ . '/pint.json'); - safeUnlink(__DIR__ . '/.github/workflows/pint.yml'); - - remove_composer_deps([ - 'laravel/pint', - ]); +if (! $useDependabot) { + safeUnlink(__DIR__ . '/.github/dependabot.yml'); + safeUnlink(__DIR__ . '/.github/workflows/dependabot-auto-merge.yml'); +} - remove_composer_script(['pint']); +if (! $useLaravelRay) { + removeComposerDeps(['spatie/laravel-ray'], 'require-dev'); } if (! $usePhpStan) { @@ -98,32 +165,41 @@ safeUnlink(__DIR__ . '/phpstan-baseline.neon'); safeUnlink(__DIR__ . '/.github/workflows/phpstan.yml'); - remove_composer_deps([ + removeComposerDeps([ 'phpstan/extension-installer', 'phpstan/phpstan-deprecation-rules', 'phpstan/phpstan-phpunit', 'nunomaduro/larastan', - ]); + ], 'require-dev'); - remove_composer_script([ - 'test:phpstan', - '@test:phpstan', - ]); + removeComposerDeps(['analyse'], 'scripts'); +} + +if (! $usePint) { + safeUnlink(__DIR__ . '/.github/workflows/fix-php-code-style-issues.yml'); + safeUnlink(__DIR__ . '/pint.json'); + + removeComposerDeps([ + 'laravel/pint', + ], 'require-dev'); + + removeComposerDeps(['format'], 'scripts'); } if (! $useUpdateChangelogWorkflow) { safeUnlink(__DIR__ . '/.github/workflows/update-changelog.yml'); } -confirm('Execute `composer install` and run tests?') && run('composer install && composer test'); +confirm('Execute `composer install`?') && run('composer install'); -confirm('Let this script delete itself?', true) && unlink(__FILE__); +if (confirm('Let this script delete itself?', true)) { + unlink(__FILE__); +} function ask(string $question, string $default = ''): string { - $consoleColor = new ConsoleColor(); - $def = $default ? $consoleColor->apply('yellow', " ({$default})") : null; - $answer = readline($consoleColor->apply('green', $question . $def . ': ')); + $def = $default ? "\e[0;33m ($default)" : ''; + $answer = readline("\e[0;32m" . $question . $def . ": \e[0m"); if (! $answer) { return $default; @@ -134,11 +210,9 @@ function ask(string $question, string $default = ''): string function confirm(string $question, bool $default = false): bool { - $consoleColor = new ConsoleColor(); - $answer = ask($question, ($default ? 'Y/n' : 'y/N')); - if (! $answer) { + if (strtolower($answer) === 'y/n') { return $default; } @@ -152,18 +226,7 @@ function writeln(string $line): void function run(string $command): string { - return trim(shell_exec($command)); -} - -function str_after(string $subject, string $search): string -{ - $pos = strrpos($subject, $search); - - if ($pos === false) { - return $subject; - } - - return substr($subject, $pos + strlen($search)); + return trim((string) shell_exec($command)); } function slugify(string $subject): string @@ -171,12 +234,17 @@ function slugify(string $subject): string return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $subject), '-')); } -function title_case(string $subject): string +function titleCase(string $subject): string { return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $subject))); } -function replace_in_file(string $file, array $replacements): void +function titleSnake(string $subject, string $replace = '_'): string +{ + return str_replace(['-', '_'], $replace, $subject); +} + +function replaceInFile(string $file, array $replacements): void { $contents = file_get_contents($file); @@ -190,7 +258,7 @@ function replace_in_file(string $file, array $replacements): void ); } -function remove_prefix(string $prefix, string $content): string +function removePrefix(string $prefix, string $content): string { if (str_starts_with($content, $prefix)) { return substr($content, strlen($prefix)); @@ -199,53 +267,68 @@ function remove_prefix(string $prefix, string $content): string return $content; } -function remove_composer_deps(array $names) +function removeComposerDeps(array $names, string $location): void { $data = json_decode(file_get_contents(__DIR__ . '/composer.json'), true); - foreach ($data['require-dev'] as $name => $version) { + foreach ($data[$location] as $name => $version) { if (in_array($name, $names, true)) { - unset($data['require-dev'][$name]); + unset($data[$location][$name]); } } file_put_contents(__DIR__ . '/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } -function remove_composer_script(array $scriptNames) +function removeNpmDeps(array $names, string $location): void { - $data = json_decode(file_get_contents(__DIR__ . '/composer.json'), true); + $data = json_decode(file_get_contents(__DIR__ . '/package.json'), true); - foreach ($data['scripts'] as $name => $script) { - if (is_array($script)) { - foreach ($script as $k => $s) { - if (in_array($s, $scriptNames)) { - unset($data['scripts'][$name][$k]); - - break; - } - } - } elseif (in_array($name, $scriptNames)) { - unset($data['scripts'][$name]); - - break; + foreach ($data[$location] as $name => $version) { + if (in_array($name, $names, true)) { + unset($data[$location][$name]); } } - file_put_contents(__DIR__ . '/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + file_put_contents(__DIR__ . '/package.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | + JSON_UNESCAPED_UNICODE)); } -function remove_readme_paragraphs(string $file): void +function removeTag(string $file, string $tag): void { $contents = file_get_contents($file); file_put_contents( $file, - preg_replace('/.*/s', '', $contents) ?: $contents + preg_replace('/.*/s', '', $contents) ?: $contents ); } -function safeUnlink(string $filename) +function setupPackageJsonForTheme(): void +{ + removeNpmDeps([ + 'purge', + 'dev', + 'dev:scripts', + 'build', + 'build:scripts', + ], 'scripts'); + + removeNpmDeps([ + '@awcodes/filament-plugin-purge', + 'esbuild', + 'npm-run-all', + 'prettier', + 'prettier-plugin-tailwindcss', + ], 'devDependencies'); + + replaceInFile(__DIR__ . '/package.json', [ + 'dev:styles' => 'dev', + 'build:styles' => 'build', + ]); +} + +function safeUnlink(string $filename): void { if (file_exists($filename) && is_file($filename)) { unlink($filename); @@ -259,118 +342,25 @@ function determineSeparator(string $path): string function replaceForWindows(): array { - return preg_split('/\\r\\n|\\r|\\n/', run('dir /S /B * | findstr /v /i .git\ | findstr /v /i vendor | findstr /v /i ' . basename(__FILE__) . ' | findstr /r /i /M /F:/ ":author :vendor :package VendorName skeleton vendor_name vendor_slug author@domain.com"')); + return preg_split('/\\r\\n|\\r|\\n/', run('dir /S /B * | findstr /v /i .git\ | findstr /v /i vendor | findstr /v /i ' . basename(__FILE__) . ' | findstr /r /i /M /F:/ ":author :vendor :package VendorName skeleton migration_table_name vendor_name vendor_slug author@domain.com"')); } function replaceForAllOtherOSes(): array { - return explode(PHP_EOL, run('grep -E -r -l -i ":author|:vendor|:package|VendorName|skeleton|vendor_name|vendor_slug|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v ' . basename(__FILE__))); + return explode(PHP_EOL, run('grep -E -r -l -i ":author|:vendor|:package|VendorName|skeleton|migration_table_name|vendor_name|vendor_slug|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v ' . basename(__FILE__))); } -class ConsoleColor +function removeDirectory($dir): void { - const FOREGROUND = 38; - - const BACKGROUND = 48; - - const COLOR256_REGEXP = '~^(bg_)?color_([0-9]{1,3})$~'; - - const RESET_STYLE = 0; - - /** @var bool */ - private $isSupported; - - /** @var bool */ - private $forceStyle = false; - - /** @var array */ - private $styles = [ - 'none' => null, - 'bold' => '1', - 'dark' => '2', - 'italic' => '3', - 'underline' => '4', - 'blink' => '5', - 'reverse' => '7', - 'concealed' => '8', - - 'default' => '39', - 'black' => '30', - 'red' => '31', - 'green' => '32', - 'yellow' => '33', - 'blue' => '34', - 'magenta' => '35', - 'cyan' => '36', - 'light_gray' => '37', - - 'dark_gray' => '90', - 'light_red' => '91', - 'light_green' => '92', - 'light_yellow' => '93', - 'light_blue' => '94', - 'light_magenta' => '95', - 'light_cyan' => '96', - 'white' => '97', - - 'bg_default' => '49', - 'bg_black' => '40', - 'bg_red' => '41', - 'bg_green' => '42', - 'bg_yellow' => '43', - 'bg_blue' => '44', - 'bg_magenta' => '45', - 'bg_cyan' => '46', - 'bg_light_gray' => '47', - - 'bg_dark_gray' => '100', - 'bg_light_red' => '101', - 'bg_light_green' => '102', - 'bg_light_yellow' => '103', - 'bg_light_blue' => '104', - 'bg_light_magenta' => '105', - 'bg_light_cyan' => '106', - 'bg_white' => '107', - ]; - - /** @var array */ - private $themes = []; - - public function __construct() - { - $this->isSupported = $this->isSupported(); - } - - /** - * @param string|array $style - * @param string $text - * @return string - * - * @throws InvalidStyleException - * @throws \InvalidArgumentException - */ - public function apply($style, $text) - { - if (! $this->isStyleForced() && ! $this->isSupported()) { - return $text; - } - - if (is_string($style)) { - $style = [$style]; - } - if (! is_array($style)) { - throw new \InvalidArgumentException('Style must be string or array.'); - } - - $sequences = []; - - foreach ($style as $s) { - if (isset($this->themes[$s])) { - $sequences = array_merge($sequences, $this->themeSequence($s)); - } elseif ($this->isValidStyle($s)) { - $sequences[] = $this->styleSequence($s); - } else { - throw new InvalidStyleException($s); + if (is_dir($dir)) { + $objects = scandir($dir); + foreach ($objects as $object) { + if ($object != '.' && $object != '..') { + if (filetype($dir . '/' . $object) == 'dir') { + removeDirectory($dir . '/' . $object); + } else { + unlink($dir . '/' . $object); + } } } diff --git a/database/migrations/create_skeleton_table.php.stub b/database/migrations/create_skeleton_table.php.stub index 0e4bef7..2efdce9 100644 --- a/database/migrations/create_skeleton_table.php.stub +++ b/database/migrations/create_skeleton_table.php.stub @@ -8,7 +8,7 @@ return new class extends Migration { public function up() { - Schema::create('skeleton_table', function (Blueprint $table) { + Schema::create('migration_table_name_table', function (Blueprint $table) { $table->id(); // add fields diff --git a/package.json b/package.json index 028b7ff..afec3c0 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,26 @@ { "private": true, + "type": "module", "scripts": { - "dev:styles": "npx tailwindcss -i resources/css/plugin.css -o resources/dist/skeleton.css --postcss --watch", - "dev:scripts": "esbuild resources/js/plugin.js --bundle --sourcemap=inline --outfile=resources/dist/skeleton.js --watch", - "build:styles": "npx tailwindcss -i resources/css/plugin.css -o resources/dist/skeleton.css --postcss --minify && npm run purge", - "build:scripts": "esbuild resources/js/plugin.js --bundle --minify --outfile=resources/dist/skeleton.js", - "purge": "filament-purge -i resources/dist/skeleton.css -o resources/dist/skeleton.css", + "dev:styles": "npx tailwindcss -i resources/css/index.css -o resources/dist/skeleton.css --postcss --watch", + "dev:scripts": "node bin/build.js --dev", + "build:styles": "npx tailwindcss -i resources/css/index.css -o resources/dist/skeleton.css --postcss --minify && npm run purge", + "build:scripts": "node bin/build.js", + "purge": "filament-purge -i resources/dist/skeleton.css -o resources/dist/skeleton.css -v 3.x", "dev": "npm-run-all --parallel dev:*", "build": "npm-run-all build:*" }, "devDependencies": { - "@awcodes/filament-plugin-purge": "^1.0.2", - "autoprefixer": "^10.4.7", - "esbuild": "^0.8.57", + "@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.14", - "tailwindcss": "^3.1.6" + "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/phpunit.xml.dist b/phpunit.xml.dist index 3bbe22a..e953c0e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,14 +1,10 @@ @@ -24,9 +21,6 @@ - - ./src - @@ -36,4 +30,9 @@ + + + ./src + + diff --git a/pint.json b/pint.json index 304d8f3..c6ddb49 100644 --- a/pint.json +++ b/pint.json @@ -6,6 +6,9 @@ "spacing": "one" }, "method_argument_space": true, - "single_trait_insert_per_statement": 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 0000000..2855394 --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,8 @@ +module.exports = { + plugins: { + "postcss-import": {}, + "tailwindcss/nesting": {}, + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 12a703d..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/resources/css/index.css b/resources/css/index.css new file mode 100644 index 0000000..b3949d5 --- /dev/null +++ b/resources/css/index.css @@ -0,0 +1 @@ +@import '../../vendor/filament/filament/resources/css/theme.css'; diff --git a/resources/css/plugin.css b/resources/css/plugin.css deleted file mode 100644 index 65dd5f6..0000000 --- a/resources/css/plugin.css +++ /dev/null @@ -1 +0,0 @@ -@tailwind utilities; diff --git a/resources/js/plugin.js b/resources/js/index.js similarity index 100% rename from resources/js/plugin.js rename to resources/js/index.js diff --git a/resources/lang/en/skeleton.php b/resources/lang/en/skeleton.php new file mode 100644 index 0000000..d1b24d9 --- /dev/null +++ b/resources/lang/en/skeleton.php @@ -0,0 +1,6 @@ +comment('All done'); + + return self::SUCCESS; + } +} diff --git a/src/Facades/Skeleton.php b/src/Facades/Skeleton.php new file mode 100644 index 0000000..1fa9076 --- /dev/null +++ b/src/Facades/Skeleton.php @@ -0,0 +1,16 @@ +getId()); + + return $plugin; + } +} diff --git a/src/SkeletonServiceProvider.php b/src/SkeletonServiceProvider.php index c682d67..266d50d 100644 --- a/src/SkeletonServiceProvider.php +++ b/src/SkeletonServiceProvider.php @@ -2,39 +2,153 @@ namespace VendorName\Skeleton; -use Filament\PluginServiceProvider; +use Filament\Support\Assets\AlpineComponent; +use Filament\Support\Assets\Asset; +use Filament\Support\Assets\Css; +use Filament\Support\Assets\Js; +use Filament\Support\Facades\FilamentAsset; +use Filament\Support\Facades\FilamentIcon; +use Illuminate\Filesystem\Filesystem; +use Livewire\Features\SupportTesting\Testable; +use Spatie\LaravelPackageTools\Commands\InstallCommand; use Spatie\LaravelPackageTools\Package; +use Spatie\LaravelPackageTools\PackageServiceProvider; +use VendorName\Skeleton\Commands\SkeletonCommand; +use VendorName\Skeleton\Testing\TestsSkeleton; -class SkeletonServiceProvider extends PluginServiceProvider +class SkeletonServiceProvider extends PackageServiceProvider { public static string $name = 'skeleton'; - protected array $resources = [ - // CustomResource::class, - ]; + public static string $viewNamespace = 'skeleton'; - protected array $pages = [ - // CustomPage::class, - ]; + public function configurePackage(Package $package): void + { + /* + * This class is a Package Service Provider + * + * More info: https://github.com/spatie/laravel-package-tools + */ + $package->name(static::$name) + ->hasCommands($this->getCommands()) + ->hasInstallCommand(function (InstallCommand $command) { + $command + ->publishConfigFile() + ->publishMigrations() + ->askToRunMigrations() + ->askToStarRepoOnGitHub(':vendor_slug/:package_slug'); + }); - protected array $widgets = [ - // CustomWidget::class, - ]; + $configFileName = $package->shortName(); - protected array $styles = [ - 'plugin-skeleton' => __DIR__ . '/../resources/dist/skeleton.css', - ]; + if (file_exists($package->basePath("/../config/{$configFileName}.php"))) { + $package->hasConfigFile(); + } - protected array $scripts = [ - 'plugin-skeleton' => __DIR__ . '/../resources/dist/skeleton.js', - ]; + if (file_exists($package->basePath('/../database/migrations'))) { + $package->hasMigrations($this->getMigrations()); + } - // protected array $beforeCoreScripts = [ - // 'plugin-skeleton' => __DIR__ . '/../resources/dist/skeleton.js', - // ]; + if (file_exists($package->basePath('/../resources/lang'))) { + $package->hasTranslations(); + } - public function configurePackage(Package $package): void + if (file_exists($package->basePath('/../resources/views'))) { + $package->hasViews(static::$viewNamespace); + } + } + + public function packageRegistered(): void + { + } + + 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/skeleton/{$file->getFilename()}"), + ], 'skeleton-stubs'); + } + } + + // Testing + Testable::mixin(new TestsSkeleton()); + } + + protected function getAssetPackageName(): ?string + { + return ':vendor_slug/:package_slug'; + } + + /** + * @return array + */ + protected function getAssets(): array + { + return [ + // AlpineComponent::make('skeleton', __DIR__ . '/../resources/dist/components/skeleton.js'), + Css::make('skeleton-styles', __DIR__ . '/../resources/dist/skeleton.css'), + Js::make('skeleton-scripts', __DIR__ . '/../resources/dist/skeleton.js'), + ]; + } + + /** + * @return array + */ + protected function getCommands(): array + { + return [ + SkeletonCommand::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 { - $package->name(static::$name); + return [ + 'create_skeleton_table', + ]; } } diff --git a/src/SkeletonTheme.php b/src/SkeletonTheme.php new file mode 100644 index 0000000..6c647e0 --- /dev/null +++ b/src/SkeletonTheme.php @@ -0,0 +1,39 @@ +font('DM Sans') + ->primaryColor(Color::Amber) + ->secondaryColor(Color::Gray) + ->warningColor(Color::Amber) + ->dangerColor(Color::Rose) + ->successColor(Color::Green) + ->grayColor(Color::Gray) + ->theme('skeleton'); + } + + public function boot(Panel $panel): void + { + // + } +} diff --git a/src/Testing/TestsSkeleton.php b/src/Testing/TestsSkeleton.php new file mode 100644 index 0000000..0e33b51 --- /dev/null +++ b/src/Testing/TestsSkeleton.php @@ -0,0 +1,13 @@ +expect(['dd', 'dump', 'ray']) + ->each->not->toBeUsed(); diff --git a/tests/TestCase.php b/tests/TestCase.php index ff22f2a..8ef205d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,10 +2,22 @@ namespace VendorName\Skeleton\Tests; +use BladeUI\Heroicons\BladeHeroiconsServiceProvider; +use BladeUI\Icons\BladeIconsServiceProvider; +use Filament\Actions\ActionsServiceProvider; use Filament\FilamentServiceProvider; +use Filament\Forms\FormsServiceProvider; +use Filament\Infolists\InfolistsServiceProvider; +use Filament\Notifications\NotificationsServiceProvider; +use Filament\SpatieLaravelSettingsPluginServiceProvider; +use Filament\SpatieLaravelTranslatablePluginServiceProvider; +use Filament\Support\SupportServiceProvider; +use Filament\Tables\TablesServiceProvider; +use Filament\Widgets\WidgetsServiceProvider; use Illuminate\Database\Eloquent\Factories\Factory; use Livewire\LivewireServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; +use RyanChandler\BladeCaptureDirective\BladeCaptureDirectiveServiceProvider; use VendorName\Skeleton\SkeletonServiceProvider; class TestCase extends Orchestra @@ -22,8 +34,20 @@ protected function setUp(): void protected function getPackageProviders($app) { return [ - LivewireServiceProvider::class, + ActionsServiceProvider::class, + BladeCaptureDirectiveServiceProvider::class, + BladeHeroiconsServiceProvider::class, + BladeIconsServiceProvider::class, FilamentServiceProvider::class, + FormsServiceProvider::class, + InfolistsServiceProvider::class, + LivewireServiceProvider::class, + NotificationsServiceProvider::class, + SpatieLaravelSettingsPluginServiceProvider::class, + SpatieLaravelTranslatablePluginServiceProvider::class, + SupportServiceProvider::class, + TablesServiceProvider::class, + WidgetsServiceProvider::class, SkeletonServiceProvider::class, ]; }