Merge branch '3.x'

This commit is contained in:
Adam Weston
2023-11-28 10:31:46 -05:00
38 changed files with 757 additions and 347 deletions

View File

@@ -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

21
.gitattributes vendored
View File

@@ -5,15 +5,24 @@
/.github export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpunit.xml.dist export-ignore
/art export-ignore
/docs export-ignore
/tests 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
/phpstan.neon.dist export-ignore
/phpstan-baseline.neon export-ignore

View File

@@ -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

66
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,5 @@
name: dependabot-auto-merge
name: "Dependabot Auto-Merge"
on: pull_request_target
permissions:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
run: vendor/bin/pest --ci

View File

@@ -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,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

7
.gitignore vendored
View File

@@ -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

5
.prettierrc Normal file
View File

@@ -0,0 +1,5 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "all"
}

View File

@@ -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)
<!--delete-->
@@ -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

50
bin/build.js Normal file
View File

@@ -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',
})

View File

@@ -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
}

View File

@@ -7,14 +7,16 @@ $authorName = ask('Author name', $gitName);
$gitEmail = run('git config user.email');
$authorEmail = ask('Author email', $gitEmail);
$usernameGuess = explode(':', run('git config remote.origin.url'))[1];
$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 @@ $folderName = basename($currentDirectory);
$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);
writeln('------');
writeln("Author : {$authorName} ({$authorUsername}, {$authorEmail})");
writeln("Vendor : {$vendorName} ({$vendorSlug})");
writeln("Package : {$packageSlug} <{$description}>");
writeln("Namespace : {$vendorNamespace}\\{$className}");
writeln("Class name : {$className}");
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('------');
$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 : \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("\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 @@ foreach ($files as $file) {
':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');
if (! $useDependabot) {
safeUnlink(__DIR__ . '/.github/dependabot.yml');
safeUnlink(__DIR__ . '/.github/workflows/dependabot-auto-merge.yml');
}
remove_composer_deps([
'laravel/pint',
]);
remove_composer_script(['pint']);
if (! $useLaravelRay) {
removeComposerDeps(['spatie/laravel-ray'], 'require-dev');
}
if (! $usePhpStan) {
@@ -98,32 +165,41 @@ if (! $usePhpStan) {
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('/<!--delete-->.*<!--\/delete-->/s', '', $contents) ?: $contents
preg_replace('/<!--' . $tag . '-->.*<!--\/' . $tag . '-->/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);
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != '.' && $object != '..') {
if (filetype($dir . '/' . $object) == 'dir') {
removeDirectory($dir . '/' . $object);
} else {
throw new InvalidStyleException($s);
unlink($dir . '/' . $object);
}
}
}

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -1,14 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
executionOrder="random"
@@ -16,7 +12,8 @@
failOnRisky="true"
failOnEmptyTestSuite="true"
beStrictAboutOutputDuringTests="true"
verbose="true"
cacheDirectory=".phpunit.cache"
backupStaticProperties="false"
>
<testsuites>
<testsuite name="VendorName Test Suite">
@@ -24,9 +21,6 @@
</testsuite>
</testsuites>
<coverage>
<include>
<directory suffix=".php">./src</directory>
</include>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
@@ -36,4 +30,9 @@
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>

View File

@@ -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"
}
}
}

8
postcss.config.cjs Normal file
View File

