├── .editorconfig ├── .gitattributes.dist ├── .github ├── ISSUE_TEMPLATE │ ├── bug.yml │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── check-links.yml │ ├── dependabot-auto-merge.yml │ ├── php-cs-fixer.yml │ ├── phpstan.yml │ ├── tests.yml │ └── update-changelog.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── html-min.php ├── docs └── images │ └── abordage-laravel-html-min-cover.png ├── phpstan-baseline.neon ├── phpstan.neon.dist ├── phpunit.xml.dist ├── src ├── Facades │ └── HtmlMin.php ├── HtmlMin.php ├── HtmlMinServiceProvider.php └── Middleware │ └── HtmlMinify.php └── tests └── HtmlMinifyTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.gitattributes.dist: -------------------------------------------------------------------------------- 1 | # Ignore all test and documentation with "export-ignore". 2 | .editorconfig export-ignore 3 | .gitattributes export-ignore 4 | .github export-ignore 5 | .gitignore export-ignore 6 | .php-cs-fixer.cache export-ignore 7 | .php-cs-fixer.php export-ignore 8 | /art export-ignore 9 | /docs export-ignore 10 | /tests export-ignore 11 | UPGRADING.md export-ignore 12 | testbench.yaml export-ignore 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a general package issue. Filling in the issue template is mandatory, issues without it will be closed. 3 | title: "[Bug]: " 4 | labels: [ bug ] 5 | body: 6 | - type: checkboxes 7 | id: terms 8 | attributes: 9 | label: Is the bug applicable and reproducible to the latest version of the package and hasn't it been reported before? 10 | options: 11 | - label: Yes, it's still reproducible 12 | required: true 13 | - type: input 14 | attributes: 15 | label: What version of Package are you using? 16 | description: 'For example: 1.2.0' 17 | validations: 18 | required: true 19 | - type: input 20 | attributes: 21 | label: What version of Laravel are you using? 22 | description: 'For example: 9.52.0' 23 | validations: 24 | required: true 25 | - type: input 26 | attributes: 27 | label: What version of PHP are you using? 28 | description: 'For example: 8.1.0' 29 | validations: 30 | required: true 31 | - type: textarea 32 | attributes: 33 | label: Describe your issue 34 | description: Describe the problem you're seeing, Please be short, but concise. 35 | validations: 36 | required: true 37 | - type: textarea 38 | attributes: 39 | label: How can the issue be reproduced? 40 | description: Please provide easy-to-reproduce steps (repository, simple code example, failing unit test). Please don't paste your entire code, but create a reproducible scenario that can be tested using a simple User model in a blank Laravel installation. 41 | validations: 42 | required: true 43 | - type: textarea 44 | attributes: 45 | label: What should be the expected behaviour? 46 | description: Please describe what the expected outcome should be. Any suggestions to what is wrong? 47 | validations: 48 | required: true 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://github.com/abordage/laravel-html-min/discussions/new?category=q-a 5 | about: Ask the community for help 6 | - name: Request a feature 7 | url: https://github.com/abordage/laravel-html-min/discussions/new?category=ideas 8 | about: Share ideas for new features -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. 2 | 3 | 4 | > 💡 Please take note of our [contributing guidelines](https://github.com/abordage/.github/blob/master/CONTRIBUTING.md) 5 | 6 | 1. Why should it be added? What are the benefits of this change? 7 | 2. Does it contain multiple, unrelated changes? Please separate the PRs out. 8 | 3. Does it include tests, if possible? 9 | 4. Any drawbacks? Possible breaking changes? 10 | 11 | **Mark the following tasks as done:** 12 | - [ ] Checked the codebase to ensure that your feature doesn't already exist. 13 | - [ ] Take note of the contributing guidelines. 14 | - [ ] Checked the pull requests to ensure that another person hasn't already submitted a fix. 15 | - [ ] Added tests to ensure against regression. 16 | - [ ] Updated the changelog 17 | 18 | ### Thanks for contributing! 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | labels: 12 | - "dependencies" -------------------------------------------------------------------------------- /.github/workflows/check-links.yml: -------------------------------------------------------------------------------- 1 | name: Check Links 2 | 3 | on: 4 | repository_dispatch: 5 | workflow_dispatch: 6 | schedule: 7 | - cron: "30 3 * * 1" 8 | 9 | jobs: 10 | linkChecker: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Link Checker 16 | id: lychee 17 | uses: lycheeverse/lychee-action@v1.10.0 18 | with: 19 | args: --verbose --no-progress --exclude-mail './**/*.md' './**/*.html' 20 | env: 21 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 22 | 23 | - name: Create Issue From File 24 | if: steps.lychee.outputs.exit_code != 0 25 | uses: peter-evans/create-issue-from-file@v5 26 | with: 27 | title: Link Checker Report 28 | content-filepath: ./lychee/out.md 29 | labels: report, automated issue 30 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | 3 | on: pull_request_target 4 | 5 | permissions: 6 | pull-requests: write 7 | contents: write 8 | 9 | jobs: 10 | dependabot: 11 | runs-on: ubuntu-latest 12 | if: ${{ github.actor == 'dependabot[bot]' }} 13 | steps: 14 | 15 | - name: Dependabot metadata 16 | id: metadata 17 | uses: dependabot/fetch-metadata@v2.2.0 18 | with: 19 | github-token: "${{ secrets.GITHUB_TOKEN }}" 20 | 21 | - name: Auto-merge Dependabot PRs for semver-minor updates 22 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} 23 | run: gh pr merge --auto --merge "$PR_URL" 24 | env: 25 | PR_URL: ${{github.event.pull_request.html_url}} 26 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 27 | 28 | - name: Auto-merge Dependabot PRs for semver-patch updates 29 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 30 | run: gh pr merge --auto --merge "$PR_URL" 31 | env: 32 | PR_URL: ${{github.event.pull_request.html_url}} 33 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 34 | -------------------------------------------------------------------------------- /.github/workflows/php-cs-fixer.yml: -------------------------------------------------------------------------------- 1 | name: PHP CS Fixer 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | php-cs-fixer: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v4 12 | with: 13 | ref: ${{ github.head_ref }} 14 | 15 | - name: Run PHP CS Fixer 16 | uses: docker://oskarstark/php-cs-fixer-ga 17 | with: 18 | args: --config=.php-cs-fixer.dist.php 19 | 20 | - name: Commit changes 21 | uses: stefanzweifel/git-auto-commit-action@v5 22 | with: 23 | commit_message: fix styling 24 | -------------------------------------------------------------------------------- /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: PHPStan 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.php' 7 | - 'phpstan.neon.dist' 8 | pull_request: 9 | paths: 10 | - '**.php' 11 | - 'phpstan.neon.dist' 12 | 13 | jobs: 14 | phpstan: 15 | name: phpstan 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Setup PHP 21 | uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: '8.0' 24 | coverage: none 25 | 26 | - name: Install composer dependencies 27 | uses: ramsey/composer-install@v3 28 | 29 | - name: Run PHPStan 30 | run: ./vendor/bin/phpstan --error-format=github 31 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | stability: [ prefer-stable ] 13 | os: [ ubuntu-latest ] 14 | php: [7.4, 8.0, 8.1, 8.2, 8.3] 15 | laravel: [8.*, 9.*, 10.*, 11.*] 16 | include: 17 | - laravel: 8.* 18 | testbench: ^6.0 19 | - laravel: 9.* 20 | testbench: ^7.0 21 | - laravel: 10.* 22 | testbench: ^8.0 23 | - laravel: 11.* 24 | testbench: ^9.0 25 | exclude: 26 | - php: 7.4 27 | laravel: 9.* 28 | - php: 7.4 29 | laravel: 10.* 30 | - php: 7.4 31 | laravel: 11.* 32 | - php: 8.0 33 | laravel: 10.* 34 | - php: 8.0 35 | laravel: 11.* 36 | - php: 8.1 37 | laravel: 11.* 38 | - php: 8.2 39 | laravel: 8.* 40 | - php: 8.3 41 | laravel: 8.* 42 | - php: 8.3 43 | laravel: 9.* 44 | 45 | name: Laravel ${{ matrix.laravel }} on PHP ${{ matrix.php }} 46 | 47 | steps: 48 | - name: Checkout code 49 | uses: actions/checkout@v4 50 | 51 | - name: Setup PHP 52 | uses: shivammathur/setup-php@v2 53 | with: 54 | php-version: ${{ matrix.php }} 55 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 56 | tools: composer:v2 57 | coverage: none 58 | 59 | - name: Install dependencies 60 | run: | 61 | composer require "laravel/framework=${{ matrix.laravel }}" "orchestra/testbench=${{ matrix.testbench }}" --no-update 62 | composer update --prefer-dist --no-interaction --no-progress 63 | - name: Run tests 64 | run: vendor/bin/phpunit 65 | -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | name: "Update Changelog" 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | update: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v4 14 | with: 15 | ref: ${{ github.event.release.target_commitish }} 16 | 17 | - name: Update Changelog 18 | uses: stefanzweifel/changelog-updater-action@v1 19 | with: 20 | latest-version: ${{ github.event.release.tag_name }} 21 | release-notes: ${{ github.event.release.body }} 22 | 23 | - name: Commit updated CHANGELOG 24 | uses: stefanzweifel/git-auto-commit-action@v5 25 | with: 26 | branch: ${{ github.event.release.target_commitish }} 27 | commit_message: Update CHANGELOG 28 | file_pattern: CHANGELOG.md 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .php-cs-fixer.cache 2 | .php-cs-fixer.php 3 | .phpunit.result.cache 4 | .vscode 5 | /.idea 6 | /build 7 | /coverage 8 | /node_modules 9 | /vendor 10 | composer.lock 11 | npm-debug.log 12 | phpstan.neon 13 | phpunit.xml 14 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in([ 5 | __DIR__ . '/config', 6 | __DIR__ . '/src', 7 | __DIR__ . '/tests', 8 | ]) 9 | ->name('*.php') 10 | ->notName('*.blade.php') 11 | ->ignoreDotFiles(true) 12 | ->ignoreVCS(true); 13 | 14 | return (new PhpCsFixer\Config()) 15 | ->setRules([ 16 | '@PSR12' => true, 17 | 'array_syntax' => ['syntax' => 'short'], 18 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 19 | 'no_unused_imports' => true, 20 | 'not_operator_with_successor_space' => false, 21 | 'trailing_comma_in_multiline' => false, 22 | 'phpdoc_scalar' => true, 23 | 'unary_operator_spaces' => true, 24 | 'binary_operator_spaces' => true, 25 | 'blank_line_before_statement' => [ 26 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 27 | ], 28 | 'phpdoc_single_line_var_spacing' => true, 29 | 'phpdoc_var_without_name' => true, 30 | 'class_attributes_separation' => [ 31 | 'elements' => [ 32 | 'method' => 'one', 33 | ], 34 | ], 35 | 'method_argument_space' => [ 36 | 'on_multiline' => 'ensure_fully_multiline', 37 | 'keep_multiple_spaces_after_comma' => true, 38 | ], 39 | 'single_trait_insert_per_statement' => true, 40 | ]) 41 | ->setFinder($finder); 42 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-html-min` will be documented in this file. 4 | 5 | ## 0.2.2 - 2025-02-27 6 | 7 | ### What's Changed 8 | 9 | * Bump lycheeverse/lychee-action from 1.8.0 to 1.9.3 by @dependabot in https://github.com/abordage/laravel-html-min/pull/26 10 | * Bump dependabot/fetch-metadata from 1.6.0 to 2.0.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/27 11 | * Bump peter-evans/create-issue-from-file from 4 to 5 by @dependabot in https://github.com/abordage/laravel-html-min/pull/28 12 | * Bump ramsey/composer-install from 2 to 3 by @dependabot in https://github.com/abordage/laravel-html-min/pull/29 13 | * Bump dependabot/fetch-metadata from 2.0.0 to 2.1.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/30 14 | * Bump lycheeverse/lychee-action from 1.9.3 to 1.10.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/31 15 | * Bump dependabot/fetch-metadata from 2.1.0 to 2.2.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/32 16 | * Update composer.json to support laravel 12 by @phunsok in https://github.com/abordage/laravel-html-min/pull/37 17 | 18 | ### New Contributors 19 | 20 | * @phunsok made their first contribution in https://github.com/abordage/laravel-html-min/pull/37 21 | 22 | **Full Changelog**: https://github.com/abordage/laravel-html-min/compare/0.2.1...0.2.2 23 | 24 | ## 0.2.1 - 2024-03-23 25 | 26 | ### What's Changed 27 | 28 | * Bump lycheeverse/lychee-action from 1.5.4 to 1.6.1 by @dependabot in https://github.com/abordage/laravel-html-min/pull/14 29 | * Bump lycheeverse/lychee-action from 1.6.1 to 1.7.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/15 30 | * Bump dependabot/fetch-metadata from 1.3.6 to 1.4.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/16 31 | * Bump lycheeverse/lychee-action from 1.7.0 to 1.8.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/17 32 | * Bump dependabot/fetch-metadata from 1.4.0 to 1.5.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/18 33 | * Bump dependabot/fetch-metadata from 1.5.0 to 1.5.1 by @dependabot in https://github.com/abordage/laravel-html-min/pull/19 34 | * Bump dependabot/fetch-metadata from 1.5.1 to 1.6.0 by @dependabot in https://github.com/abordage/laravel-html-min/pull/21 35 | * Bump actions/checkout from 3 to 4 by @dependabot in https://github.com/abordage/laravel-html-min/pull/22 36 | * Bump stefanzweifel/git-auto-commit-action from 4 to 5 by @dependabot in https://github.com/abordage/laravel-html-min/pull/24 37 | * Laravel 11 by @abordage in https://github.com/abordage/laravel-html-min/pull/25 38 | 39 | **Full Changelog**: https://github.com/abordage/laravel-html-min/compare/0.2.0...0.2.1 40 | 41 | ## 0.2.0 - 2023-02-25 42 | 43 | ### What's Changed 44 | 45 | - Laravel 10 support by @abordage in https://github.com/abordage/laravel-html-min/pull/13 46 | - Update lychee-action config by @abordage in https://github.com/abordage/laravel-html-min/pull/4 47 | - Bump dependabot/fetch-metadata from 1.3.1 to 1.3.3 by @dependabot in https://github.com/abordage/laravel-html-min/pull/1 48 | - Bump lycheeverse/lychee-action from 1.5.0 to 1.5.1 by @dependabot in https://github.com/abordage/laravel-html-min/pull/2 49 | - Bump dependabot/fetch-metadata from 1.3.3 to 1.3.4 by @dependabot in https://github.com/abordage/laravel-html-min/pull/6 50 | - Bump dependabot/fetch-metadata from 1.3.4 to 1.3.5 by @dependabot in https://github.com/abordage/laravel-html-min/pull/8 51 | - Bump lycheeverse/lychee-action from 1.5.1 to 1.5.4 by @dependabot in https://github.com/abordage/laravel-html-min/pull/10 52 | - Bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 by @dependabot in https://github.com/abordage/laravel-html-min/pull/11 53 | 54 | ### New Contributors 55 | 56 | - @dependabot made their first contribution in https://github.com/abordage/laravel-html-min/pull/1 57 | 58 | **Full Changelog**: https://github.com/abordage/laravel-html-min/compare/0.1.5...0.2.0 59 | 60 | ## 0.1.5 - 2022-06-28 61 | 62 | **Full Changelog**: https://github.com/abordage/laravel-html-min/compare/0.1.4...0.1.5 63 | 64 | ## 0.1.4 - 2022-06-28 65 | 66 | **Full Changelog**: https://github.com/abordage/laravel-html-min/compare/0.1.3...0.1.4 67 | 68 | ## 0.1.3 - 2022-06-28 69 | 70 | **Full Changelog**: https://github.com/abordage/laravel-html-min/compare/0.1.1...0.1.3 71 | 72 | ## 0.1.2 - 2022-06-28 73 | 74 | **Full Changelog**: https://github.com/abordage/laravel-html-min/compare/0.1.1...0.1.2 75 | 76 | ## 0.1.1 - 2022-06-28 77 | 78 | **Full Changelog**: https://github.com/abordage/laravel-html-min/compare/0.1.0...0.1.1 79 | 80 | ## 0.1.0 - 2022-06-13 81 | 82 | **Full Changelog**: https://github.com/abordage/laravel-html-min/commits/0.1.0 83 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) abordage 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # HtmlMin: Laravel package for HTML minification 4 | 5 | Very simple (and very fast) html compression. 6 | 7 | [//]: # (

) 8 | 9 | [//]: # ( Laravel HtmlMin) 10 | 11 | [//]: # (

) 12 | 13 |