@@ -0,0 +1,8 @@
module.exports = {
plugins: {
"postcss-import": {},
"tailwindcss/nesting": {},
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

1
resources/css/index.css Normal file
View File

@@ -0,0 +1 @@
@import '../../vendor/filament/filament/resources/css/theme.css';

View File

@@ -1 +0,0 @@
@tailwind utilities;

View File

@@ -0,0 +1,6 @@
<?php
// translations for VendorName/Skeleton
return [
//
];

View File

@@ -0,0 +1,19 @@
<?php
namespace VendorName\Skeleton\Commands;
use Illuminate\Console\Command;
class SkeletonCommand extends Command
{
public $signature = 'skeleton';
public $description = 'My command';
public function handle(): int
{
$this->comment('All done');
return self::SUCCESS;
}
}

16
src/Facades/Skeleton.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
namespace VendorName\Skeleton\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @see \VendorName\Skeleton\Skeleton
*/
class Skeleton extends Facade
{
protected static function getFacadeAccessor()
{
return \VendorName\Skeleton\Skeleton::class;
}
}

7
src/Skeleton.php Normal file
View File

@@ -0,0 +1,7 @@
<?php
namespace VendorName\Skeleton;
class Skeleton
{
}

37
src/SkeletonPlugin.php Normal file
View File

@@ -0,0 +1,37 @@
<?php
namespace VendorName\Skeleton;
use Filament\Contracts\Plugin;
use Filament\Panel;
class SkeletonPlugin implements Plugin
{
public function getId(): string
{
return 'skeleton';
}
public function register(Panel $panel): void
{
//
}
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;
}
}

View File

@@ -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,
];
protected array $pages = [
// CustomPage::class,
];
protected array $widgets = [
// CustomWidget::class,
];
protected array $styles = [
'plugin-skeleton' => __DIR__ . '/../resources/dist/skeleton.css',
];
protected array $scripts = [
'plugin-skeleton' => __DIR__ . '/../resources/dist/skeleton.js',
];
// protected array $beforeCoreScripts = [
// 'plugin-skeleton' => __DIR__ . '/../resources/dist/skeleton.js',
// ];
public static string $viewNamespace = 'skeleton';
public function configurePackage(Package $package): void
{
$package->name(static::$name);
/*
* 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');
});
$configFileName = $package->shortName();
if (file_exists($package->basePath("/../config/{$configFileName}.php"))) {
$package->hasConfigFile();
}
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
{
}
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<Asset>
*/
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<class-string>
*/
protected function getCommands(): array
{
return [
SkeletonCommand::class,
];
}
/**
* @return array<string>
*/
protected function getIcons(): array
{
return [];
}
/**
* @return array<string>
*/
protected function getRoutes(): array
{
return [];
}
/**
* @return array<string, mixed>
*/
protected function getScriptData(): array
{
return [];
}
/**
* @return array<string>
*/
protected function getMigrations(): array
{
return [
'create_skeleton_table',
];
}
}

39
src/SkeletonTheme.php Normal file
View File

@@ -0,0 +1,39 @@
<?php
namespace VendorName\Skeleton;
use Filament\Contracts\Plugin;
use Filament\Panel;
use Filament\Support\Assets\Theme;
use Filament\Support\Color;
use Filament\Support\Facades\FilamentAsset;
class Skeleton implements Plugin
{
public function getId(): string
{
return 'skeleton';
}
public function register(Panel $panel): void
{
FilamentAsset::register([
Theme::make('skeleton', __DIR__ . '/../resources/dist/skeleton.css'),
]);
$panel
->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
{
//
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace VendorName\Skeleton\Testing;
use Livewire\Features\SupportTesting\Testable;
/**
* @mixin Testable
*/
class TestsSkeleton
{
//
}

View File

@@ -1,21 +1,10 @@
const colors = require("tailwindcss/colors");
const preset = require('./vendor/filament/filament/tailwind.config.preset')
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./resources/views/**/*.blade.php", "./src/**/*.php"],
darkMode: "class",
theme: {
extend: {
colors: {
danger: colors.rose,
primary: colors.yellow,
success: colors.green,
warning: colors.amber,
},
},
},
corePlugins: {
preflight: false,
},
plugins: [],
};
presets: [preset],
content: [
'./app/Filament/**/*.php',
'./resources/views/filament/**/*.blade.php',
'./vendor/filament/**/*.blade.php',
],
}

5
tests/ArchTest.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
it('will not use debugging functions')
->expect(['dd', 'dump', 'ray'])
->each->not->toBeUsed();

View File

@@ -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 @@ class TestCase extends Orchestra
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,
];
}