14 | 15 | 16 | Packagist Version 17 | 18 | 19 | 20 | Scrutinizer Quality Score 22 | 23 | 24 | 25 | GitHub Tests Status 26 | 27 | 28 | 29 | 30 | GitHub Code Style Status 31 | 32 | 33 | 34 | 35 | 36 | PHP Version Support 37 | 38 | 39 | 40 | License 41 | 42 | 43 |

44 | 45 | 46 | ## Features: 47 | - Removing extra whitespaces 48 | - Removing html comments (works correctly with `livewire/livewire` comments) 49 | - Skip `textarea`, `pre` and `script` elements 50 | - Very fast. See benchmark 51 | 52 | ## Requirements 53 | - PHP 7.4 - 8.3 54 | - Laravel 8.x - 11.x 55 | 56 | ## Installation 57 | 58 | You can install the package via composer: 59 | 60 | ```bash 61 | composer require abordage/laravel-html-min 62 | ``` 63 | 64 | You can publish the config file with: 65 | 66 | ```bash 67 | php artisan vendor:publish --tag="html-min-config" 68 | ``` 69 | ## Usage 70 | To enable compression just register middleware: 71 | 72 | ```php 73 | // app/Http/Kernel.php 74 | 75 | protected $middleware = [ 76 | 'web' => [ 77 | // other middleware 78 | 79 | \Abordage\LaravelHtmlMin\Middleware\HtmlMinify::class, 80 | ], 81 | 82 | // ... 83 | ]; 84 | ``` 85 | 86 | It's all. Optionally you can change the settings in `config/html-min.php` 87 | 88 | ## Configuration 89 | 90 | ```php 91 | return [ 92 | /* 93 | |-------------------------------------------------------------------------- 94 | | Enable Html Min 95 | |-------------------------------------------------------------------------- 96 | */ 97 | 'enable' => env('HTML_MINIFY', true), 98 | 99 | /* 100 | |-------------------------------------------------------------------------- 101 | | Find DOCTYPE in document 102 | |-------------------------------------------------------------------------- 103 | */ 104 | 'find_doctype_in_document' => true, 105 | 106 | /* 107 | |-------------------------------------------------------------------------- 108 | | Remove whitespace between tags 109 | |-------------------------------------------------------------------------- 110 | */ 111 | 'remove_whitespace_between_tags' => true, 112 | 113 | /* 114 | |-------------------------------------------------------------------------- 115 | | Remove blank lines in script elements 116 | |-------------------------------------------------------------------------- 117 | */ 118 | 'remove_blank_lines_in_script_elements' => false, 119 | ]; 120 | ``` 121 | 122 | ## Benchmark 123 | 124 | See [abordage/html-min-benchmark](https://github.com/abordage/html-min-benchmark) 125 | 126 | ## Testing 127 | 128 | ```bash 129 | composer test:all 130 | ``` 131 | 132 | or 133 | 134 | ```bash 135 | composer test:phpunit 136 | composer test:phpstan 137 | composer test:phpcsf 138 | ``` 139 | 140 | or see https://github.com/abordage/laravel-html-min/actions/workflows/tests.yml 141 | 142 | ## Feedback 143 | 144 | If you have any feedback, comments or suggestions, please feel free to open an issue within this repository. 145 | 146 | ## Contributing 147 | 148 | Please see [CONTRIBUTING](https://github.com/abordage/.github/blob/master/CONTRIBUTING.md) for details. 149 | 150 | ## Credits 151 | 152 | - [Pavel Bychko](https://github.com/abordage) 153 | - [All Contributors](https://github.com/abordage/laravel-html-min/graphs/contributors) 154 | 155 | ## License 156 | 157 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 158 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "abordage/laravel-html-min", 3 | "description": "HtmlMin is very simple (and very fast) html compression package for Laravel", 4 | "license": "MIT", 5 | "keywords": [ 6 | "html compress", 7 | "html minification", 8 | "html minifier", 9 | "html minify", 10 | "html min" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Pavel Bychko", 15 | "email": "box@abordage.dev", 16 | "role": "Developer" 17 | } 18 | ], 19 | "homepage": "https://github.com/abordage/laravel-html-min", 20 | "require": { 21 | "php": ">=7.4", 22 | "abordage/html-min": "^0.2.1", 23 | "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" 24 | }, 25 | "require-dev": { 26 | "ext-json": "*", 27 | "friendsofphp/php-cs-fixer": "^3.0", 28 | "nunomaduro/collision": "^5.0 || ^6.0 || ^7.0 || ^8.0", 29 | "nunomaduro/larastan": "^1.0 || ^2.0", 30 | "orchestra/testbench": "^6.0 || ^7.0 || ^8.0 || ^9.0", 31 | "phpunit/phpunit": "^9.6 || ^10.0" 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true, 35 | "autoload": { 36 | "psr-4": { 37 | "Abordage\\LaravelHtmlMin\\": "src" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "Abordage\\LaravelHtmlMin\\Tests\\": "tests" 43 | } 44 | }, 45 | "config": { 46 | "sort-packages": true 47 | }, 48 | "extra": { 49 | "laravel": { 50 | "aliases": { 51 | "HtmlMin": "Abordage\\LaravelHtmlMin\\Facades\\HtmlMin" 52 | }, 53 | "providers": [ 54 | "Abordage\\LaravelHtmlMin\\HtmlMinServiceProvider" 55 | ] 56 | } 57 | }, 58 | "scripts": { 59 | "phpcsf": "vendor/bin/php-cs-fixer fix --diff", 60 | "phpstan:generate-baseline": "vendor/bin/phpstan --generate-baseline", 61 | "test:all": [ 62 | "@test:phpcsf", 63 | "@test:phpstan", 64 | "@test:phpunit" 65 | ], 66 | "test:phpcsf": "vendor/bin/php-cs-fixer fix --dry-run", 67 | "test:phpstan": "vendor/bin/phpstan analyse", 68 | "test:phpunit": "vendor/bin/phpunit --colors=always" 69 | }, 70 | "scripts-descriptions": { 71 | "phpcsf": "Run PHP-CS-Fixer fix", 72 | "phpstan:generate-baseline": "Generate baseline for PHPStan", 73 | "test:all": "Run all code analysis and tests", 74 | "test:phpcsf": "Run PHP-CS-Fixer test", 75 | "test:phpstan": "Run PHPStan", 76 | "test:phpunit": "Run PHPUnit" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /config/html-min.php: -------------------------------------------------------------------------------- 1 | env('HTML_MINIFY', true), 12 | 13 | /* 14 | |-------------------------------------------------------------------------- 15 | | Find DOCTYPE in document 16 | |-------------------------------------------------------------------------- 17 | */ 18 | 'find_doctype_in_document' => true, 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Remove whitespace between tags 23 | |-------------------------------------------------------------------------- 24 | */ 25 | 'remove_whitespace_between_tags' => true, 26 | 27 | /* 28 | |-------------------------------------------------------------------------- 29 | | Remove blank lines in script elements 30 | |-------------------------------------------------------------------------- 31 | */ 32 | 'remove_blank_lines_in_script_elements' => false, 33 | ]; 34 | -------------------------------------------------------------------------------- /docs/images/abordage-laravel-html-min-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abordage/laravel-html-min/dcb6d0d21b78f658ffeed3f8cc73ac44ab4d0678/docs/images/abordage-laravel-html-min-cover.png -------------------------------------------------------------------------------- /phpstan-baseline.neon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abordage/laravel-html-min/dcb6d0d21b78f658ffeed3f8cc73ac44ab4d0678/phpstan-baseline.neon -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline.neon 3 | - ./vendor/nunomaduro/larastan/extension.neon 4 | 5 | parameters: 6 | 7 | paths: 8 | - src 9 | - tests 10 | - config 11 | 12 | # The level 9 is the highest level 13 | level: 9 14 | 15 | ignoreErrors: 16 | 17 | excludePaths: 18 | - ./*/*/FileToBeExcluded.php 19 | - ./dir/dir/FileToBeExcluded.php 20 | 21 | editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%' 22 | 23 | checkMissingIterableValueType: false 24 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Facades/HtmlMin.php: -------------------------------------------------------------------------------- 1 | findDoctypeInDocument((bool)config('html-min.find_doctype_in_document')); 14 | $this->removeWhitespaceBetweenTags((bool)config('html-min.remove_whitespace_between_tags')); 15 | $this->removeBlankLinesInScriptElements((bool)config('html-min.remove_blank_lines_in_script_elements')); 16 | 17 | return parent::minify($html); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/HtmlMinServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 17 | $this->publishes([ 18 | __DIR__ . '/../config/html-min.php' => config_path('html-min.php'), 19 | ], 'html-min-config'); 20 | } 21 | } 22 | 23 | /** 24 | * Register the application services. 25 | */ 26 | public function register(): void 27 | { 28 | $this->mergeConfigFrom(__DIR__ . '/../config/html-min.php', 'html-min'); 29 | $this->app->singleton('laravel-html-min', fn () => new HtmlMin()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Middleware/HtmlMinify.php: -------------------------------------------------------------------------------- 1 | compressionPossible($request, $response)) { 24 | return $response; 25 | } 26 | 27 | $html = $response->getContent(); 28 | 29 | if (class_exists('\BeyondCode\ServerTiming\Facades\ServerTiming')) { 30 | \BeyondCode\ServerTiming\Facades\ServerTiming::start('Minification'); 31 | } 32 | 33 | $htmlMin = HtmlMin::minify($html); 34 | 35 | if (class_exists('\BeyondCode\ServerTiming\Facades\ServerTiming')) { 36 | \BeyondCode\ServerTiming\Facades\ServerTiming::stop('Minification'); 37 | } 38 | 39 | return $response->setContent($htmlMin); 40 | } 41 | 42 | /** 43 | * @param Request $request 44 | * @param mixed $response 45 | * @return bool 46 | */ 47 | private function compressionPossible(Request $request, $response): bool 48 | { 49 | if (!config('html-min.enable')) { 50 | return false; 51 | } 52 | 53 | if (!in_array(strtoupper($request->getMethod()), ['GET', 'HEAD'])) { 54 | return false; 55 | } 56 | 57 | if (!$response instanceof Response) { 58 | return false; 59 | } 60 | 61 | if ($response->getStatusCode() >= 500) { 62 | return false; 63 | } 64 | 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/HtmlMinifyTest.php: -------------------------------------------------------------------------------- 1 | content '; 20 | protected string $htmlResponse = << 22 | 23 | 24 | 25 | Awesome 26 | Title 27 | 28 | 35 | 36 | 37 |
38 |

39 | 40 | I imagine that right now you're 41 | feeling a bit like Alice, 42 | 43 | tumbling down 44 | the rabbit hole? 45 |

46 |
47 | 48 |
49 | 50 | 56 | 57 |
58 | 59 | 60 | 61 | EOT; 62 | 63 | protected string $jsonResponse; 64 | 65 | protected function setUp(): void 66 | { 67 | parent::setUp(); 68 | 69 | $jsonData = [ 70 | 'name' => 'Alice', 71 | 'state' => 'Wonderland', 72 | 'author' => ' Lewis Carroll ' 73 | ]; 74 | $this->jsonResponse = (string)json_encode($jsonData); 75 | 76 | Route::any('/dummy-without-doctype', fn () => response($this->htmlWithoutDoctype))->middleware( 77 | HtmlMinify::class 78 | ); 79 | Route::any('/dummy-json', fn () => response()->json($jsonData))->middleware(HtmlMinify::class); 80 | Route::any('/dummy-post-500', fn () => response($this->htmlResponse, 500))->middleware(HtmlMinify::class); 81 | Route::any('/dummy-post', fn () => $this->htmlResponse)->middleware(HtmlMinify::class); 82 | } 83 | 84 | protected function getPackageProviders($app): array 85 | { 86 | return [ 87 | HtmlMinServiceProvider::class, 88 | ]; 89 | } 90 | 91 | public function testDoNotMinifyIfDoctypeIsNotFound(): void 92 | { 93 | $content = $this->get('/dummy-without-doctype')->content(); 94 | $excepted = $this->htmlWithoutDoctype; 95 | assertEquals($excepted, $content); 96 | } 97 | 98 | public function testMinifyIfDoctypeIsNotFound(): void 99 | { 100 | config(['html-min.find_doctype_in_document' => false]); 101 | $content = $this->get('/dummy-without-doctype')->content(); 102 | $excepted = '
content
'; 103 | assertEquals($excepted, $content); 104 | config(['html-min.find_doctype_in_document' => true]); 105 | } 106 | 107 | public function testDoNotMinifyIfJsonResponse(): void 108 | { 109 | $content = $this->get('/dummy-json')->content(); 110 | $excepted = $this->jsonResponse; 111 | assertEquals($excepted, $content); 112 | } 113 | 114 | public function testDoNotMinifyIfFatalError(): void 115 | { 116 | $content = $this->get('/dummy-post-500')->content(); 117 | $excepted = $this->htmlResponse; 118 | assertEquals($excepted, $content); 119 | } 120 | 121 | public function testDisableMinifyIfPutMethod(): void 122 | { 123 | $content = $this->put('/dummy-post')->content(); 124 | $excepted = $this->htmlResponse; 125 | assertEquals($excepted, $content); 126 | } 127 | 128 | public function testDisableMinify(): void 129 | { 130 | config(['html-min.enable' => false]); 131 | $content = $this->get('/dummy-post')->content(); 132 | $excepted = $this->htmlResponse; 133 | assertEquals($excepted, $content); 134 | 135 | config(['html-min.enable' => true]); 136 | } 137 | 138 | public function testMinify(): void 139 | { 140 | config(['html-min.remove_blank_lines_in_script_elements' => true]); 141 | 142 | $content = $this->get('/dummy-post')->content(); 143 | $excepted = <<Awesome Title

I imagine that right now you're feeling a bit like Alice, tumbling down the rabbit hole?

148 | EOT; 149 | assertEquals($excepted, $content); 150 | 151 | config(['html-min.remove_blank_lines_in_script_elements' => false]); 152 | } 153 | 154 | public function testMinifyWithoutScriptsAndWhitespaceBetweenTags(): void 155 | { 156 | config(['html-min.remove_whitespace_between_tags' => false]); 157 | 158 | $content = $this->get('/dummy-post')->content(); 159 | $excepted = << Awesome Title

I imagine that right now you're feeling a bit like Alice, tumbling down the rabbit hole?

166 | EOT; 167 | assertEquals($excepted, $content); 168 | 169 | config(['html-min.remove_whitespace_between_tags' => true]); 170 | } 171 | } 172 | --------------------------------------------------------------------------------