diff --git a/packages/framework/.gitattributes b/packages/framework/.gitattributes new file mode 100644 index 00000000000..bd4a8ddef66 --- /dev/null +++ b/packages/framework/.gitattributes @@ -0,0 +1,22 @@ +* text=auto + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +/bin export-ignore +/types export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.styleci.yml export-ignore +codecov.yml export-ignore +CHANGELOG-* export-ignore +CODE_OF_CONDUCT.md export-ignore +CONTRIBUTING.md export-ignore +docker-compose.yml export-ignore +phpstan.src.neon.dist export-ignore +phpstan.types.neon.dist export-ignore +phpunit.xml.dist export-ignore diff --git a/packages/framework/.github/CODE_OF_CONDUCT.md b/packages/framework/.github/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..bcf7bf8a9f7 --- /dev/null +++ b/packages/framework/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +caen@desilva.se. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/packages/framework/.github/CONTRIBUTING.md b/packages/framework/.github/CONTRIBUTING.md new file mode 100644 index 00000000000..b547507997c --- /dev/null +++ b/packages/framework/.github/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +Please read and understand the contribution guide before creating an issue or pull request. + +## Etiquette + +This project is open source, and as such, the maintainers give their free time to build and maintain the source code +held within. They make the code freely available in the hope that it will be of use to other developers. It would be +extremely unfair for them to suffer abuse or anger for their hard work. + +Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the +world that developers are civilized and selfless people. + +It's the duty of the maintainer to ensure that all submissions to the project are of sufficient +quality to benefit the project. Many developers have different skill sets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. + +## Viability + +When requesting or submitting new features, first consider whether it might be useful to others. Open +source projects are used by many developers, who may have entirely different needs to your own. Think about +whether or not your feature is likely to be used by other users of the project. + +## Procedure + +Before filing an issue: + +- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. +- Check to make sure your feature suggestion isn't already present within the project. +- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. +- Check the pull requests tab to ensure that the feature isn't already in progress. + +Before submitting a pull request: + +- Check the codebase to ensure that your feature doesn't already exist. +- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. + +## Requirements + +If the project maintainer has any additional requirements, you will find them listed here. + +We try to follow the Laravel standards, https://laravel.com/docs/9.x/contributions#coding-style + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** + +**Please add tests!** +- **Add tests!** - Your patch might not be accepted if it doesn't have tests. + +- **Document any change in behavior** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + + + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. This makes it easier to keep track of changes. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + +**Happy coding**! diff --git a/packages/framework/.github/ISSUE_TEMPLATE/blank.md b/packages/framework/.github/ISSUE_TEMPLATE/blank.md new file mode 100644 index 00000000000..e814af37eea --- /dev/null +++ b/packages/framework/.github/ISSUE_TEMPLATE/blank.md @@ -0,0 +1,4 @@ +--- +name: Blank issue +about: Start with a blank template +--- diff --git a/packages/framework/.github/ISSUE_TEMPLATE/bug_report.md b/packages/framework/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..dd84ea7824f --- /dev/null +++ b/packages/framework/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/packages/framework/.github/ISSUE_TEMPLATE/feature_request.md b/packages/framework/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000000..bbcbbe7d615 --- /dev/null +++ b/packages/framework/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/packages/framework/.github/dev-docs/advanced-markdown.md b/packages/framework/.github/dev-docs/advanced-markdown.md new file mode 100644 index 00000000000..4ef458c640c --- /dev/null +++ b/packages/framework/.github/dev-docs/advanced-markdown.md @@ -0,0 +1,53 @@ +--- +label: "Advanced Markdown" +priority: 26 +category: "Digging Deeper" +--- + +# Advanced Markdown + +## Introduction + +Since HydePHP makes heavy use of Markdown there are some extra features and helpers +created just for Hyde to make using Markdown even easier! + +## Blade Support + +Since Hyde v0.30.x you can use Laravel Blade in Markdown files! + +### Using Blade in Markdown + +To use Blade in your Markdown files, simply use the Blade shortcode directive, +followed by your desired Blade string. + +#### Standard syntax + +```markdown + [Blade]: {{ "Hello World!" }} // Will render: 'Hello World!' +``` + +#### Blade includes + +Only single-line shortcode directives are supported. If you need to use multi-line Blade code, +use an `@include` directive to render a more complex Blade template. +You can pass data to includes by specifying an array to the second argument. + +```markdown + [Blade]: @include("hello-world") + [Blade]: @include("hello", ["name" => "World"]) +``` + +### Enabling Blade-supported Markdown +It's disabled by default since it allows arbitrary PHP to run, which could be a security risk, +depending on your setup. However, if your Markdown is trusted, and you know it's safe, +you can enable it in the `config/markdown.php` file. + +```php +// torchlight! {"lineNumbers": false} +'enable_blade' => true, +``` + +#### Limitations + +All shortcodes must be the first word on a new line. +For example, using a space before the `[Blade]:` will intentionally cause it to not render. diff --git a/packages/framework/.github/dev-docs/architecture-concepts.md b/packages/framework/.github/dev-docs/architecture-concepts.md new file mode 100644 index 00000000000..8da2211971f --- /dev/null +++ b/packages/framework/.github/dev-docs/architecture-concepts.md @@ -0,0 +1,94 @@ +--- +priority: 10 +category: "Getting Started" +--- + +# Some key concepts in Hyde + +## The HydeCLI + +When you are not writing Markdown and Blade, most of your interactions with Hyde will be through the +Hyde Command Line Interface (CLI). +Since the CLI is based on the Laravel Artisan Console, so you may actually already be familiar with it. + +You should take a look at [the Console Commands page](console-commands.html) +to learn more and see the available commands and their usage. + +```bash +php hyde [--help] +``` + +## Directory structure + +To take full advantage of the framework, it may first be good to familiarize ourselves with the directory structure. + +``` +// torchlight! {"lineNumbers": false} +├── _docs // For documentation pages +├── _posts // For blog posts +├── _pages // For static Markdown and Blade pages +├── _media // Store static assets to be copied to the build directory +├── _site // The build directory where your compiled site will be stored +├── config // Configuration files for Hyde and integrations +├── resources/assets // Location for Laravel Mix source files (optional) +└── resources/views/components // Location for Blade components (optional) +``` + +> Note that the `_site` directory is emptied every time you run the `hyde build` command. +> It's intended that you keep the directory out of your VCS, for example by adding it to your .gitignore file. + + +## File Autodiscovery + +Content files, meaning source Markdown and Blade files, are automatically +discovered by Hyde and compiled to HTML when building the site. +This means that you don't need to worry about routing and controllers! + +The directory a source file is in will determine the Blade template that is used to render it. + +Here is an overview of the content source directories, the output directory of the compiled files, +and the file extensions supported by each. Files starting with an `_underscore` are ignored. + +| Page/File Type | Source Directory | Output Directory | File Extensions | +|----------------|------------------|------------------|---------------------| +| Static Pages | `_pages/` | `_site/` | `.md`, `.blade.php` | +| Blog Posts | `_posts/` | `_site/posts/` | `.md` | +| Documentation | `_docs/` | `_site/docs/` | `.md` | +| Media Assets | `_media/` | `_site/media/` | See full list below | + + +
+Media file types supported: `.png`, `.svg`, `.jpg`, `.jpeg`, `.gif`, `.ico`, `.css`, `.js` +
+
+ +## Convention over Configuration + +Hyde favours the "Convention over Configuration" paradigm and thus comes preconfigured with sensible defaults. +However, Hyde also strives to be modular and endlessly customizable hackable if you need it. +Take a look at the [customization and configuration guide](customization.html) to see the endless options available! + +## Front Matter + +All Markdown content files support Front Matter. Blog posts for example make heavy use of it. + +The specific usage and schemas used for pages are documented in their respective documentation, +however, here is a primer on the fundamentals. + +- Front matter is stored in a block of YAML that starts and ends with a `---` line. +- The front matter should be the very first thing in the Markdown file. +- Each key-pair value should be on its own line. + +**Example:** +```markdown +--- +title: "My New Post" +author: + name: "John Doe" + website: https://mrhyde.example.com +--- + +## Markdown comes here + +Lorem ipsum dolor sit amet, etc. +``` diff --git a/packages/framework/.github/dev-docs/blog-posts.md b/packages/framework/.github/dev-docs/blog-posts.md new file mode 100644 index 00000000000..8846ab31c51 --- /dev/null +++ b/packages/framework/.github/dev-docs/blog-posts.md @@ -0,0 +1,221 @@ +--- +label: "Creating Blog Posts" +priority: 11 +category: "Creating Content" +--- + +# Creating Blog Posts + +## Introduction to Hyde Posts + +Making blog posts with Hyde is easy. At the most basic level, +all you need is to add a Markdown file to your `_posts` folder. + +To use the full power of the Hyde post module however, +you'll want to add YAML Front Matter to your posts. + +You can scaffold posts with automatic front matter using the HydeCLI: +```bash +php hyde make:post +``` +Learn more about scaffolding posts, and other files, in the [console commands](console-commands.html) documentation. + + +## Short Video Tutorial + + + +## Best Practices and Hyde Expectations + +Since Hyde does a lot of things automatically, there are some things you may need +to keep in mind when creating blog posts so that you don't get unexpected results. + +### Filenames + +- Markdown post files are stored in the `_posts` directory +- The filename is used as the filename for the compiled HTML +- Filenames should use `kebab-case-slug` followed by the extension `.md` +- Files prefixed with `_underscores` are ignored by Hyde +- Your post will be stored in `_site/posts/.html` + +**Example:** +```bash +✔ _posts/hello-world.md # Valid and will be compiled to _site/posts/hello-world.html +``` + +### Front Matter + +Front matter is optional, but highly recommended for blog posts. + +You can read more about the Front Matter format in the [Front Matter documentation](architecture-concepts.html#front-matter). +Here is a quick primer: + +- Front matter is stored in a block of YAML that starts and ends with a `---` line. +- The front matter should be the very first thing in the Markdown file. +- Each key-pair value should be on its own line. +- The front matter is used to construct dynamic HTML markup for the post as well as meta tags and post feeds. + You are encouraged to look at the compiled HTML to learn and understand how your front matter is used. + + +**Example:** +```markdown +--- +title: "My New Post" +--- + +## Markdown comes here +``` + +You can use the `php hyde make:post` command to automatically generate the front matter based on your input. + + +## A first look at Front Matter + +Before digging in deeper on all the supported front matter options, +let's take a look at what a basic post with front matter looks like. + +This file was created using the `make:post` by hitting the `Enter` key to use +all the defaults (with some extra lorem ipsum to illustrate). + +```markdown {: filepath="_posts/my-new-post.md"} +--- +title: My New Post +description: A short description used in previews and SEO +category: blog +author: Mr. Hyde +date: 2022-05-09 18:38 +--- + +## Write your Markdown here + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. +Autem aliquid alias explicabo consequatur similique, +animi distinctio earum ducimus minus, magnam. +``` + +### How the Front Matter is used + +The front matter is used to construct dynamic HTML markup for the post as well as meta tags and post feeds. + +You are encouraged to look at the compiled HTML to learn and understand how your front matter is used. + +### Front matter syntax + +Here is a quick reference of the syntax Hyde uses and expects: + +```markdown +--- +key: value +string: "quoted string" +boolean: true +integer: 100 +array: + key: value + key: value +--- +``` + +## Supported Front Matter properties + +### Post Front Matter Schema + +Here is a quick reference of the supported front matter properties. +Keep on reading to see further explanations, details, and examples. + + +| **KEY NAME** | **VALUE TYPE** | **EXAMPLE / FORMAT** | +|----------------|----------------|----------------------------------| +| `title` | string | "My New Post" | +| `description` | string | "A short description" | +| `category` | string | "my favorite recipes" | +| `date` | string | "YYYY-MM-DD [HH:MM]" | +| `author` | string/array | _See [author](#author) section_ | +| `image` | string/array | _See [image](#image) section_ | + + +Note that YAML here is pretty forgiving. In most cases you do not need to wrap strings +in quotes, but it can help in certain edge cases, thus they are included here. + +In the examples below, when there are multiple keys, they signify various ways to use the parameter. + +### Title + +```yaml +title: "My New Post" +``` + + +### Description + +```yaml +description: "A short description used in previews and SEO" +``` + + +### Category + +```yaml +category: blog +category: "My favorite recipes" +``` + + +### Date + +```yaml +date: "2022-01-01 12:00" +date: "2022-01-01" +``` + + +### Author + +```yaml +author: "Mr. Hyde" # Arbitrary name displayed "as is" +author: mr_hyde # Username defined in `authors` config +author: # Array of author data + name: "Mr. Hyde" + username: mr_hyde + website: https://twitter.com/hyde_php +``` + +When specifying an array you don't need all the sub-properties. +The example just shows all the supported values. Array values here +will override all the values in the `authors` config entry. + +### Image + +```yaml +image: image.jpg # Expanded by Hyde to `_media/image.jpg` and is resolved automatically +image: https://cdn.example.com/image.jpg # Full URL starting with `http(s)://`) +image: + path: image.jpg + uri: https://cdn.example.com/image.jpg # Takes precedence over `path` + description: "Alt text for image" + title: "Tooltip title" + copyright: "Copyright (c) 2022" + license: "CC-BY-SA-4.0" + licenseUrl: https://example.com/license/ + credit: https://photographer.example.com/ + author: "John Doe" +``` + +When supplying an image with a local image path, the image is expected to be stored in the `_media/` directory. + +The image will be used as the cover image, and any array data is constructed into a dynamic fluent caption, +and injected into post and page metadata. + +> See [posts/introducing-images](https://hydephp.com/posts/introducing-images.html) +> for a detailed blog post with examples and schema information! +{ .info } + + +## Using images in posts + +To use images stored in the `_media/` directory, you can use the following syntax: + +```markdown +![Image Alt](../media/image.png "Image Title") # Note the relative path +``` + +To learn more, check out the [chapter in managing assets](managing-assets.html#managing-images) \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/compile-and-deploy.md b/packages/framework/.github/dev-docs/compile-and-deploy.md new file mode 100644 index 00000000000..5618cf80f63 --- /dev/null +++ b/packages/framework/.github/dev-docs/compile-and-deploy.md @@ -0,0 +1,82 @@ +--- +priority: 25 +label: "Compile & Deploy" +category: "Creating Content" +--- + +# Compile and Deploy your site + +## Running the build command + +### Compile the entire site to static HTML +Now that you have some amazing content, you'll want to compile your site into static HTML. + +**This is as easy as executing the `build` command:** +```bash +php hyde build +``` + +### Other ways to compile the site + +**You can also compile a single file:** +```bash +php hyde rebuild +``` + +**And, you can even start a development server to compile your site on the fly:** +```bash +php hyde serve +``` + +### Further reading + +#### Learn more about these commands in the [console commands](console-commands) documentation: + +- [Build command](console-commands#build-the-static-site) +- [Rebuild command](console-commands#build-a-single-file) +- [Serve command](console-commands#start-the-realtime-compiler) + + +#### Key Concept: Autodiscovery +When building the site, Hyde will your source directories for files and compile them into static HTML using the appropriate layout depending on what kind of page it is. You don't have to worry about routing as Hyde takes care of that, including creating navigation menus! + +--- + +## Deploying your site + +One of the things that make static sites so enjoyable to work with is how easy it is to deploy them to the web. +This list is not exhaustive, but gives you a general idea of the most common ways to deploy your site. +If you have ideas to add to the documentation, please send a pull request! + +### General deployment + +In essence, all you need to do is copy the contents of the `_site` directory to a web server and you're done. + +Once the site is compiled there is nothing to configure or worry about. + +### FTP and File Managers + +If you have a conventional web host, you can use FTP/SFTP/FTPS to upload your site to the web server. +Some web hosting services also have web based file managers. + +To deploy your site using any of these methods, all you need to do is upload the entire contents +of your `_site` directory to the web server, usually in the `public_html`, `htdocs`, or `www` directory. + +### GitHub Pages - Manually + +GitHub Pages is a free service that allows you to host your static site on the web. + +In general, push the entire contents of your `_site` directory to the `gh-pages` branch of your repository, +or the `docs/` directory on your main branch, depending on how you set it up. + +Please see the [GitHub Pages documentation](https://help.github.com/pages/getting-started-with-github-pages/) for more information. + +### GitHub Pages - CI/CD + +Hyde works amazing with GitHub Pages and GitHub Actions and the entire build and deploy process can be automated. + +HydePHP.com is hosted on GitHub Pages, and the site is compiled in a GitHub Action workflow that compiles and +deploys the site automatically when the source is updated. This is done in the [DocsCI repository](https://github.com/hydephp/DocsCI). + +You can take a look at the workflow HydePHP.com uses to create your own workflow. +See the [DocsCI build.yml on GitHub](https://github.com/hydephp/DocsCI/blob/master/.github/workflows/build.yml) diff --git a/packages/framework/.github/dev-docs/console-commands.md b/packages/framework/.github/dev-docs/console-commands.md new file mode 100644 index 00000000000..7a85dde9101 --- /dev/null +++ b/packages/framework/.github/dev-docs/console-commands.md @@ -0,0 +1,246 @@ +--- +priority: 5 +category: "Getting Started" +--- + +# Console Commands + +The primary way of interacting with Hyde is through the command line using the HydeCLI. + +If you have ever used the Artisan Console in Laravel you will feel right at home, +the Hyde CLI is based on Artisan after all! + +## Hyde Commands + +To use the HydeCLI, run `php hyde` from your project directory followed by a command. + +### Documentation syntax + +Wondering what the different formatting in examples means? Here's a quick guide: + +```bash + # Comes after the command name. +[] # Optional argument. + +--option # Sometimes referred to as a flag. +--option= # Option which takes an value. +[--option] # Optional option. +``` + +All HydeCLI commands start with `php hyde`. Anything in `[brackets]` is optional. +If an argument or option value has a space in it, it needs to be wrapped in quotes. + + + +### Got stuck? The CLI can help. + +You can always run the base command `php hyde`, or `php hyde list`, to show the list of commands. + +```bash +php hyde # or `php hyde list` +``` + +You can also always add `--help` to a command to show detailed usage information. +```bash +php hyde --help +``` + + +### Initialize a new Hyde project +```bash +php hyde install +``` + +While Hyde doesn't need much configuration to get started, this command speeds up the little there is. + +For example, it updates the config file with the supplied site name and URL, +and can also publish a starter homepage and rebuild the site. + + +### Build the static site +```bash +php hyde build +``` + +Maybe the most important command is the Build command, which -- you guessed it -- builds your static site! + +**Supports the following options:** +``` +--run-dev Run the NPM dev script after build +--run-prod Run the NPM prod script after build +--run-prettier Format the output using NPM Prettier* +--no-api Disable API calls, for example, Torchlight +``` + +> *Before v0.25.x this option was called `--pretty` + +#### Sitemaps and RSS feeds + +Sitemaps and RSS feeeds require that you have a `site_url` set, (and that you have not disabled them). + +When the features are avaliable the build commnad will generate a sitemap and RSS feed. + +You can also rebuild just the sitemap and RSS feed by using their respective commands: + +```bash +php hyde build:sitemap +php hyde build:rss +``` + +### Build a single file +```bash +php hyde rebuild +``` +Using the php hyde build command is great and all that, +but when you just need to update _that one file_ it gets a little... overkill. +To solve this problem, you can use the `rebuild` command to compile a single file. + +**Requires the following Arguments:** +``` +path The relative file path +``` + +**Example:** +```bash +php hyde rebuild _posts/hello-world.md +``` + +### Start the realtime compiler. +```bash +php hyde serve +``` + +The serve command feels similar to the Laravel Artisan serve command, but works by +starting a local PHP server. When you visit a page, the server will use the +realtime compiler to locate the source file, recompile it, and proxy +the resulting HTML and any media files to your browser. + +If you are missing the extension, you can always reinstall it with Composer `composer require hyde/realtime-compiler`. +You can also learn more on the [GitHub page](https://github.com/hydephp/realtime-compiler). + +**Supports the following options:** +``` +--port[=PORT] [default: "8080"] +--host[=HOST] [default: "localhost"] +``` + + +### Scaffold a new blog post file +```bash +php hyde make:post +``` + +At the core, blog posts are just pain ol' Markdown files. +However, by adding a special YAML syntax called Front Matter, you can add metadata to your posts. +But who can remember the syntax? You can use the `make:post` command to scaffold a new blog post file. +The command will ask you a series of interactive questions, letting you fill in the blanks. +It will then create a file, converting your input into front matter. It automatically +sets the date and time for you, and the file name will be based on the title. + + +### Scaffold a new page file +```bash +php hyde make:page [--type=TYPE] +``` + +The `make:page` command is similar to the `make:post` command and lets you quickly +create one of the following page types: + +- **Markdown**: + Creates a Markdown file in the `_pages` directory. +- **Blade**: + Creates a Blade file using the app layout in the `_pages` directory. +- **Docs**: + Creates a Markdown file in the `_docs` directory. + +In all cases, the title will be used in the created file as the page title, and to generate the filename. + +**Requires the following Arguments:** +``` +title The name of the page file to create +``` + +**Supports the following options:** +``` +--type[=TYPE] The type of page to create (markdown, blade, or docs) [default: "markdown"] +``` + +**Example:** +```bash +php hyde make:page About # Defaults to Markdown +php hyde make:page "Photo Gallery" --type=blade +php hyde make:page "Hyde CLI Guide" --type=docs +``` + +### Publish a default homepage +```bash +php hyde publish:homepage [<name>] +``` + +Hyde comes with three homepage options to choose from. The homepage you select is stored as +`_pages/index.blade.php` and becomes the `index.html` file when compiling the site. + +On a fresh install the page 'welcome' is installed. +However, you can use this command to publish another one. +If you have modified the file, you will need to supply the --force option to overwrite it. + +The available homepages are: + +- **welcome:** The default welcome page. Unlike the other pages, the styles are defined inline. +- **posts:** A Blade feed of your latest blog posts. Perfect for a blog site! +- **blank:** A blank Blade template with just the base app layout. + +You can supply the homepage name directly to the command, otherwise you will be prompted to select one. + + +### Publish the Hyde Blade views +```bash +php hyde publish:views [<category>] +``` + +Since Hyde is based on the Laravel view system the compiler uses Blade views and components. +Laravel actually registers two locations for the Hyde views: your site's `resources/views/vendor/hyde` directory and the `resources` directory located in the Framework package. + +<blockquote class="warning"> +<p>Warning: This command will overwrite any existing files in the <code>resources/views/vendor</code> directory. <br> +You should be sure to have backups, or version control such as Git, before running this command.</p> +</blockquote> + +So, when compiling a site, Laravel will first check if a custom version of the view has been placed in the `resources/views/vendor/hyde` directory by the developer (you). Then, if the view has not been customized, Laravel will search the Hyde framework view directory. This makes it easy for you to customize / override the package's views. + +The available views you can publish are: + +- **all:** Publish all categories listed below +- **layouts:** Global layout views, such as the app layout, navigation menu, and Markdown page templates. +- **components:** More or less self-contained components, extracted for customizability and DRY code. +- **404:** A beautiful 404 error page by the Laravel Collective. This file is already published by default. + +You can supply the category name directly to the command, otherwise you will be prompted to select one. + +<blockquote class="info"> +Note that when a view is updated in the framework you will need to republish the views to get the new changes! +</blockquote> + +### Republish the configuration files +```bash +php hyde update:configs +``` + +When updating Hyde to a new version (or if you mess up your config files), +you can use this command to republish the configuration files. + +<blockquote class="warning"> +<p>Warning: This command will overwrite any existing files in the <code>config</code> directory. <br> +You should be sure to have backups, or version control such as Git, before running this command.</p> +</blockquote> + + +### Run validation tests to optimize your site +```bash +php hyde validate +``` + +Hyde ships with a very useful command that runs a series of checks to validate your setup and catch any potential issues. + +> The validate command requires that [Pest](https://pestphp.com/) is installed. +> Pest is by default bundled as a dev-dependency with Hyde. \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/customization.md b/packages/framework/.github/dev-docs/customization.md new file mode 100644 index 00000000000..fb5a3d7a35e --- /dev/null +++ b/packages/framework/.github/dev-docs/customization.md @@ -0,0 +1,179 @@ +--- +label: "Customizing your Site" +priority: 25 +category: "Digging Deeper" +--- + +# Customizing your Site + +## Introduction + +Hyde favours <a href="https://en.wikipedia.org/wiki/Convention_over_configuration">"Convention over Configuration"</a> +and thus comes preconfigured with sensible defaults. However, Hyde also strives to be modular and endlessly customizable hackable if you need it. This page guides you through the endless options available! + + +## Main Configuration File +The main configuration file is in `config/hyde.php`. The [config file](https://github.com/hydephp/hyde/blob/master/config/hyde.php) is fully documented, so I recommend you take a look to see all the options. + +In this config file, you can customize the site name, what modules to enable, and programmatically customize the navigation menu and documentation sidebar. + +Here are a few examples of the config options. + +### Modules +With a concept directly inspired by [Laravel Jetstream](https://jetstream.laravel.com/), this setting allows you to toggle various modules. +```php +// torchlight! {"lineNumbers": false} +'features' => [ + Features::blogPosts(), + Features::bladePages(), + Features::markdownPages(), + // Features::documentationPages(), +], +``` + +### Authors +Hyde has support for adding authors in front matter, for example to +automatically add a link to your website or social media profiles. +However, it's tedious to have to add those to each and every +post you make, and keeping them updated is even harder. + +You can predefine authors in the Hyde config. +When writing posts, just specify the username in the front matter, +and the rest of the data will be pulled from a matching entry. + +#### Example +// torchlight! {"lineNumbers": false} +```php +'authors' => [ + Author::create( + username: 'mr_hyde', // Required username + name: 'Mr. Hyde', // Optional display name + website: 'https://hydephp.com' // Optional website URL + ), +], +``` + +This is equivalent to the following front matter: +```yaml +author: + username: mr_hyde + name: Mr. Hyde + website: https://hydephp.com +``` + +But you only have to specify the username: +```yaml +author: mr_hyde +``` + +### Footer +The footer can be customized using Markdown, and even disabled completely. + +```php +// torchlight! {"lineNumbers": false} +'footer' => [ + 'enabled' => true, + 'markdown' => 'Site built with [HydePHP](https://github.com/hydephp/hyde).' +], +``` + +### Navigation Menu & Sidebar +One of my (the author's) favourite features with Hyde is its automatic navigation menu and documentation sidebar generator. + +#### How it works: +The sidebar works by creating a list of all the documentation pages. + +The navigation menu is a bit more sophisticated, it adds all the top-level Blade and Markdown pages. It also adds an automatic link to the docs if there is an `index.md` or `readme.md` in the `_docs` directory. + +#### Reordering Items +Sadly, Hyde is not intelligent enough to determine what order items should be in (blame Dr Jekyll for this), so you will probably want to set a custom order. + +Reordering items in the documentation sidebar is as easy as can be. In the hyde config, there is an array just for this. When the sidebar is generated it looks through this config array. If a slug is found here it will get priority according to its position in the list. If a page does not exist in the list they get priority 999, which puts them last. + +Let's see an example: +```php +// torchlight! {"lineNumbers": false} +// This is the default values in the config. It puts the readme.md first in order. +'documentationPageOrder' => [ + 'readme', // This is the first entry, so it gets the priority 0 + 'installation', // This gets priority 1 + 'getting-started', // And this gets priority 2 + // Any other pages not listed will get priority 999 +] +``` + +> Navigation menu items will be ordered in the same way in a coming update, but for now, they can be reordered by overriding them which you can learn in the next section. + +#### Adding Custom Navigation Menu Links +> Until the navigation link order is implemented, you can use this feature to reorder navigation menu items. + +The links are added in the config/hyde.php file, and the syntax for adding custom links is documented in the config. Here are some examples: + +```php +// torchlight! {"lineNumbers": false} +// External link +[ + 'title' => 'GitHub', + 'destination' => 'https://github.com/hydephp/hyde', + 'priority' => 1200, +], + +// Internal link (Hyde automatically resolves relative paths) +[ + 'title' => 'Featured Blog Post', + 'slug' => 'posts/hello-world', + // The 'priority' is not required. +] +``` + +#### Removing Items (Blacklist) + +Sometimes, especially if you have a lot of pages, you may want to prevent links from showing up in the main navigation menu. To remove items from being automatically added, simply add the slug to the blacklist. As you can see, the `404` page has already been filled in for you. + +```php +'navigationMenuBlacklist' => [ + '404' +], +``` + +> Tip: You can publish the included 404 page using `php hyde publish:404`! + +## Blade Views +Hyde uses the Laravel templating system called Blade. Most parts have been extracted into components to be customized easily. + +> Before editing Blade views you should familiarize yourself with how they work in the official documentation https://laravel.com/docs/9.x/blade. + +To edit the default component you need to publish them first using the `hyde publish:views` command. + +The files will then be available in the `resources/views/vendor/hyde` directory. + +## Frontend Styles +Hyde is designed to not only serve as a framework but a whole starter kit and comes with a Tailwind starter template for you to get up and running quickly. If you want to customize these, you are free to do so. Please see the [Managing Assets](managing-assets.html) page to learn more. + + +## CommonMark environment + +Hyde uses [League CommonMark](https://commonmark.thephpleague.com/) for converting Markdown into HTML. + +Hyde ships with the GitHub Flavored Markdown extension, and +the Torchlight extension is enabled automatically when needed. + +You can add extra CommonMark extensions, or change the default ones, in the `config/markdown.php` file. + +```php +'extensions' => [ + \League\CommonMark\Extension\GithubFlavoredMarkdownExtension::class, + \League\CommonMark\Extension\Attributes\AttributesExtension::class, + \League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension::class, +], +``` + +In the same file you can also change the config to be passed to the CommonMark environment. + +```php +'config' => [ + 'disallowed_raw_html' => [ + 'disallowed_tags' => [], + ], +], +``` diff --git a/packages/framework/.github/dev-docs/directory-structure.md b/packages/framework/.github/dev-docs/directory-structure.md new file mode 100644 index 00000000000..9870577ae6a --- /dev/null +++ b/packages/framework/.github/dev-docs/directory-structure.md @@ -0,0 +1,9 @@ +--- +hidden: true +--- + +<meta http-equiv="refresh" content="0;url=architecture-concepts.html#directory-structure" /> + +Redirecting you to [architecture-concepts#directory-structure](architecture-concepts.html#directory-structure) + +<!-- Note to self, if we need a lot of these in the future it may make more sense to put them in a CI that loads redirects from a config --> \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/documentation-pages.md b/packages/framework/.github/dev-docs/documentation-pages.md new file mode 100644 index 00000000000..576314042db --- /dev/null +++ b/packages/framework/.github/dev-docs/documentation-pages.md @@ -0,0 +1,277 @@ +--- +label: Documentation Pages +priority: 12 +category: "Creating Content" +--- + +# Creating Documentation Pages + +## Introduction to Hyde Documentation Pages + +Hyde makes it easier than ever to create documentation sites. +By the way, this documentation site is of course made with the Hyde Documentation module! + +In short, all you need to do is put standard Markdown files in the `_docs/` directory and Hyde will do the rest. + +What is "the rest", you may ask? Well, for starters: + +- Hyde compiles your Markdown content into a beautiful static HTML page based on [the Lagrafo frontend](https://github.com/caendesilva/lagrafo) +- A sidebar (which is responsive) is automatically created based on your Markdown files + - If you have an `index.md` or `readme.md` in the `_docs/` directory, it will be used as the sidebar header + - You can even [customize the order and labels](#sidebar-page-order) of sidebar items +- If you have an `index.md` or `readme.md` in the `_docs/` directory, + a link to it will be added to the site navigation menu named "Docs". +- If you have a Torchlight API token in your .env file, Hyde will even automatically enable Syntax Highlighting for you. + See more about this in the [extensions page](extensions.html#torchlight). + +### Best Practices and Hyde Expectations + +Since Hyde does a lot of things automatically, there are some things you may need +to keep in mind when creating blog posts so that you don't get unexpected results. + +#### Filenames + +- Hyde Documentation pages are files are stored in the `_docs` directory +- The filename is used as the filename for the compiled HTML +- Filenames should use `kebab-case-slug` format, followed by the appropriate extension +- Files prefixed with `_underscores` are ignored by Hyde +- You should always have an `index.md` file in the `_docs/` directory +- Your page will be stored in `_site/docs/<slug>.html` unless you [change it in the config](#output-directory) + + +## Creating Documentation Pages +You can create a Documentation page by adding a file to the `_docs` directory where the filename ends in `.md`. + +You can also scaffold one quickly by using the [HydeCLI](console-commands.html). + +```bash +php hyde make:page "Page Title" --type="docs" +``` + +This will create the following file saved as `_docs/page-title.md` + +```markdown +# Page Title +``` + +### Front Matter is optional + +You don't need to use [front matter](blog-posts.html#supported-front-matter-properties) to create a documentation page. + +However, Hyde still supports front matter here as it allows you to quickly override the default values. + +Here is a quick reference, however, you should take a look at the [dynamic content](#dynamic-content-generation) section to learn more. + +```yaml +--- +title: "Page Title" +label: "Sidebar Label" +hidden: true +priority: 5 +--- +``` + + +## Dynamic content generation + +Hyde makes documentation pages easy to create by automatically generating dynamic content such as the sidebar and page title. +If you are not happy with the results you can customize them in the config or with front matter. + +Before we look at how to override things, here is an overview of the relevant content Hyde generates, +and where the data is from as well as where it can be overridden. + + +| Property | Description | Source | Override in | +|----------------------|--------------------------------------------------------|--------------------------------|----------------------| +| `title` (string) | The title of the page used in the HTML `<title>` tag | The first H1 heading (`# Foo`) | Front matter | +| `label` (string) | The label for the page shown in the sidebar | The page filename (slug) | Front matter | +| `hidden` (boolean) | Hides the page from the sidebar | _null_ | Front matter | +| `priority` (integer) | The priority of the page used for ordering the sidebar | Defaults to 999 | Front matter, config | + + +## Sidebar + +The sidebar is automatically generated from the files in the `_docs` directory. You will probably want to change the order +of these items. You can do this in two ways, either in the config or with front matter. + +### Table of contents + +Hyde automatically generates a table of contents for the page and adds it to the sidebar. + +The behaviour of this can be changed in the configuration file. +See [the customization page](customization.html#navigation-menu--sidebar) for more details. + + +### Sidebar ordering + +The sidebar is sorted/ordered by the `priority` property. The higher the priority the further down in the sidebar it will be. +The default priority is 999. You can override the priority using the following front matter: + +```yaml +priority: 5 +``` + +You can also change the order in the Docs configuration file. +See [the chapter in the customization page](customization.html#navigation-menu--sidebar) for more details. <br> + _I personally think the config route is easier as it gives an instant overview, however the first way is nice as well._ + +### Sidebar labels + +The sidebar items are labeled with the `label` property. The default label is the filename of the file. +You can change it with the following front matter: + +```yaml +label: "My Custom Sidebar Label" +``` + +### Sidebar grouping + +Sidebar grouping was introduced in Hyde [v0.24.0-beta](https://github.com/hydephp/framework/releases/tag/v0.24.0-beta) +and allows you to group items in the sidebar into categories. This is useful for creating a sidebar with a lot of items. +The Hyde docs for instance use this. + +The feature is enabled automatically when one or more of your documentation pages have the category property set +in the front matter. This will then switch to a slightly more compact sidebar layout with pages sorted into categories. +Any pages without the category front matter will get put in the "Other" category. + +To enable sidebar grouping, you can add the following front matter to your documentation pages: + +```yaml +category: "Getting Started" +``` + + +### Hiding items + +You can hide items from the sidebar by adding the `hidden` property to the front matter: + +```yaml +hidden: true +``` + +This can be useful to create redirects or other items that should not be shown in the sidebar. + +> The index page is by default not shown as a sidebar item, but instead is linked in the sidebar header. <br> +> In the future, this might be disabled by setting the `hidden` property to `false` in the front matter. + +## Customization + +Please see the [customization page](customization.html) for in-depth information on how to customize Hyde, +including the documentation pages. Here is a high level overview for quick reference though. + +### Output directory + +If you want to store the compiled documentation pages in a different directory than the default 'docs' directory, +for example to specify a version like the Hyde docs does, you can specify the output directory in the Docs configuration file. + +```php +'output_directory' => 'docs' // default +'output_directory' => 'docs/master' // What the Hyde docs use +``` + +### Sidebar header name + +By default, the site title shown in the sidebar header is generated from the configured site name suffixed with "docs". +You can change this in the Docs configuration file. + +```php +'title' => 'API Documentation', +``` + +### Sidebar page order + +To quickly arrange the order of items in the sidebar, you can reorder the page slugs in the list and the links will be sorted in that order. +Link items without an entry here will have fall back to the default priority of 999, putting them last. + +```php +'sidebar_order' => [ + 'readme', + 'installation', + 'getting-started', +] +``` + +See [the chapter in the customization page](customization.html#navigation-menu--sidebar) for more details. <br> + + +### Table of contents settings + +In the `config/docs.php` file you can configure the behavior, content, +and the look and feel of the sidebar table of contents. +You can also disable the feature completely. + +```php +'table_of_contents' => [ + 'enabled' => true, + 'min_heading_level' => 2, + 'max_heading_level' => 4, + 'smooth_page_scrolling' => true, +], +``` + +### Search feature + +The HydeSearch plugin was introduced in v0.29.0-beta and adds a search feature to documentation pages. + +The search feature is enabled by default. +You can disable it by removing the `documentationSearch` from the Hyde `Features` config array. + +The search works by generating a JSON search index which the JavaScript plugin loads asynchronously. + +Two types of search methods are added, one is a full page search screen that will saved to `docs/search.html`. +<small><blockquote>The full page can be disabled by setting `create_search_page` to `false` in the `docs` config.</blockquote></small> + +The second method is a button added to the documentation pages, similar to how Algolia DocSearch works. +Opening it will open a dialog modal with an integrated search screen. +You can also open the dialog using the keyboard shortcut `/`. + +### Automatic "Edit Page" button + +#### Introduction + +Added in v0.31, Hyde can automatically add links to documentation pages that takes the user +to a GitHub page (or similar) to edit the page. This makes it great for open-source projects +looking to allow others to contribute to the documentation in a quick and easy manner. + +The feature is automatically enabled when you specify a base URL in the Docs configuration file. +Hyde expects this to be a GitHub path, but it will probably work with other methods as well, +if not, please send a PR and/or create an issue on the [GitHub repository](https://github.com/hydephp/framework)! + +#### Example configuration + +Let's take a practical example for how HydePHP.com uses this feature. + +```php +// Filepath: config/docs.php + +'source_file_location_base' => 'https://github.com/hydephp/docs/blob/master/', +``` + +#### Changing the button text + +Changing the label is easy, just change the following config setting: + +```php +// Filepath: config/docs.php +'edit_source_link_text' => 'Edit Source on GitHub', +``` + +#### Changing the position + +By default the button will be shown in both the documentation page footer. +You can change this by setting the following config setting to `'header'`, `'footer'`, or `'both'` + +```php +// Filepath: config/docs.php +'edit_source_link_position' => 'header', +``` + +#### Adding a button icon + +This is not included out of the box, but is easy to add with some CSS! +Just target the `.edit-page-link` class. + +```css +// filepath e.g. app.css +.edit-page-link::before {content: "✏ "} +``` \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/extensions.md b/packages/framework/.github/dev-docs/extensions.md new file mode 100644 index 00000000000..6c752c62a3c --- /dev/null +++ b/packages/framework/.github/dev-docs/extensions.md @@ -0,0 +1,59 @@ +--- +label: "Extensions & Integrations" +priority: 28 +category: "Digging Deeper" +--- + +# Extensions and Integrations + +## First party extensions + +### Realtime Compiler + +The Hyde Realtime Compiler is now included with Hyde +installations and is what powers the `php hyde serve` command. + +- **GitHub**: [hydephp/realtime-compiler](https://github.com/hydephp/realtime-compiler) +- **Packagist**: [hydephp/realtime-compiler](https://packagist.org/packages/hyde/realtime-compiler) +- **YouTube video**: [Introducing the Hyde Realtime Compiler](https://www.youtube.com/watch?v=1ZM4fQMKi64) + + +## Integrations with third-party tools + +### Torchlight + +#### About Torchlight +Torchlight is an amazing API for syntax highlighting and is what this site uses. +I cannot recommend it highly enough, especially for documentation sites and code-heavy blogs! + +#### Getting started +To get started you need an API token which you can get through the [torchlight.dev website](https://torchlight.dev/). +It is entirely [free for personal and open source projects](https://torchlight.dev/#pricing). + +When you have an API token, set it in the `.env` file in the root directory of your project. +Once a token is set, Hyde will automatically enable the CommonMark extension. + +```env +TORCHLIGHT_TOKEN=torch_<your-api-token> +``` + +#### Attribution and configuration + +Note that you need to provide an attribution link, thankfully Hyde injects a customizable link automatically to all pages +that use Torchlight. You can of course disable this in the `config/torchlight.php` file. +```php +'attribution' => [ + 'enabled' => true, + 'markdown' => 'Syntax highlighting by <a href="https://torchlight.dev/" rel="noopener nofollow">Torchlight.dev</a>', +], +``` + + + + + +## Contribute + +Have an idea for an extension or integration? Let me know! I'd love to hear from you. + +Get in touch on [GitHub](https://github.com/hydephp/Hyde) or send me a DM on [Twitter](https://twitter.com/CodeWithCaen). \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/getting-started.md b/packages/framework/.github/dev-docs/getting-started.md new file mode 100644 index 00000000000..507d8caa2bc --- /dev/null +++ b/packages/framework/.github/dev-docs/getting-started.md @@ -0,0 +1,9 @@ +--- +hidden: true +--- + +<meta http-equiv="refresh" content="0;url=quickstart.html" /> + +Redirecting you to [quickstart](quickstart.html) + +<!-- Note to self, if we need a lot of these in the future it may make more sense to put them in a CI that loads redirects from a config --> \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/index.md b/packages/framework/.github/dev-docs/index.md new file mode 100644 index 00000000000..d7bc3ec76bc --- /dev/null +++ b/packages/framework/.github/dev-docs/index.md @@ -0,0 +1,41 @@ +# Elegant and Powerful Static App Builder + +<p> + <!-- <a href="https://packagist.org/packages/hyde/hyde"><img style="display: inline; margin: 4px 2px;" src="https://img.shields.io/packagist/v/hyde/hyde" alt="Latest Version on Packagist" title="Latest version of Hyde/Hyde"></a> --> + <a href="https://packagist.org/packages/hyde/framework"><img style="display: inline; margin: 4px 2px;" src="https://img.shields.io/packagist/v/hyde/framework?include_prereleases" alt="Latest Version on Packagist" title="Latest version of Hyde/Framework"></a> + <a href="https://packagist.org/packages/hyde/framework"><img style="display: inline; margin: 4px 2px;" src="https://img.shields.io/packagist/dt/hyde/framework" alt="Total Downloads on Packagist"></a> + <a href="https://github.com/hydephp/hyde/blob/master/LICENSE.md"> <img style="display: inline; margin: 4px 2px;" src="https://img.shields.io/github/license/hydephp/hyde" alt="License MIT"> </a> + <a href="https://hydephp.com/developer-tools/coverage-report/"><img style="display: inline; margin: 4px 2px;" src="https://cdn.desilva.se/microservices/coverbadges/badges/9b8f6a9a7a48a2df54e6751790bad8bd910015301e379f334d6ec74c4c3806d1.svg" alt="Test Coverage" title="Average coverage between categories"></a> + <img style="display: inline; margin: 4px 2px;" src="https://github.com/hydephp/framework/actions/workflows/test-suite.yml/badge.svg" alt="Test Suite"> <img style="display: inline; margin: 4px 2px;" src="https://github.styleci.io/repos/472503421/shield?branch=master" alt="StyleCI Status"> +</p> + +## ⚠ Beta Software Warning +Heads up! HydePHP is still new and currently in beta. Please report any bugs and issues in the appropriate issue tracker. Versions in the 0.x series might not be stable and may change at any time. No backwards compatibility guarantees are made and there will be breaking changes without notice. + +Please wait until v1.0 for production use and remember to back up your source files before updating (use Git!). See https://hydephp.com/docs/master/updating-hyde.html for the upgrade guide. + + +## About HydePHP + +HydePHP is a new Static Site Builder focused on writing content, not markup. With Hyde, it is easy to create static websites, blogs, and documentation pages using Markdown and (optionally) Blade. + +Hyde is different from other static site builders. It's blazingly fast and stupidly simple to get started with, yet it has the full power of Laravel when you need it. + +Hyde makes creating websites easy and fun by taking care of the boring stuff, like routing, writing boilerplate, and endless configuration. Instead, when you create a new Hyde project, everything you need to get started is already there -- including precompiled TailwindCSS, well crafted Blade templates, and easy asset management. + +Hyde is powered by Laravel Zero which is a stripped-down version of the robust Laravel Framework. Using Blade templates the site is intelligently compiled into static HTML. + +Hyde was inspired by JekyllRB and is created for Developers who are comfortable writing posts in Markdown. It requires virtually no configuration out of the box as it favours convention over configuration and is preconfigured with sensible defaults. + + +## Installation + +The recommended method of installation is using Composer. + +```bash +composer create-project hyde/hyde --stability=dev +``` + +For the best experience you should have PHP >= 8.0, Composer, and NPM installed. + +### To learn more, head over to the [quickstart page](quickstart.html). \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/installation.md b/packages/framework/.github/dev-docs/installation.md new file mode 100644 index 00000000000..507d8caa2bc --- /dev/null +++ b/packages/framework/.github/dev-docs/installation.md @@ -0,0 +1,9 @@ +--- +hidden: true +--- + +<meta http-equiv="refresh" content="0;url=quickstart.html" /> + +Redirecting you to [quickstart](quickstart.html) + +<!-- Note to self, if we need a lot of these in the future it may make more sense to put them in a CI that loads redirects from a config --> \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/managing-assets.md b/packages/framework/.github/dev-docs/managing-assets.md new file mode 100644 index 00000000000..8093a36dda5 --- /dev/null +++ b/packages/framework/.github/dev-docs/managing-assets.md @@ -0,0 +1,115 @@ +--- +priority: 20 +category: "Creating Content" +--- + +# Managing and Compiling Assets + +## Introduction + +Managing and compiling assets is a very common task in web development. Unfortunately, it's rarely fun. + +With hyde, **you don't have to do it**, in fact, you can skip this entire page if you are happy with how it is. +But as always with Hyde, you can customize everything if you want to. + +Hyde ships with a complete frontend where base styles and scripts are included through [HydeFront](https://github.com/hydephp/hydefront) which adds accessibility and mobile support as well as interactions for dark mode switching and navigation and sidebar interactions. + +HydeFront is split into two files, `hyde.css` and `hyde.js`. These are loaded in the default Blade views using the [jsDelivr CDN](https://www.jsdelivr.com/package/npm/hydefront). This is the recommended way to load the base styles as the [Hyde Framework](https://github.com/hydephp/framework) automatically makes sure that the correct HydeFront version for the current version of Hyde is loaded. If you don't want to use HydeFribtm you can customize the `styles.blade.php` file. + +The bulk of the frontend is built with [TailwindCSS](https://tailwindcss.com/). To get you started, when installing Hyde, all the Tailwind styles you need come precompiled and minified into `_media/app.css`. + +## Some extra information, and answers to possible questions + +### Do I have to use NPM to use Hyde? +No. NPM is optional as all the compiled styles you need are already installed. You only need NPM if you want to compile your own styles. + +### When do I need to compile assets? + +#### When customizing +If you want to customize the Tailwind settings or add custom styles, you will need to take care of compiling the styles yourself. + +#### When adding new classes +The `_media/app.css` file that comes with Hyde contains TailwindCSS for all classes that are used in the default Blade views, but nothing more. + +If you customize the Blade views and add new classes, or if you add new classes in Blade-based pages, you may need to compile the assets yourself to get the new styles. + +If you stick to using Markdown based pages, you don't need to compile anything. + +## How are assets stored and managed? + +Currently, the frontend assets are separated into three places. + +The `resources/assets` contains **source** files, meaning files that will be compiled into something else. Here you will find the `app.css` file that bootstraps the TailwindCSS styles. This file is also an excellent place to add your custom styles. + +The `_media` folder contains **compiled** (and usually minified) files. When Hyde compiles your static site, all asset files here will get copied as they are into the `_site/media` folder. + +The `_site/media` folder contains the files that are served to the user. + +### What is the difference between `_media` and `_site/media`? +It may seem weird to have two folders for storing the compiled assets, but it is quite useful. + +The `_site` directory is intended to be excluded from version control while the `_media` folder is included in the version control, though you may choose to exclude the compiled files from the `_media` folder if you want to. + +You are of course free to modify this behavior by editing the `webpack.mix.js` file. + +## How do I compile assets? + +First, make sure that you have installed all the NodeJS dependencies using `npm install`. +Then run `npm run dev` to compile the assets. If you want to compile the assets for production, run `npm run prod`. +You can also run `npm run watch` to watch for changes in the source files and recompile the assets automatically. + +### How does it work? + +Hyde uses [Laravel Mix](https://laravel-mix.com/) (which is a wrapper for [webpack](https://webpack.js.org/)) to compile the assets. + +When running the `npm run dev/prod` command, the following happens: + +1. Laravel Mix will compile the `resources/assets/app.css` file into `_media/app.css` using PostCSS with TailwindCSS and AutoPrefixer. +2. Mix then copies the `_media` folder into `_site/media`, this is so that they are automatically accessible to your site without having to rerun `php hyde build`. + + +### Compile TailwindCSS directly + +Since Laravel Mix can be a bit slow to boot, you can also run the TailwindCSS compiler directly. +You'll need to have the [TailwindCSS CLI](https://tailwindcss.com/docs/cli) installed, and you will +need to run the build command afterwards to transfer the compiled styles into the build output. + +```bash +npx tailwindcss -i resources/assets/app.css -o _media/app.css + +php hyde build OR php hyde rebuild _media +``` + + +## Managing images +As mentioned above, assets stored in the _media folder are automatically copied to the _site/media folder, +making it the recommended place to store images. You can then easily reference them in your Markdown files. + +### Referencing images + +The recommended way to reference images are with relative paths as this offers the most compatibility, +allowing you to browse the site both locally on your filesystem and on the web when serving from a subdirectory. + +> Note: The path is relative to the **compiled** file +{.warning} + +The path to use depends on the location of the page. Note the subtle difference in the path prefix. + +- If you are in a **Blog Post or Documentation Page**, use `../media/image.png` +- If in a **Markdown Page or Blade Page**, use `media/image.png` +- While not recommended, you can also use absolute paths: `/media/image.png` + +#### Making images accessible + +To improve accessibility, you should always add an `alt` text. Here is a full example for an image in a blog post: + +```markdown +![Image Alt](../media/image.png "Image Title") # Note the relative path +``` + +### Setting a featured image for blog posts + +Hyde offers great support for creating data-rich and accessible featured images for blog posts. + +You can read more about this in the [creating blog posts page](blog-posts.html#image). + diff --git a/packages/framework/.github/dev-docs/quickstart.md b/packages/framework/.github/dev-docs/quickstart.md new file mode 100644 index 00000000000..ec426dd53d1 --- /dev/null +++ b/packages/framework/.github/dev-docs/quickstart.md @@ -0,0 +1,107 @@ +--- +label: Quickstart Guide +priority: 1 +category: "Getting Started" +--- + +# Quickstart Guide + +## Installing HydePHP using Composer +The recommended method of installing Hyde is using Composer. +```bash +// torchlight! {"lineNumbers": false} +composer create-project hyde/hyde --stability=dev +``` + +### Requirements +> These requirements are for your local development environment. + +Hyde is based on [Laravel 9](https://laravel.com/docs/9.x/releases) +which requires a minimum PHP version of 8.0. +You should also have [Composer](https://getcomposer.org/) installed. + +To use some features like [compiling your own assets](managing-assets.html) +you also need NodeJS and NPM. + + +## Using the Hyde CLI +The main way to interact with Hyde is through HydeCLI. + +If you are familiar with Laravel Artisan you will feel right at home. + +Learn more about the HydeCLI in the [console commands](console-commands.html) documentation. + +## Starting a development server + +To make previewing your site a breeze you can use the real-time compiler +which builds your pages on the fly. Start it using the HydeCLI: +```bash +php hyde serve +``` + +## Creating content + +### Directory structure + +Creating content with Hyde is easy. Simply place Markdown files in one of the source directories, which are as follows: +``` +// torchlight! {"lineNumbers": false} +├── _docs // For documentation pages +├── _posts // For blog posts +└── _pages // For static Markdown and Blade pages +``` + +> There are a few more directories that you should know about. Please see the +> [directory structure](architecture-concepts.html#directory-structure) section. + +### Scaffolding files + +You can scaffold blog post files using the `php hyde make:post` command with automatically creates the front matter based on your input selections. + +You can also scaffold pages with the `php hyde make:page` command. + +```bash +php hyde make:page "Page Title" # Markdown is the default page type +php hyde make:page --type=blade # Creates a file extending the default layout +php hyde make:page --type=docs # Quickly creates a documentation page +``` + +### Autodiscovery + +When building the site, Hyde will your source directories for files and +compile them into static HTML using the appropriate layout depending +on what kind of page it is. You don't have to worry about routing +as Hyde takes care of that, including creating navigation menus! + +## Compiling to static HTML + +Now that you have some amazing content, you'll want to compile your site into static HTML. + +This is as easy as executing the `build` command: +```bash +php hyde build +``` + +**Your site is then stored in the `_site` directory.** + +### Managing assets + +Hyde comes bundled with a precompiled and minified `app.css` containing all the Tailwind you need for the default views meaning that you don't even need to use NPM. However, Hyde is already configured to use Laravel Mix to compile your assets if you feel like there's a need to. See more on the [Managing Assets](managing-assets.html) page. + +### Deploying your site + +You are now ready to show your site to the world! + +Simply copy the `_site` directory to your web server's document root, and you're ready to go. + +You can even use GitHub pages to host your site for free. That's what the Hyde website does, +using a CI that automatically builds and deploys this site. + + +## Further reading + +Here's some ideas of what to read next: + +- [Architecture Concepts & Directory Structure](architecture-concepts.html) +- [Console Commands with the HydeCLI](console-commands.html) +- [Creating Blog Posts](blog-posts.html) \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/static-pages.md b/packages/framework/.github/dev-docs/static-pages.md new file mode 100644 index 00000000000..3533e253996 --- /dev/null +++ b/packages/framework/.github/dev-docs/static-pages.md @@ -0,0 +1,141 @@ +--- +priority: 11 +label: "Markdown & Blade Pages" +category: "Creating Content" +--- + +# Creating Static Pages + +## Introduction to Hyde Pages + +Hyde offers two ways to create static pages: +**Markdown pages** which are perfect for simple pages that focuses heavily on the content, +and **Blade pages** which are perfect for more complex pages where you want full control over the HTML, +and where you may want to include other components. + +Let's start with the basics. + +### Best Practices and Hyde Expectations + +Since Hyde does a lot of things automatically, there are some things you may need +to keep in mind when creating blog posts so that you don't get unexpected results. + +#### Filenames + +- Hyde Pages are files are stored in the `_pages` directory +- The filename is used as the filename for the compiled HTML +- Filenames should use `kebab-case-slug` format, followed by the appropriate extension +- Files prefixed with `_underscores` are ignored by Hyde +- Your page will be stored in `_site/<slug>.html` +- Blade pages will override any Markdown pages with the same filename when compiled + +## Creating Markdown Pages + +Markdown pages are the easiest way to create static pages, and are similar to [blog posts](blog-posts.html). +You may want to read that page first as it explains [how front matter works](blog-posts.html#supported-front-matter-properties) +and how to use it. + +You can create a Markdown page by adding a file to the `_pages` directory where the filename ends in `.md`. + +### Scaffolding Markdown Pages +Scaffolding a Markdown page is as easy as using the [HydeCLI](console-commands.html). + +```bash +php hyde make:page "Page Title" +``` + +This will create the following file saved as `_pages/page-title.md` + +```markdown +--- +title: Page Title +--- + +# Page Title + +// Write your content here +``` + +You can of course also create the file yourself with your text editor. + +### Front Matter is optional + +The only front matter supported is the title, which is used as the HTML `<title>`. + +If you don't supply a front matter title, Hyde will attempt to find a title in the Markdown body by searching +for the first level one heading (`# Page Title`), and if that fails, it will generate one from the filename. + +In the future, more front matter options such as page descriptions and meta tags will be supported. + + +## Creating Blade Pages + +Since Hyde is based on Laravel and uses the Blade templating engine, +you can use Blade pages to create more complex pages. + +If you are not familiar with Blade, you may want to read [the Laravel Blade docs](https://laravel.com/docs/9.x/blade) first. + + +### Scaffolding Blade Pages +We can scaffold Blade pages using the same CLI command as Markdown pages, however, +this time we need to specify that we want to use the `blade` page type. + +```bash +php hyde make:page "Page Title" --type="blade" +``` + +This will create a file saved as `_pages/page-title.blade.php` + +You can of course also create the file yourself with your text editor, however, +the scaffolding command for Blade pages is arguably even more helpful than the +one for Markdown pages, as this one automatically adds the included app Layout. + +Let's take a look at the scaffolded file. You can also copy and paste this +if you don't want to use the scaffolding command. + +```blade +@extends('hyde::layouts.app') +@section('content') +@php($title = "Page Title") + +<main class="mx-auto max-w-7xl py-16 px-8"> + <h1 class="text-center text-3xl font-bold">Page Title</h1> +</main> + +@endsection +``` + +> Tip: You don't have to use Blade in Blade pages. It's also perfectly fine to use plain HTML, +> however you still need to use the `blade.php` extension so Hyde can recognize it. + + +## When to use which? + +Markdown pages look great and work well for simple "about" pages and the like, but with Markdown we are still pretty limited. + +If you are comfortable with it, and have the need for it, use Blade to create more complex pages! And mix and match between them! Some page types are better suited for Markdown, and others for Blade. + +### Comparison + +| Markdown | Blade | +|-----------------------------------------------------|------------------------------------------------------------------------------------------| +| ➕ Easily created and updated | ➕ Full control over the HTML | +| ➕ Very fast to create simple and lightweight pages | ➕ Use the default app layout or create your own | +| ➕ Suited for content heavy pages such as "about us" | ➕ Use Blade templates and components to keep code DRY | +| ➖ Not as flexible as Blade pages | ➕ Use arbitrary PHP right in the page to create dynamic content | +| | ➕ Access to all Blade helper directives like @foreach, @if, etc. | +| | ➖ Takes longer to create as as you need to write the markup | +| | ➖ You may need to [recompile your CSS](managing-assets.html) if you add Tailwind classes | + + +### Live Demos + +The Hyde website ([hydephp.com](https://hydephp.com/)) uses both Markdown and Blade pages. + +The "Privacy" which you can find at [hydephp.com/privacy](https://hydephp.com/privacy) is a Markdown page, +which is a perfect fit for this task, where the goal was to simply inform about the privacy policy. + +The "Gallery" which you can find at [hydephp.com/gallery](https://hydephp.com/gallery) is a Blade page. +While a photo gallery could be used in a Markdown page, here I opted to use a Blade page instead. This allowed me +to create a bunch of cool and dynamic interactions and animations as I had full control over the HTML and could +easily add scripts, styles, and iframes. I also seperated sections into components to make them easier to manage. diff --git a/packages/framework/.github/dev-docs/troubleshooting.md b/packages/framework/.github/dev-docs/troubleshooting.md new file mode 100644 index 00000000000..d0d6acdec7a --- /dev/null +++ b/packages/framework/.github/dev-docs/troubleshooting.md @@ -0,0 +1,105 @@ +--- +priority: 35 +category: "Digging Deeper" +--- + +# Troubleshooting + +Since Hyde has a lot of "magic" features which depend on some base assumptions, +there might be some "gotchas" you might run into. Here are some I can think of, +did you find a new one? Send a PR to [update the docs](https://github.com/hydephp/docs)! + +> Tip: You can run `php hyde validate` to run a series of tests to help you catch common issues. +{.info} + + +## General Tips +(In no particular order of importance) + +1. In general, **Hyde is actually pretty forgiving**. While this article makes it sound like there are a lot of rules to follow, + honestly don't worry about it. Hyde will attempt to fix mistakes and make your life easier. +2. You don't need to set an H1 heading in blog posts. The H1 is set by Hyde based on the front matter title. +3. You never need front matter, though it is often useful. + For example, Hyde makes attempts to guess the title for a page depending on the content. (Headings, filenames, etc). +4. Currently, Hyde does not support nested directories besides those already defined. + This means that for example files in `_posts/foo/bar/` will not be compiled. + This is a feature that will be added in the future. + + +## Conventions to follow + +### File naming + +For Hyde to be able to discover your files, you should follow the following conventions. + +Markdown files should have the extension `.md`. Blade files should have the extension `.blade.php`. + +Unexpected behaviour might occur if you use conflicting file names. +All the following filenames are resolved into the same destination file: +`foo-bar.md`, `Foo-Bar.md`, `foo-bar.blade.php`, causing only one of them to be saved. + +Remember, files retain their slugs when compiled to HTML. + +#### Summary +- ✔ **Do** use lowercase filenames and extensions +- ✔ **Do** use filenames written in kebab-case-format +- ✔ **Do** use the proper file extensions + +- ❌ **Don't** use conflicting source file names + +## Extra Information + +### Definitions + +We will use the following definitions to describe the behaviour of Hyde. +Based on [this blog post](https://github.com/hydephp/DocsCI/blob/ff4589b175c2794b0dfd4eedfe975fb02d20523c/_posts/draft-developer-definitions.markdown). + +- **Hyde**: The application that you are using. +- **HydeCLI**: The command-line interface for Hyde. +- **Framework**: The Hyde core codebase. + +- **Slug**: The filename without the extension (basename). Example: `hello-world` +- **Filename**: The full name of a file with the extension. Example: `hello-world.md` +- **Filepath**: The full file path including extension (almost always relative to the Hyde project) Example: `_posts/hello-world.md` + +<style> +#document-main-content > ul > li > p { + margin-top: 0; + margin-bottom: 0; +} +</style> + + +## Common issues, causes, and solutions + +| Issue | Possible Cause | Possible Solution | +|----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 404 error when visiting site | Are you missing an index file in the _pages directory? | Add an index.md or index.blade.php | +| Navigation menu not linking to the docs | You probably don't have an index.md file in the _docs directory. | Create an index file | +| Page not discovered when compiling | The file name may be invalid | Ensure you follow the correct file naming convention. | +| Page compiles slowly | The Torchlight extension may cause the compile times to increase as API calls need to be made. | Try disabling Torchlight | +| Torchlight not working | Missing Composer package, missing API token, extension disabled in the config. | Reinstall Torchlight, add your token in the .env file, check config | +| Error in Parser.php: "unable to parse" | Could be an issue with the YAML front matter parser. | Try adding a block of front matter to the top of the file | +| Missing styles and/or assets | You may have accidentally deleted the files, or you have added new Tailwind classes. | Run `npm run dev` | +| Image not found | You may be using a bad relative path. See [managing-assets](managing-assets.html#referencing-images). | Ensure your relative paths are correct. | +| Wrong layout used | Hyde determines the layout template to use depending on the directory of the source file | Ensure your source file is in the right directory. | +| Invalid/no permalinks or post URIs | You may be missing or have an invalid site URL | Set the site URL in the .env file | +| No styles in custom Blade pages | When using custom blade pages need to add the styles yourself. You can do this by extending the default layout | Use the app layout, or by include the Blade components directly. | +| Overriding Hyde views is not working | Ensure the Blade views are in the correct directory. | Rerun php hyde publish:views. | +| Styles not updating when deploying site | It could be a caching issue. To be honest, when dealing with styles, it's always a caching issue. | Clear your cache, and optionally complain to your site host | +| Documentation sidebar items are in the wrong order | Double check the config, make sure the slugs are written correctly. Check that you are not overriding with front matter. | Check config for typos and front matter | +| Documentation table of contents is weird | The table of contents markup is generated by the [Leauge/CommonMark extension](https://commonmark.thephpleague.com/2.3/extensions/table-of-contents/) | Make sure that your Markdown headings make sense | +| Issues with date in blog post front matter | The date is parsed by the PHP strtotime() function. The date may be in an invalid format, or the front matter is invalid | Ensure the date is in a format that strtotime() can parse. Wrap the front matter value in quotes. | +| RSS feed not being generated | The RSS feed requires that you have set a site URL in the Hyde config or the .env file. Also check that you have blog posts, and that they are enabled. | Check your configuration files. | | +| Sitemap not being generated | The sitemap requires that you have set a site URL in the Hyde config or the .env file. | Check your configuration files. | | +| Unable to do literally anything | If everything is broken, you may be missing a Composer package or your configuration files could be messed up. | Run `composer install` and/or `composer update`. If you can run HydeCLI commands, update your configs with `php hyde update:configs`, or copy them manually from GitHub or the vendor directory. | + +### Extra troubleshooting information + +#### Fixing a broken config +If your configuration is broken, you might not be able to run any commands through the HydeCLI. +To remedy this you can copy the config files from the vendor directory into the project directory. +You can do this manually, or with the following rescue command: +``` +copy .\vendor\hyde\framework\config\hyde.php .\config\hyde.php +``` \ No newline at end of file diff --git a/packages/framework/.github/dev-docs/updating-hyde.md b/packages/framework/.github/dev-docs/updating-hyde.md new file mode 100644 index 00000000000..be0ffd29e7a --- /dev/null +++ b/packages/framework/.github/dev-docs/updating-hyde.md @@ -0,0 +1,89 @@ +--- +priority: 35 +category: "Digging Deeper" +--- + +# Updating Hyde + +## While Hyde is in beta, stuff can change rapidly. +This guide will help you update Hyde to the latest version. It is recommended to back up your source files before updating. + +## Updating Hyde/Framework + +Run the following command from your Hyde/Hyde installation: +```bash +composer update hyde/framework +``` + +Next, follow the post-update instructions for Hyde/Hyde. + +## Updating Hyde/Hyde +When updating an existing installation, first ensure you have a Git backup of your source files to revert the update. + +Depending on how you installed Hyde, there are a few different ways to update it. + +### Using Git +Make sure you have a remote set up for the repository. +```bash +git remote add upstream https://github.com/hydephp/hyde.git +``` + +Then pull the latest changes from the remote: +```bash +git pull upstream master +``` + +After this, you should update your composer dependencies: +```bash +composer update +``` + +Next, follow the post-update instructions for Hyde/Hyde. + +### Manual Update +Since all resource files are in the content directories you can simply copy those files to the new location. + +If you have changed any other files, for example in the App directory, you will need to update those files manually as well. But if you have done that you probably know what you are doing. I hope. The same goes if you have created any custom blade components or have modified Hyde ones. + +Example CLI workflow, assuming the Hyde/Hyde project is stored as `my-project` in the home directory: +```bash +cd ~ +mv my-project my-project-old +composer create-project hyde/hyde my-project + +cp -r my-old-project/_pages my-project/content/_pages +cp -r my-old-project/_posts my-project/content/_posts +cp -r my-old-project/_media my-project/content/_media +cp -r my-old-project/_docs my-project/content/_docs +cp -r my-old-project/config my-project/config +``` + +Next, follow the post-update instructions for Hyde/Hyde. After verifying that everything is working, you can delete the old project directory. + +## Post-update instructions +After updating Hyde you should update your config and resource files. This is where things can get a tiny bit dangerous as the files will be overwritten. However, since you should be using Git, you can take care of any merge conflicts that arise. + +```bash +php hyde update:configs +php hyde update:assets +``` + +If you have published any of the Hyde Blade components you will need to re-publish them. + +```bash +php hyde publish:views layouts +php hyde publish:views components +``` + +Next, re-build your site. + +```bash +php hyde build +``` + +And recompile your assets if applicable. + +```bash +npm install +npm run dev/prod +``` diff --git a/packages/framework/.github/release.yml b/packages/framework/.github/release.yml new file mode 100644 index 00000000000..d8d6482aff3 --- /dev/null +++ b/packages/framework/.github/release.yml @@ -0,0 +1,4 @@ +changelog: + exclude: + labels: + - ignore-for-release diff --git a/packages/framework/.github/workflows/code-reports.yml b/packages/framework/.github/workflows/code-reports.yml new file mode 100644 index 00000000000..d3de31a6bdc --- /dev/null +++ b/packages/framework/.github/workflows/code-reports.yml @@ -0,0 +1,100 @@ +name: Create Code Reports + +on: + push: + branches: [master] + + workflow_dispatch: + +jobs: + create-coverage-report: + runs-on: ubuntu-latest + environment: + name: live-coverage-report + url: https://hydephp.com/developer-tools/coverage-report/ + + + steps: + - uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e + with: + php-version: "8.0" + coverage: xdebug + extensions: fileinfo + - name: Install Hyde + run: git clone https://github.com/hydephp/hyde.git $(pwd) + - name: Install Dependencies + run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + - name: Set Framework version to dev-master + run: composer require hyde/framework:dev-master + - name: Set environment to testing + run: echo "ENV=testing" > .env + - name: Run tests with HTML coverage reporting + run: vendor/bin/pest --coverage-html covrep + env: + ENV: testing + + - name: Upload the report as an artifact + uses: actions/upload-artifact@v1 + with: + name: "coverage-report" + path: "covrep" + + build-php-docs: + runs-on: ubuntu-latest + environment: + name: internal-api-docs + url: https://hydephp.com/developer-tools/api-docs/ + steps: + - name: Download the Framework + run: git clone https://github.com/hydephp/framework.git + + - name: Download phpDocumentor + run: | + wget https://phpdoc.org/phpDocumentor.phar + chmod +x phpDocumentor.phar + + - name: Run phpDocumentor + run: php phpDocumentor.phar -d framework/src -t api-docs + + - name: Upload the documentation as an artifact + uses: actions/upload-artifact@v1 + with: + name: "api-docs" + path: "api-docs" + + upload-html-files: + needs: [create-coverage-report, build-php-docs] + runs-on: ubuntu-latest + + steps: + - name: Download coverage report artifact + uses: actions/download-artifact@v3 + with: + name: coverage-report + path: coverage-report + + - name: Download api docs artifact + uses: actions/download-artifact@v3 + with: + name: api-docs + path: api-docs + + - name: Create .nojekyll file + run: touch .nojekyll + + - name: Write run metadata to file + run: | + currentDate=`date` + echo "Report automatically uploaded by GitHub at $currentDate" > coverage-report/report-information.txt + echo "Report automatically uploaded by GitHub at $currentDate" > api-docs/report-information.txt + + - name: Upload files to GitHub Pages + uses: cpina/github-action-push-to-another-repository@v1.4.2 + + env: + API_TOKEN_GITHUB: ${{ secrets.PAT }} + with: + source-directory: "." + destination-github-username: "hydephp" + destination-repository-name: "developer-tools" + target-branch: gh-pages diff --git a/packages/framework/.github/workflows/dependency-review.yml b/packages/framework/.github/workflows/dependency-review.yml new file mode 100644 index 00000000000..bdcdf9b50b6 --- /dev/null +++ b/packages/framework/.github/workflows/dependency-review.yml @@ -0,0 +1,20 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Reqest, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: "Dependency Review" +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: "Checkout Repository" + uses: actions/checkout@v3 + - name: "Dependency Review" + uses: actions/dependency-review-action@v1 diff --git a/packages/framework/.github/workflows/snyk-analysis.yml b/packages/framework/.github/workflows/snyk-analysis.yml new file mode 100644 index 00000000000..adbbcb6d9e9 --- /dev/null +++ b/packages/framework/.github/workflows/snyk-analysis.yml @@ -0,0 +1,19 @@ +name: Snyk Security Analysis + +on: push +jobs: + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Run Snyk to check for vulnerabilities + uses: snyk/actions/php@master + continue-on-error: true # To make sure that SARIF upload gets called + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --sarif-file-output=snyk.sarif + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: snyk.sarif diff --git a/packages/framework/.github/workflows/test-suite.yml b/packages/framework/.github/workflows/test-suite.yml new file mode 100644 index 00000000000..98342b8073e --- /dev/null +++ b/packages/framework/.github/workflows/test-suite.yml @@ -0,0 +1,137 @@ +name: Test Suite (Matrix) +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + # First run tests with coverage. If this fails, we don't need to run the matrix. + test-coverage: + runs-on: ubuntu-latest + + steps: + - uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e + with: + php-version: "8.0" + coverage: xdebug + extensions: fileinfo + - name: Install Hyde + run: git clone https://github.com/hydephp/hyde.git $(pwd) + - name: Install Dependencies + run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + + - name: Set Framework version to dev-github.base_ref + if: github.event_name != 'push' + run: "composer require hyde/framework:dev-${{ github.base_ref }}" + - name: Set Framework version to dev-master + if: github.event_name == 'push' + run: "composer require hyde/framework:dev-master" + + - name: Set environment to testing + run: echo "ENV=testing" > .env + - name: Output debug information + run: php hyde debug + + - name: Run tests with output coverage reporting + run: vendor/bin/pest --coverage --coverage-text=report.txt --colors=always --coverage-clover build/coverage/clover.xml --coverage-cobertura build/coverage/cobertura.xml --coverage-crap4j build/coverage/crap4j.xml --coverage-xml build/coverage/coverage-xml --log-junit build/junit.xml + env: + ENV: testing + + - name: Upload the report as an artifact + uses: actions/upload-artifact@v1 + with: + name: "report.txt" + path: "report.txt" + + - name: "Publish coverage report to Codecov" + uses: codecov/codecov-action@v3 + with: + #token: ${{ secrets.CODECOV_TOKEN }} + directory: ./build/coverage + + hyde-tests: + needs: test-coverage + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + + steps: + - uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e + with: + php-version: "8.0" + extensions: fileinfo + - name: Install Hyde + run: git clone https://github.com/hydephp/hyde.git $(pwd) + - name: Install Dependencies + run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + + - name: Set Framework version to dev-github.base_ref + if: github.event_name != 'push' + run: "composer require hyde/framework:dev-${{ github.base_ref }}" + - name: Set Framework version to dev-master + if: github.event_name == 'push' + run: "composer require hyde/framework:dev-master" + + - name: Set environment to testing + run: echo "ENV=testing" > .env + - name: Execute tests (Unit and Feature tests) via PHPUnit/Pest + run: vendor/bin/pest + env: + ENV: testing + + update-coverage-badge: + runs-on: ubuntu-latest + needs: test-coverage + if: github.event_name == 'push' + + steps: + - uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e + with: + php-version: "8.0" + extensions: fileinfo + + - name: Download report + uses: actions/download-artifact@v3 + with: + name: report.txt + + - name: Download Coverage Parser + run: "wget https://gist.githubusercontent.com/caendesilva/75a5ff8dd4e23921b75f503149655924/raw/456bd4bae423eda2a3ff2ba2ce8f8246f366d46f/parser.php" + + - name: Run the parser + id: run-coverage-parser + run: echo ::set-output name=average_coverage::$(php parser.php) + - name: Output the value + run: echo Result ${{ steps.run-coverage-parser.outputs.average_coverage }} + - name: Post the result to server + run: 'curl -X POST https://cdn.desilva.se/microservices/coverbadges/api.php -H "Content-Type: application/x-www-form-urlencoded" -d "repo=https://github.com/hydephp/hyde&value=${{ steps.run-coverage-parser.outputs.average_coverage }}&token=${{ secrets.COVERBADGES_SECRET }}"' + + - name: Download the SVG badge + run: "wget https://cdn.desilva.se/microservices/coverbadges/badges/9b8f6a9a7a48a2df54e6751790bad8bd910015301e379f334d6ec74c4c3806d1.svg" + + - name: Upload the SVG badge as an artifact + uses: actions/upload-artifact@v1 + with: + name: "badge.svg" + path: "9b8f6a9a7a48a2df54e6751790bad8bd910015301e379f334d6ec74c4c3806d1.svg" + + purge-camo-content: + runs-on: ubuntu-latest + needs: update-coverage-badge + if: github.event_name == 'push' + steps: + - name: Download Purge Script + run: "wget https://raw.githubusercontent.com/hydephp/developer-tools/46734ae87ec74ac27d7c81f18b58ec52362340f2/scripts/purge-camo-cache.php" + + - name: Run the script + run: php purge-camo-cache.php + + validate-composer: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Validate composer.json and composer.lock + run: composer validate --strict \ No newline at end of file diff --git a/packages/framework/.github/workflows/update-changelog.yml b/packages/framework/.github/workflows/update-changelog.yml new file mode 100644 index 00000000000..838c6176c46 --- /dev/null +++ b/packages/framework/.github/workflows/update-changelog.yml @@ -0,0 +1,34 @@ +name: Update Changelog + +on: + release: + types: [published] + + workflow_dispatch: + +jobs: + update-changelog: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-node@v3 + + - name: Install Auto Changelog + run: npm install -g auto-changelog + + - name: Generate the Changelog + run: auto-changelog + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + with: + commit-message: Automatic Changelog Update + title: Update Changelog + body: Update changelog to reflect release changes + branch: update-changelog + base: master + labels: ignore-for-release + delete-branch: true diff --git a/packages/framework/.gitignore b/packages/framework/.gitignore new file mode 100644 index 00000000000..dd284bf5d2a --- /dev/null +++ b/packages/framework/.gitignore @@ -0,0 +1,16 @@ +/vendor +/.run +/.idea +/.vscode +/.vagrant +.phpunit.result.cache + +.phpdoc +phpDocumentor.phar +phpdoc.dist.xml +api-docs + +phpstan.phar + +.github/dev-docs/README.markdown +.github/dev-docs/.github/ \ No newline at end of file diff --git a/packages/framework/.styleci.yml b/packages/framework/.styleci.yml new file mode 100644 index 00000000000..0285f1790d5 --- /dev/null +++ b/packages/framework/.styleci.yml @@ -0,0 +1 @@ +preset: laravel diff --git a/packages/framework/CHANGELOG.md b/packages/framework/CHANGELOG.md new file mode 100644 index 00000000000..acf28ccec41 --- /dev/null +++ b/packages/framework/CHANGELOG.md @@ -0,0 +1,1037 @@ +### Changelog + +All notable changes to this project will be documented in this file. Dates are displayed in UTC. + +Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). + +#### [v0.34.0](https://github.com/hydephp/framework/compare/v0.33.0-beta...v0.34.0) + +> 6 June 2022 + +- Deprecate Hyde::features(), use Hyde::hasFeature() instead [`#523`](https://github.com/hydephp/framework/pull/523) +- Apply fixes from StyleCI [`#524`](https://github.com/hydephp/framework/pull/524) +- Create image link helper, fix #434 [`#522`](https://github.com/hydephp/framework/pull/522) +- Create a PageModel contract and helpers to get parsed model collections [`#521`](https://github.com/hydephp/framework/pull/521) +- Apply fixes from StyleCI [`#520`](https://github.com/hydephp/framework/pull/520) +- Update Changelog [`#519`](https://github.com/hydephp/framework/pull/519) +- Merge pull request #522 from hydephp/create-image-file-object [`#434`](https://github.com/hydephp/framework/issues/434) +- Add image path helper, fix #434 [`#434`](https://github.com/hydephp/framework/issues/434) +- Fix #516 Add Composer validation to the test suite [`#516`](https://github.com/hydephp/framework/issues/516) +- Move the static::all() helper to AbstractPage [`c726ad7`](https://github.com/hydephp/framework/commit/c726ad73cf30eff59bc2425f8c35eacbe499f2e4) +- Create MarkdownPost::latest() [`e6d9e4a`](https://github.com/hydephp/framework/commit/e6d9e4a1b2689e58cef44d7109c0594bc3df972f) +- Implement MarkdownPost::all() [`cda2010`](https://github.com/hydephp/framework/commit/cda201052547935d545869d651bc297f45617011) + +#### [v0.33.0-beta](https://github.com/hydephp/framework/compare/v0.32.1-beta...v0.33.0-beta) + +> 4 June 2022 + +- Update Changelog [`#518`](https://github.com/hydephp/framework/pull/518) +- Automatic Changelog Update [`49cbde6`](https://github.com/hydephp/framework/commit/49cbde67588a9e8bfb8694fbb69e42e446ae74fb) + +#### [v0.32.1-beta](https://github.com/hydephp/framework/compare/v0.32.0-beta...v0.32.1-beta) + +> 4 June 2022 + +- Move back hyde/realtime-compiler to hyde/hyde [`#517`](https://github.com/hydephp/framework/pull/517) +- Update Changelog [`#515`](https://github.com/hydephp/framework/pull/515) +- Update composer.lock [`246da42`](https://github.com/hydephp/framework/commit/246da42b693a07175e861bf653763cfa9af42ec2) +- Automatic Changelog Update [`073c80c`](https://github.com/hydephp/framework/commit/073c80c470a9acc035899013d763552b11e04825) +- Update composer.lock [`9e835b6`](https://github.com/hydephp/framework/commit/9e835b6cec2ef64fb81d9378a3bab3c090f460bf) + +#### [v0.32.0-beta](https://github.com/hydephp/framework/compare/v0.31.1-beta...v0.32.0-beta) + +> 4 June 2022 + +- Refactor to use Laravel cache helper instead of custom implementation [`#514`](https://github.com/hydephp/framework/pull/514) +- Apply fixes from StyleCI [`#513`](https://github.com/hydephp/framework/pull/513) +- Improve metadata for featured post images [`#512`](https://github.com/hydephp/framework/pull/512) +- Skip generating auxiliary files in the main built loop when there is no underlying content [`#511`](https://github.com/hydephp/framework/pull/511) +- Fix: #506: Move ext-simplexml in composer.json to suggest as it is not a strict dependency [`#510`](https://github.com/hydephp/framework/pull/510) +- Apply fixes from StyleCI [`#509`](https://github.com/hydephp/framework/pull/509) +- Rewrite Realtime Compiler [`#508`](https://github.com/hydephp/framework/pull/508) +- Apply fixes from StyleCI [`#507`](https://github.com/hydephp/framework/pull/507) +- Update Changelog [`#505`](https://github.com/hydephp/framework/pull/505) +- Fix #496: Missing image "contentUrl" metadata [`#496`](https://github.com/hydephp/framework/issues/496) +- Don't create search files when there are no pages [`#482`](https://github.com/hydephp/framework/issues/482) +- Update Hyde Realtime Compiler to v2.0 [`f917319`](https://github.com/hydephp/framework/commit/f917319149bfce3249f9921b6bc3ecf0a6307f42) +- Delete RELEASE-NOTES-DRAFT.md [`9853526`](https://github.com/hydephp/framework/commit/9853526ec23b8fe7a325126d8e740e354a1b4eb2) +- Remove pre-check as package is always included [`076a1be`](https://github.com/hydephp/framework/commit/076a1bef2ae68117d092b38d9ee8d6f2fef64172) + +#### [v0.31.1-beta](https://github.com/hydephp/framework/compare/v0.31.0-beta...v0.31.1-beta) + +> 3 June 2022 + +- Update Changelog [`#504`](https://github.com/hydephp/framework/pull/504) +- Automatic Changelog Update [`4e09807`](https://github.com/hydephp/framework/commit/4e09807ec0cc6168ad9c38e6ea1521932c8e28e0) + +#### [v0.31.0-beta](https://github.com/hydephp/framework/compare/v0.30.1-beta...v0.31.0-beta) + +> 2 June 2022 + +- Fix #499: Make the search dialog positioning fixed [`#503`](https://github.com/hydephp/framework/pull/503) +- Make documentation pages smarter [`#501`](https://github.com/hydephp/framework/pull/501) +- Apply fixes from StyleCI [`#500`](https://github.com/hydephp/framework/pull/500) +- Link to markdown source files [`#498`](https://github.com/hydephp/framework/pull/498) +- Apply fixes from StyleCI [`#497`](https://github.com/hydephp/framework/pull/497) +- Fix #490 Make heading permalinks visible [`#493`](https://github.com/hydephp/framework/pull/493) +- Apply fixes from StyleCI [`#492`](https://github.com/hydephp/framework/pull/492) +- Add Markdown Post/Preprocessors [`#488`](https://github.com/hydephp/framework/pull/488) +- Apply fixes from StyleCI [`#486`](https://github.com/hydephp/framework/pull/486) +- Update Changelog [`#481`](https://github.com/hydephp/framework/pull/481) +- Merge pull request #503 from hydephp/499-make-the-search-menu-dialog-position-fixed [`#499`](https://github.com/hydephp/framework/issues/499) +- Fix #499: Make the search dialog positioning fixed [`#499`](https://github.com/hydephp/framework/issues/499) +- Merge pull request #493 from hydephp/make-heading-permalinks-visible [`#490`](https://github.com/hydephp/framework/issues/490) +- Fix #490 Make heading permalinks visible [`#490`](https://github.com/hydephp/framework/issues/490) +- Merge unit tests into single feature test [`c455d1c`](https://github.com/hydephp/framework/commit/c455d1c0246d9361fd2115528ce616ea797915ea) +- Use the same static transformation instead of DOM [`bdba273`](https://github.com/hydephp/framework/commit/bdba27386df05a46ca27071601aed1f6f3f00b59) +- Document the edit button feature [`dc0d9d7`](https://github.com/hydephp/framework/commit/dc0d9d750e0b61701557472ebd1ce1b1e556058a) + +#### [v0.30.1-beta](https://github.com/hydephp/framework/compare/v0.30.0-beta...v0.30.1-beta) + +> 31 May 2022 + +- Fix support for outputting documentation pages to root output directory [`#480`](https://github.com/hydephp/framework/pull/480) +- Update Changelog [`#479`](https://github.com/hydephp/framework/pull/479) +- Fix https://github.com/hydephp/framework/issues/462#issuecomment-1142408337 [`#462`](https://github.com/hydephp/framework/issues/462) +- Automatic Changelog Update [`207a847`](https://github.com/hydephp/framework/commit/207a847da909f3b3ef22a839c1d51b8522ed26df) +- Fix bug #462 caused by trailing slash in docs path [`6be5055`](https://github.com/hydephp/framework/commit/6be5055633b4ef9be358fdc82dfcc5fc1aad068b) + +#### [v0.30.0-beta](https://github.com/hydephp/framework/compare/v0.29.5-beta...v0.30.0-beta) + +> 31 May 2022 + +- Add inline Blade support to markdown [`#478`](https://github.com/hydephp/framework/pull/478) +- Apply fixes from StyleCI [`#477`](https://github.com/hydephp/framework/pull/477) +- Update Changelog [`#476`](https://github.com/hydephp/framework/pull/476) +- Create page and document Blade-supported Markdown [`0d7ae0f`](https://github.com/hydephp/framework/commit/0d7ae0f213eba74a61257f9207f184169a36127d) +- Add base tests [`ae4b0dc`](https://github.com/hydephp/framework/commit/ae4b0dc24568483ed2be4330c290839f4382571a) +- Sketch out the service class [`4b88214`](https://github.com/hydephp/framework/commit/4b8821447f7375d2cae20a685b76a8102972ee40) + +#### [v0.29.5-beta](https://github.com/hydephp/framework/compare/v0.29.4-beta...v0.29.5-beta) + +> 31 May 2022 + +- Update Changelog [`#475`](https://github.com/hydephp/framework/pull/475) +- Automatic Changelog Update [`ed9f68a`](https://github.com/hydephp/framework/commit/ed9f68add16ec8f95ea06d261e87646381f30330) +- Bump HydeFront to v1.10 [`0f28947`](https://github.com/hydephp/framework/commit/0f28947f2b197177b1b30626d161408d46f71335) + +#### [v0.29.4-beta](https://github.com/hydephp/framework/compare/v0.29.3-beta...v0.29.4-beta) + +> 30 May 2022 + +- Update Changelog [`#474`](https://github.com/hydephp/framework/pull/474) +- Add color-scheme meta, fix #460 [`#460`](https://github.com/hydephp/framework/issues/460) +- Try to figure out why Codecov is not working [`9d3371c`](https://github.com/hydephp/framework/commit/9d3371cab5606c280261c2f3e209beeff3289f5a) +- Automatic Changelog Update [`aa15090`](https://github.com/hydephp/framework/commit/aa15090a24702b0d01ce175e99421ae97f4f123f) +- Revert codecov changes [`b253969`](https://github.com/hydephp/framework/commit/b2539690fa4d013b06082a162f05816eff99e6bd) + +#### [v0.29.3-beta](https://github.com/hydephp/framework/compare/v0.29.2-beta...v0.29.3-beta) + +> 30 May 2022 + +- Fix Bug #471: og:title and twitter:title should use the page title, and only use config one as fallback [`#473`](https://github.com/hydephp/framework/pull/473) +- Apply fixes from StyleCI [`#472`](https://github.com/hydephp/framework/pull/472) +- Update Changelog [`#470`](https://github.com/hydephp/framework/pull/470) +- Fix bug #471, make title metadata dynamic [`b9ac1c8`](https://github.com/hydephp/framework/commit/b9ac1c8d1fa0c484d2d95fad891c2c3c5c7f039c) +- Automatic Changelog Update [`426beea`](https://github.com/hydephp/framework/commit/426beead29f39d578bb9866afb321df90b0db866) +- Make dynamic meta title use title property instead [`6aaa612`](https://github.com/hydephp/framework/commit/6aaa612b80600ae4ef8136fb779623b66206119b) + +#### [v0.29.2-beta](https://github.com/hydephp/framework/compare/v0.29.1-beta...v0.29.2-beta) + +> 30 May 2022 + +- Add !important to style override [`3e28b1d`](https://github.com/hydephp/framework/commit/3e28b1dcd91802596edf5dfa454fe2178432688f) + +#### [v0.29.1-beta](https://github.com/hydephp/framework/compare/v0.29.0-beta...v0.29.1-beta) + +> 30 May 2022 + +- Update Changelog [`#469`](https://github.com/hydephp/framework/pull/469) +- Automatic Changelog Update [`3133f01`](https://github.com/hydephp/framework/commit/3133f017801ce156b19af274cb6870d8111a8356) +- Use the config defined output path [`927072e`](https://github.com/hydephp/framework/commit/927072e725624d39239845a681e744e2d309694c) +- Update Readme heading to "The Core Framework" [`7a89486`](https://github.com/hydephp/framework/commit/7a89486509ea68071cb5268956dd771766bf327a) + +#### [v0.29.0-beta](https://github.com/hydephp/framework/compare/v0.28.1-beta...v0.29.0-beta) + +> 30 May 2022 + +- Load HydeFront v1.9.x needed for HydeSearch [`#468`](https://github.com/hydephp/framework/pull/468) +- Make the search feature configurable and toggleable [`#467`](https://github.com/hydephp/framework/pull/467) +- Apply fixes from StyleCI [`#466`](https://github.com/hydephp/framework/pull/466) +- Add the HydeSearch frontend integration for documentation pages [`#465`](https://github.com/hydephp/framework/pull/465) +- Apply fixes from StyleCI [`#464`](https://github.com/hydephp/framework/pull/464) +- Apply fixes from StyleCI [`#461`](https://github.com/hydephp/framework/pull/461) +- Create the backend search index generation for documentation pages [`#459`](https://github.com/hydephp/framework/pull/459) +- Apply fixes from StyleCI [`#458`](https://github.com/hydephp/framework/pull/458) +- Apply fixes from StyleCI [`#457`](https://github.com/hydephp/framework/pull/457) +- Bump guzzlehttp/guzzle from 7.4.2 to 7.4.3 [`#456`](https://github.com/hydephp/framework/pull/456) +- Update Changelog [`#455`](https://github.com/hydephp/framework/pull/455) +- Refactor inline styles to HydeFront Sass [`86fff1d`](https://github.com/hydephp/framework/commit/86fff1d9dc7f5d5e5ca171cf79517af0d2fb1639) +- Begin sketching out the class [`ed131bd`](https://github.com/hydephp/framework/commit/ed131bd4196afbcf1684bb999c5a7fe98d1948b8) +- Extract search widget to component [`420f662`](https://github.com/hydephp/framework/commit/420f662a3040cc4c0f3f8e48d6a350686fb02803) + +#### [v0.28.1-beta](https://github.com/hydephp/framework/compare/v0.28.0-beta-pre...v0.28.1-beta) + +> 25 May 2022 + +- Fix #450: Add custom exceptions [`#454`](https://github.com/hydephp/framework/pull/454) +- Apply fixes from StyleCI [`#453`](https://github.com/hydephp/framework/pull/453) +- Apply fixes from StyleCI [`#452`](https://github.com/hydephp/framework/pull/452) +- Update Changelog [`#451`](https://github.com/hydephp/framework/pull/451) +- Refactor author configuration system [`#449`](https://github.com/hydephp/framework/pull/449) +- Apply fixes from StyleCI [`#448`](https://github.com/hydephp/framework/pull/448) +- Update Changelog [`#445`](https://github.com/hydephp/framework/pull/445) +- Merge pull request #454 from hydephp/450-add-custom-exceptions [`#450`](https://github.com/hydephp/framework/issues/450) +- Remove AuthorService [`9f9d64d`](https://github.com/hydephp/framework/commit/9f9d64dc4f232e6c0a695088cd43dc28f8535fc3) +- Clean up code [`f8452b9`](https://github.com/hydephp/framework/commit/f8452b9d697505733f147bf3f59e92abfb307727) +- Create FileConflictException [`02d534c`](https://github.com/hydephp/framework/commit/02d534cd801ed19fac264373685c30b3f6858c34) + +#### [v0.28.0-beta-pre](https://github.com/hydephp/framework/compare/v0.28.0-beta...v0.28.0-beta-pre) + +> 22 May 2022 + +#### [v0.28.0-beta](https://github.com/hydephp/framework/compare/v0.27.12-beta...v0.28.0-beta) + +> 23 May 2022 + +- Refactor author configuration system [`#449`](https://github.com/hydephp/framework/pull/449) +- Apply fixes from StyleCI [`#448`](https://github.com/hydephp/framework/pull/448) +- Update Changelog [`#445`](https://github.com/hydephp/framework/pull/445) +- Refactor configuration to use snake_case for all options, and extract documentation settings to own file [`#444`](https://github.com/hydephp/framework/pull/444) +- Apply fixes from StyleCI [`#443`](https://github.com/hydephp/framework/pull/443) +- Update Changelog [`#441`](https://github.com/hydephp/framework/pull/441) +- Remove AuthorService [`9f9d64d`](https://github.com/hydephp/framework/commit/9f9d64dc4f232e6c0a695088cd43dc28f8535fc3) +- Extract documentation configuration options to docs.php [`92b9ae5`](https://github.com/hydephp/framework/commit/92b9ae5fc4f2c7743206ebcfce48d81e4df7746d) +- Use the snake_case config format [`f578855`](https://github.com/hydephp/framework/commit/f578855047113c3181c9869f1ec9d4d521c3bd62) + +#### [v0.27.12-beta](https://github.com/hydephp/framework/compare/v0.27.11-beta...v0.27.12-beta) + +> 22 May 2022 + +- Code cleanup without affecting functionality [`#440`](https://github.com/hydephp/framework/pull/440) +- Apply fixes from StyleCI [`#439`](https://github.com/hydephp/framework/pull/439) +- Update Changelog [`#438`](https://github.com/hydephp/framework/pull/438) +- Add missing return type declarations [`684b792`](https://github.com/hydephp/framework/commit/684b792796e330c958a312d914057771eb72f2da) +- Automatic Changelog Update [`5705faa`](https://github.com/hydephp/framework/commit/5705faa543b83c6b0d61d3dc4ab4ac8a9b3ac178) +- Add PHPDoc comments with @throws tags [`ae44806`](https://github.com/hydephp/framework/commit/ae44806cb3c23249bc68a39bd1ede6fa0c4e8e56) + +#### [v0.27.11-beta](https://github.com/hydephp/framework/compare/v0.27.10-beta...v0.27.11-beta) + +> 21 May 2022 + +- Fix #429: Add page priorities to sitemap generation [`#437`](https://github.com/hydephp/framework/pull/437) +- Update Changelog [`#436`](https://github.com/hydephp/framework/pull/436) +- Merge pull request #437 from hydephp/add-dynamic-page-priorities-for-sitemap [`#429`](https://github.com/hydephp/framework/issues/429) +- Add page priority support [`0bfbbba`](https://github.com/hydephp/framework/commit/0bfbbba07fd8d1720fe6a693089e62dbc0dc018a) +- Automatic Changelog Update [`1350fab`](https://github.com/hydephp/framework/commit/1350fab302bdfb0d8d9f59f8f0e1d925d4d5ceb4) + +#### [v0.27.10-beta](https://github.com/hydephp/framework/compare/v0.27.9-beta...v0.27.10-beta) + +> 20 May 2022 + +- Improve RSS image handling and feed and sitemap generation processes [`#435`](https://github.com/hydephp/framework/pull/435) +- Apply fixes from StyleCI [`#433`](https://github.com/hydephp/framework/pull/433) +- Update Changelog [`#432`](https://github.com/hydephp/framework/pull/432) +- Create HydeBuildRssFeedCommand.php [`ac4788f`](https://github.com/hydephp/framework/commit/ac4788f987cb517d51a6d0a4fddc5684777c9a0a) +- Create build:sitemap command [`82c73a3`](https://github.com/hydephp/framework/commit/82c73a392350dff171b496220d8d1f70d363102d) +- Fetch information for local images [`a10c1c3`](https://github.com/hydephp/framework/commit/a10c1c361852154e9eb52947b003a65ede09c3ef) + +#### [v0.27.9-beta](https://github.com/hydephp/framework/compare/v0.27.8-beta...v0.27.9-beta) + +> 20 May 2022 + +- Apply fixes from StyleCI [`#431`](https://github.com/hydephp/framework/pull/431) +- Update Changelog [`#428`](https://github.com/hydephp/framework/pull/428) +- Rename and restructure internal hooks [`0562ae3`](https://github.com/hydephp/framework/commit/0562ae3558363afddfeb63a7148f967940ed4966) +- Update test code formatting [`1a9dcaf`](https://github.com/hydephp/framework/commit/1a9dcaf670a9757985013c7c3a3e01fa93f75579) +- Add sitemap link test [`9ba7b10`](https://github.com/hydephp/framework/commit/9ba7b109560881867ee9ba81a5e37bb10b370616) + +#### [v0.27.8-beta](https://github.com/hydephp/framework/compare/v0.27.7-beta...v0.27.8-beta) + +> 19 May 2022 + +- Update Changelog [`#426`](https://github.com/hydephp/framework/pull/426) +- Update Changelog [`#425`](https://github.com/hydephp/framework/pull/425) +- Automatic Changelog Update [`f33f2a4`](https://github.com/hydephp/framework/commit/f33f2a4d2f79c9986bff4b33e905f95831be326a) +- Update the tests [`a80593e`](https://github.com/hydephp/framework/commit/a80593e1ac6fc79c3d78ea2d736c89955e6b6805) +- Automatic Changelog Update [`ab7daeb`](https://github.com/hydephp/framework/commit/ab7daebd3103584c99f9290f1d5c6ba3fa36e801) + +#### [v0.27.7-beta](https://github.com/hydephp/framework/compare/v0.27.6-beta...v0.27.7-beta) + +> 19 May 2022 + +- Normalize the site URL [`a4b9ce7`](https://github.com/hydephp/framework/commit/a4b9ce7a32321e3e67df5aaed477fbfc54c6c524) + +#### [v0.27.6-beta](https://github.com/hydephp/framework/compare/v0.27.5-beta...v0.27.6-beta) + +> 19 May 2022 + +- Update Changelog [`#423`](https://github.com/hydephp/framework/pull/423) +- Add deployment documentation [`4b188f2`](https://github.com/hydephp/framework/commit/4b188f20848e87cd3b3e77af9cdde5b373e2e4d3) +- Merge sections to be more compact [`baadd48`](https://github.com/hydephp/framework/commit/baadd4891d719123720f8bc79a1a82a4837e547e) +- Restructure document flow [`40f4a3d`](https://github.com/hydephp/framework/commit/40f4a3d37b835b40b392f5f72a4ab46563df5042) + +#### [v0.27.5-beta](https://github.com/hydephp/framework/compare/v0.27.4-beta...v0.27.5-beta) + +> 19 May 2022 + +- Fix bug where categorized documentation sidebar items were not sorted [`#422`](https://github.com/hydephp/framework/pull/422) +- Update Changelog [`#421`](https://github.com/hydephp/framework/pull/421) +- Fix #367: Add upcoming documentation files [`#367`](https://github.com/hydephp/framework/issues/367) +- Create building-your-site.md [`6989bd5`](https://github.com/hydephp/framework/commit/6989bd59d33d84cebf3e0ef134f4107d149c6fd5) +- Automatic Changelog Update [`858c1ed`](https://github.com/hydephp/framework/commit/858c1ed3c34f7833e887ffdc21f60003deebd38e) +- Update documentation page orders [`b38c58b`](https://github.com/hydephp/framework/commit/b38c58bba32312d932d1a005b3015b3ce9dd7329) + +#### [v0.27.4-beta](https://github.com/hydephp/framework/compare/v0.27.3-beta...v0.27.4-beta) + +> 19 May 2022 + +- Apply fixes from StyleCI [`#420`](https://github.com/hydephp/framework/pull/420) +- Update Changelog [`#418`](https://github.com/hydephp/framework/pull/418) +- Fix #419: Add meta links to the RSS feed [`#419`](https://github.com/hydephp/framework/issues/419) +- Refactor internal helpers to be public static [`283e5d2`](https://github.com/hydephp/framework/commit/283e5d2154862f114e82f1e5e036924d449e7ebf) +- Automatic Changelog Update [`5ea9c66`](https://github.com/hydephp/framework/commit/5ea9c66b0d5c659602b6e042cd04f040dd1223d9) +- Add page slug for compatibility, fixing bug where Blade pages did not get canonical link tags [`d3ac8e4`](https://github.com/hydephp/framework/commit/d3ac8e492bb01ba538111ba8c7f4dfb48cbc5785) + +#### [v0.27.3-beta](https://github.com/hydephp/framework/compare/v0.27.2-beta...v0.27.3-beta) + +> 19 May 2022 + +- Apply fixes from StyleCI [`#417`](https://github.com/hydephp/framework/pull/417) +- Apply fixes from StyleCI [`#415`](https://github.com/hydephp/framework/pull/415) +- Update Changelog [`#414`](https://github.com/hydephp/framework/pull/414) +- Add unit test for fluent Markdown post helpers [`2a3b90b`](https://github.com/hydephp/framework/commit/2a3b90bbf2ffab9709a49447b9a4aa80cd14ca9e) +- Add Author::getName() unit test [`64616a6`](https://github.com/hydephp/framework/commit/64616a6d24d8335e890bde35c8fafa37ef9bb4ba) +- Change RSS feed default filename to feed.xml [`d545b07`](https://github.com/hydephp/framework/commit/d545b07130cb58c42cb9701b3c2322ac133e617e) + +#### [v0.27.2-beta](https://github.com/hydephp/framework/compare/v0.27.1-beta...v0.27.2-beta) + +> 19 May 2022 + +- Add RSS feed for Markdown blog posts [`#413`](https://github.com/hydephp/framework/pull/413) +- Apply fixes from StyleCI [`#412`](https://github.com/hydephp/framework/pull/412) +- Apply fixes from StyleCI [`#411`](https://github.com/hydephp/framework/pull/411) +- Apply fixes from StyleCI [`#410`](https://github.com/hydephp/framework/pull/410) +- Update Changelog [`#409`](https://github.com/hydephp/framework/pull/409) +- Add the RSSFeedService test [`a21596f`](https://github.com/hydephp/framework/commit/a21596f68792d313c551789f713950a6c2410975) +- Add the initial channel items [`9cb9b30`](https://github.com/hydephp/framework/commit/9cb9b302662de3d1dc80ba0ea09a48c3a53f2e78) +- Update sitemap tests and add rss feed tests [`fe93f5b`](https://github.com/hydephp/framework/commit/fe93f5b7cd1dea1f3bbb5a851b8185e5288f50de) + +#### [v0.27.1-beta](https://github.com/hydephp/framework/compare/v0.27.0-beta...v0.27.1-beta) + +> 18 May 2022 + +- Fix #403: Remove @HydeConfigVersion annotation from config/hyde.php [`#408`](https://github.com/hydephp/framework/pull/408) +- Apply fixes from StyleCI [`#407`](https://github.com/hydephp/framework/pull/407) +- Merge pull request #408 from hydephp/remove-hydeconfigversion-annotation-from-hyde-config [`#403`](https://github.com/hydephp/framework/issues/403) +- Remove HydeConfigVersion annotation [`84b1602`](https://github.com/hydephp/framework/commit/84b1602fc3280ef66637799c8aaa9d9513c3142c) + +#### [v0.27.0-beta](https://github.com/hydephp/framework/compare/v0.26.0-beta...v0.27.0-beta) + +> 18 May 2022 + +- Add sitemap.xml generation [`#404`](https://github.com/hydephp/framework/pull/404) +- Apply fixes from StyleCI [`#406`](https://github.com/hydephp/framework/pull/406) +- Apply fixes from StyleCI [`#402`](https://github.com/hydephp/framework/pull/402) +- Update Changelog [`#401`](https://github.com/hydephp/framework/pull/401) +- Add SitemapService tests [`ce5d8ed`](https://github.com/hydephp/framework/commit/ce5d8ed089546a8262e637d3ce399bf190672ba0) +- Refactor shared code into new helper [`46f41d6`](https://github.com/hydephp/framework/commit/46f41d6848a5562006f4290aa00df221d25d815a) +- Create basic sitemap generator [`1f66928`](https://github.com/hydephp/framework/commit/1f669282d727042df5074f0182bf5e0563d07a91) + +#### [v0.26.0-beta](https://github.com/hydephp/framework/compare/v0.25.0-beta...v0.26.0-beta) + +> 18 May 2022 + +- Fix #398: Remove the deprecated Metadata model [`#400`](https://github.com/hydephp/framework/pull/400) +- Apply fixes from StyleCI [`#399`](https://github.com/hydephp/framework/pull/399) +- Apply fixes from StyleCI [`#397`](https://github.com/hydephp/framework/pull/397) +- Fix #379: Extract menu logo to component [`#396`](https://github.com/hydephp/framework/pull/396) +- Update helper namespaces [`#395`](https://github.com/hydephp/framework/pull/395) +- Fix #385: Move page parsers into models/parsers namespace [`#394`](https://github.com/hydephp/framework/pull/394) +- Remove redundancy and merge Meta and Metadata models #384 [`#390`](https://github.com/hydephp/framework/pull/390) +- Apply fixes from StyleCI [`#393`](https://github.com/hydephp/framework/pull/393) +- Apply fixes from StyleCI [`#392`](https://github.com/hydephp/framework/pull/392) +- Apply fixes from StyleCI [`#391`](https://github.com/hydephp/framework/pull/391) +- Apply fixes from StyleCI [`#389`](https://github.com/hydephp/framework/pull/389) +- Unify the $page property and add a fluent metadata helper [`#388`](https://github.com/hydephp/framework/pull/388) +- Apply fixes from StyleCI [`#387`](https://github.com/hydephp/framework/pull/387) +- Apply fixes from StyleCI [`#386`](https://github.com/hydephp/framework/pull/386) +- Apply fixes from StyleCI [`#383`](https://github.com/hydephp/framework/pull/383) +- Apply fixes from StyleCI [`#381`](https://github.com/hydephp/framework/pull/381) +- Apply fixes from StyleCI [`#380`](https://github.com/hydephp/framework/pull/380) +- Update Changelog [`#378`](https://github.com/hydephp/framework/pull/378) +- Merge pull request #400 from hydephp/398-remove-legacy-metadata-model [`#398`](https://github.com/hydephp/framework/issues/398) +- Merge pull request #396 from hydephp/extract-navigation-menu-logo-to-component-to-make-it-easier-to-customize [`#379`](https://github.com/hydephp/framework/issues/379) +- Fix #379: Extract menu logo to component [`#379`](https://github.com/hydephp/framework/issues/379) [`#379`](https://github.com/hydephp/framework/issues/379) +- Merge pull request #394 from hydephp/385-move-page-parsers-into-a-namespace [`#385`](https://github.com/hydephp/framework/issues/385) +- Fix #385: Move page parsers into a namespace [`#385`](https://github.com/hydephp/framework/issues/385) +- Fix #382: Unify the $page property [`#382`](https://github.com/hydephp/framework/issues/382) +- Fix #375, Add config option to add og:properties [`#375`](https://github.com/hydephp/framework/issues/375) +- Extract metadata helpers to concern [`72b1356`](https://github.com/hydephp/framework/commit/72b1356298ae0537356a88630e144df07fc6adf8) +- Add test for, and improve Meta helper [`15ccd27`](https://github.com/hydephp/framework/commit/15ccd271706ddf38f8011287ed28f04a60cd4076) +- Refactor concern to not be dependent on Metadata model [`b247bb0`](https://github.com/hydephp/framework/commit/b247bb0627dc481c08bb8e47f7c38ec57816154a) + +#### [v0.25.0-beta](https://github.com/hydephp/framework/compare/v0.24.0-beta...v0.25.0-beta) + +> 17 May 2022 + +- Apply fixes from StyleCI [`#374`](https://github.com/hydephp/framework/pull/374) +- Load asset service from the service container [`#373`](https://github.com/hydephp/framework/pull/373) +- Apply fixes from StyleCI [`#372`](https://github.com/hydephp/framework/pull/372) +- Apply fixes from StyleCI [`#371`](https://github.com/hydephp/framework/pull/371) +- Rename --pretty option to --run-prettier to distinguish it better in build command [`#368`](https://github.com/hydephp/framework/pull/368) +- Allow site output directory to be customized [`#362`](https://github.com/hydephp/framework/pull/362) +- Apply fixes from StyleCI [`#366`](https://github.com/hydephp/framework/pull/366) +- Apply fixes from StyleCI [`#365`](https://github.com/hydephp/framework/pull/365) +- Apply fixes from StyleCI [`#364`](https://github.com/hydephp/framework/pull/364) +- Apply fixes from StyleCI [`#359`](https://github.com/hydephp/framework/pull/359) +- Apply fixes from StyleCI [`#360`](https://github.com/hydephp/framework/pull/360) +- Configuration and autodiscovery improvements [`#340`](https://github.com/hydephp/framework/pull/340) +- Apply fixes from StyleCI [`#358`](https://github.com/hydephp/framework/pull/358) +- Add configurable "pretty URLs" [`#354`](https://github.com/hydephp/framework/pull/354) +- Apply fixes from StyleCI [`#357`](https://github.com/hydephp/framework/pull/357) +- Apply fixes from StyleCI [`#356`](https://github.com/hydephp/framework/pull/356) +- Apply fixes from StyleCI [`#355`](https://github.com/hydephp/framework/pull/355) +- Apply fixes from StyleCI [`#352`](https://github.com/hydephp/framework/pull/352) +- Add sidebar config offset, fix #307 [`#348`](https://github.com/hydephp/framework/pull/348) +- Change BuildService to DiscoveryService [`#347`](https://github.com/hydephp/framework/pull/347) +- Apply fixes from StyleCI [`#346`](https://github.com/hydephp/framework/pull/346) +- Apply fixes from StyleCI [`#345`](https://github.com/hydephp/framework/pull/345) +- Apply fixes from StyleCI [`#344`](https://github.com/hydephp/framework/pull/344) +- Apply fixes from StyleCI [`#341`](https://github.com/hydephp/framework/pull/341) +- Apply fixes from StyleCI [`#339`](https://github.com/hydephp/framework/pull/339) +- Update Changelog [`#327`](https://github.com/hydephp/framework/pull/327) +- Fix #361 Rename --pretty option to --run-prettier [`#361`](https://github.com/hydephp/framework/issues/361) +- Fix #350, Use the model path properties [`#350`](https://github.com/hydephp/framework/issues/350) +- Add option for pretty urls fix #330 [`#330`](https://github.com/hydephp/framework/issues/330) +- Rewrite index docs path to pretty url, fix #353 [`#353`](https://github.com/hydephp/framework/issues/353) +- Fix #330, Create helper to make pretty URLs if enabled [`#330`](https://github.com/hydephp/framework/issues/330) +- Merge pull request #348 from hydephp/add-sidebar-priority-offset-for-config-defined-values [`#307`](https://github.com/hydephp/framework/issues/307) +- Add sidebar config offset, fix #307 [`#307`](https://github.com/hydephp/framework/issues/307) +- Fix #343 [`#343`](https://github.com/hydephp/framework/issues/343) +- Restructure the tests [`41bd056`](https://github.com/hydephp/framework/commit/41bd0560fb014e3a042909e3162e2a2da28c0b77) +- Add helpers to make it easier to refactor source paths [`10e145e`](https://github.com/hydephp/framework/commit/10e145ea345d2aca22c81ec15d7af073c5ee803c) +- Utalize the $sourceDirectory property in build services [`9d9cbff`](https://github.com/hydephp/framework/commit/9d9cbff800d1422461dfcee6f3983662c51c5606) + +#### [v0.24.0-beta](https://github.com/hydephp/framework/compare/v0.23.5-beta...v0.24.0-beta) + +> 11 May 2022 + +- Add documentation sidebar category labels, fixes #309 [`#326`](https://github.com/hydephp/framework/pull/326) +- Apply fixes from StyleCI [`#325`](https://github.com/hydephp/framework/pull/325) +- Update Changelog [`#324`](https://github.com/hydephp/framework/pull/324) +- Merge pull request #326 from hydephp/309-add-documentation-sidebar-category-labels [`#309`](https://github.com/hydephp/framework/issues/309) +- Sketch out the files for the category integration [`d6c81bb`](https://github.com/hydephp/framework/commit/d6c81bbcce78f0d72f131f49e1c61716e0cd26d6) +- Implement category creation [`70448b1`](https://github.com/hydephp/framework/commit/70448b14ac6d8be3c8162ec78d12901f7a5c7579) +- Set category of uncategorized items [`9f0feb3`](https://github.com/hydephp/framework/commit/9f0feb364a0fa8be9401a5453d8a1ded4b0ae40a) + +#### [v0.23.5-beta](https://github.com/hydephp/framework/compare/v0.23.4-beta...v0.23.5-beta) + +> 11 May 2022 + +- Apply fixes from StyleCI [`#323`](https://github.com/hydephp/framework/pull/323) +- Add back skip to content button to Lagrafo docs layout, fix #300 [`#322`](https://github.com/hydephp/framework/pull/322) +- Change max prose width of markdown pages to match blog posts, fix #303 [`#321`](https://github.com/hydephp/framework/pull/321) +- Fix #153, bug where config option uses app name instead of Hyde name. [`#320`](https://github.com/hydephp/framework/pull/320) +- Update Changelog [`#319`](https://github.com/hydephp/framework/pull/319) +- Add option to mark site as installed, fix #289 [`#289`](https://github.com/hydephp/framework/issues/289) +- Merge pull request #322 from hydephp/300-add-back-skip-to-content-button-to-lagrafo-docs-layout [`#300`](https://github.com/hydephp/framework/issues/300) +- Add skip to content button docs layout, fix #300 [`#300`](https://github.com/hydephp/framework/issues/300) +- Merge pull request #321 from hydephp/303-change-max-width-of-markdown-pages-to-match-blog-posts [`#303`](https://github.com/hydephp/framework/issues/303) +- Change max width to match blog posts, fix #303 [`#303`](https://github.com/hydephp/framework/issues/303) +- Merge pull request #320 from hydephp/294-fix-bug-where-config-option-uses-app-name-instead-of-hyde-name [`#153`](https://github.com/hydephp/framework/issues/153) +- Automatic Changelog Update [`807b63b`](https://github.com/hydephp/framework/commit/807b63b55c0c669638eb6b10a609d25327807a13) +- #153 Fix bug where config option uses app name instead of Hyde name. [`c90977c`](https://github.com/hydephp/framework/commit/c90977cf942cad214b8ea8218be3d5773d1fc633) +- Update install command for new site name syntax [`0687351`](https://github.com/hydephp/framework/commit/06873511064dd2b5ed2faa6ff1ad87c3210185ea) + +#### [v0.23.4-beta](https://github.com/hydephp/framework/compare/v0.23.3-beta...v0.23.4-beta) + +> 11 May 2022 + +- Refactor post excerpt component to be less reliant on directly using front matter and add view test [`#318`](https://github.com/hydephp/framework/pull/318) +- Apply fixes from StyleCI [`#317`](https://github.com/hydephp/framework/pull/317) +- Formatting: Add newline after console output when running build without API calls, fix #313 [`#316`](https://github.com/hydephp/framework/pull/316) +- Fix #314, add background color fallback to documentation page body [`#315`](https://github.com/hydephp/framework/pull/315) +- Update Changelog [`#312`](https://github.com/hydephp/framework/pull/312) +- Restructure and format component, fix #306 [`#306`](https://github.com/hydephp/framework/issues/306) +- Merge pull request #316 from hydephp/313-formatting-add-newline-after-disabling-external-api-calls-in-build-command [`#313`](https://github.com/hydephp/framework/issues/313) +- Formatting: Add newline after --no-api info, fix #313 [`#313`](https://github.com/hydephp/framework/issues/313) +- Merge pull request #315 from hydephp/314-add-dark-mode-background-to-body-in-documentation-pages-to-prevent-fouc [`#314`](https://github.com/hydephp/framework/issues/314) +- Fix #314, add background color fallback to docs body [`#314`](https://github.com/hydephp/framework/issues/314) +- Implement hidden: true front matter to hide documentation pages from sidebar, fix #310 [`#310`](https://github.com/hydephp/framework/issues/310) +- Create ArticleExcerptViewTest.php [`4a3ecaa`](https://github.com/hydephp/framework/commit/4a3ecaa02134583c36d3b8685fa5005f586f4293) +- Add tests for the fluent date-author string [`30f7f67`](https://github.com/hydephp/framework/commit/30f7f6762c6481c148908c26a5930f6e2daf1d80) +- Automatic Changelog Update [`6f3ef7a`](https://github.com/hydephp/framework/commit/6f3ef7aa044755211102911803eb8f2a5adf6c06) + +#### [v0.23.3-beta](https://github.com/hydephp/framework/compare/v0.23.2-beta...v0.23.3-beta) + +> 10 May 2022 + +- Fix #310, allow documentation pages to be hidden from sidebar using front matter [`#311`](https://github.com/hydephp/framework/pull/311) +- Update Changelog [`#302`](https://github.com/hydephp/framework/pull/302) +- Merge pull request #311 from hydephp/310-implement-hidden-true-front-matter-to-hide-documentation-pages-from-sidebar [`#310`](https://github.com/hydephp/framework/issues/310) +- Fix #310, allow items to be hidden from sidebar with front matter [`#310`](https://github.com/hydephp/framework/issues/310) +- Automatic Changelog Update [`e8a938c`](https://github.com/hydephp/framework/commit/e8a938c7f8b8449a108f6f480309cf210c926702) + +#### [v0.23.2-beta](https://github.com/hydephp/framework/compare/v0.23.1-beta...v0.23.2-beta) + +> 7 May 2022 + +- Refactor documentation sidebar internals [`#299`](https://github.com/hydephp/framework/pull/299) +- Apply fixes from StyleCI [`#298`](https://github.com/hydephp/framework/pull/298) +- Update Changelog [`#297`](https://github.com/hydephp/framework/pull/297) +- Create feature test for the new sidebar service [`0adf948`](https://github.com/hydephp/framework/commit/0adf94889c36e0b77fb63018221b16c7f1fc8374) +- Remove deprecated action [`063a85a`](https://github.com/hydephp/framework/commit/063a85aa8979fa5780ba5622c9d9f395c2c159b3) +- Create the sidebar models [`fbcae7c`](https://github.com/hydephp/framework/commit/fbcae7cacd100267440b362a97f97d7bbdee09a9) + +#### [v0.23.1-beta](https://github.com/hydephp/framework/compare/v0.23.0-beta...v0.23.1-beta) + +> 6 May 2022 + +- Apply fixes from StyleCI [`#296`](https://github.com/hydephp/framework/pull/296) +- Update Changelog [`#295`](https://github.com/hydephp/framework/pull/295) +- Add the test helper files [`3cd5a56`](https://github.com/hydephp/framework/commit/3cd5a56aec24fde17bc1a40c6760d6fc24db3113) +- Test description has warning for out of date config [`a90c0b1`](https://github.com/hydephp/framework/commit/a90c0b17663683737cea8fa75dd3d3d39e743f66) +- Delete .run directory [`8cd71fc`](https://github.com/hydephp/framework/commit/8cd71fc4f98efb514c9995a665e5f47f839fa940) + +#### [v0.23.0-beta](https://github.com/hydephp/framework/compare/v0.22.0-beta...v0.23.0-beta) + +> 6 May 2022 + +- Apply fixes from StyleCI [`#293`](https://github.com/hydephp/framework/pull/293) +- Refactor docs layout to use Lagrafo instead of Laradocgen [`#292`](https://github.com/hydephp/framework/pull/292) +- Update Changelog [`#288`](https://github.com/hydephp/framework/pull/288) +- Port lagrafo (wip) [`6ca2309`](https://github.com/hydephp/framework/commit/6ca230964211c79fe19df5954a65ad846500ba5e) +- Move all head tags into blade component [`3093ebf`](https://github.com/hydephp/framework/commit/3093ebf65556e185649a40fd8459caa3fa250d7d) +- Use the Hyde layout [`e09e301`](https://github.com/hydephp/framework/commit/e09e301dba196f6d3336a3f9cf8a265c8939af6c) + +#### [v0.22.0-beta](https://github.com/hydephp/framework/compare/v0.21.6-beta...v0.22.0-beta) + +> 5 May 2022 + +- Update HydeFront version to v1.5.x [`#287`](https://github.com/hydephp/framework/pull/287) +- Refactor script interactions [`#286`](https://github.com/hydephp/framework/pull/286) +- Apply fixes from StyleCI [`#285`](https://github.com/hydephp/framework/pull/285) +- Apply fixes from StyleCI [`#284`](https://github.com/hydephp/framework/pull/284) +- Update Changelog [`#281`](https://github.com/hydephp/framework/pull/281) +- Hide the install command once it has been run, fix #280 [`#280`](https://github.com/hydephp/framework/issues/280) +- Hide the install command once it has been run, fix #280 [`#280`](https://github.com/hydephp/framework/issues/280) +- Automatic Changelog Update [`b381382`](https://github.com/hydephp/framework/commit/b3813826a3d8dda92d0f55b80d460b2c3b4e3e7a) +- Replace onclick with element IDs [`e97d545`](https://github.com/hydephp/framework/commit/e97d5457117e4980425d12fea97bb0dc81eae904) +- Move dark mode switch [`9f6fdf8`](https://github.com/hydephp/framework/commit/9f6fdf83561f4f4e1f8d2e5d4b44e0a923963c94) + +#### [v0.21.6-beta](https://github.com/hydephp/framework/compare/v0.21.5-beta...v0.21.6-beta) + +> 4 May 2022 + +- Create installer command, fix #149 [`#279`](https://github.com/hydephp/framework/pull/279) +- Apply fixes from StyleCI [`#278`](https://github.com/hydephp/framework/pull/278) +- Apply fixes from StyleCI [`#277`](https://github.com/hydephp/framework/pull/277) +- Update Changelog [`#276`](https://github.com/hydephp/framework/pull/276) +- Merge pull request #279 from hydephp/149-create-installer-command [`#149`](https://github.com/hydephp/framework/issues/149) +- Create Install command that can publish a homepage [`b890eb7`](https://github.com/hydephp/framework/commit/b890eb790fddc7ad8e23785b3677e304343b6616) +- Use installer to set the site name in config [`3f0c843`](https://github.com/hydephp/framework/commit/3f0c843955b8dbfa0cc14879771c50397670cae0) +- Use installer to set the site URL in config [`d5f56ac`](https://github.com/hydephp/framework/commit/d5f56ac20d82eb362363b382695c016157f66e42) + +#### [v0.21.5-beta](https://github.com/hydephp/framework/compare/v0.21.4-beta...v0.21.5-beta) + +> 3 May 2022 + +- Update Changelog [`#275`](https://github.com/hydephp/framework/pull/275) +- Update the test to fix updated exception output and remove comments [`cd5a70d`](https://github.com/hydephp/framework/commit/cd5a70d3f8a7b9cf0d97e584191d35ebc642cf5a) +- Automatic Changelog Update [`11c5983`](https://github.com/hydephp/framework/commit/11c5983a916ce170155b547939f993a4c0fcb135) + +#### [v0.21.4-beta](https://github.com/hydephp/framework/compare/v0.21.3-beta...v0.21.4-beta) + +> 3 May 2022 + +- Update Changelog [`#274`](https://github.com/hydephp/framework/pull/274) +- Fix #231 [`#231`](https://github.com/hydephp/framework/issues/231) +- Automatic Changelog Update [`244e6b2`](https://github.com/hydephp/framework/commit/244e6b2e5e02870597e3c3432b22b321bbdda812) + +#### [v0.21.3-beta](https://github.com/hydephp/framework/compare/v0.21.2-beta...v0.21.3-beta) + +> 3 May 2022 + +- Allow documentation pages to be scaffolded using the make:page command [`#273`](https://github.com/hydephp/framework/pull/273) +- Apply fixes from StyleCI [`#272`](https://github.com/hydephp/framework/pull/272) +- Update Changelog [`#271`](https://github.com/hydephp/framework/pull/271) +- Allow documentation pages to be scaffolded using the command [`7bbe012`](https://github.com/hydephp/framework/commit/7bbe0123f0e7b609954ca8e52216d19453c96f1a) +- Automatic Changelog Update [`3392504`](https://github.com/hydephp/framework/commit/3392504c5d8a2609cbaa4c106e7e5e9d077075cf) + +#### [v0.21.2-beta](https://github.com/hydephp/framework/compare/v0.21.1-beta...v0.21.2-beta) + +> 3 May 2022 + +- Send a non-intrusive warning when the config file is out of date [`#270`](https://github.com/hydephp/framework/pull/270) +- Apply fixes from StyleCI [`#269`](https://github.com/hydephp/framework/pull/269) +- Apply fixes from StyleCI [`#268`](https://github.com/hydephp/framework/pull/268) +- Apply fixes from StyleCI [`#267`](https://github.com/hydephp/framework/pull/267) +- Update Changelog [`#266`](https://github.com/hydephp/framework/pull/266) +- Create crude action to check if a config file is up to date [`e31210f`](https://github.com/hydephp/framework/commit/e31210f055dea4ca6d76750b9b2ad24c61c05850) +- Create FileCacheServiceTest [`d9141cc`](https://github.com/hydephp/framework/commit/d9141cca4125c055f927c53edf7bf2b7bde9c9d0) +- Add the test [`ee4a64d`](https://github.com/hydephp/framework/commit/ee4a64d9a22314339b002bbd856b2f79c08bffea) + +#### [v0.21.1-beta](https://github.com/hydephp/framework/compare/v0.21.0-beta...v0.21.1-beta) + +> 3 May 2022 + +- Create filecache at runtime instead of relying on a JSON file that needs to be up to date [`#265`](https://github.com/hydephp/framework/pull/265) +- Apply fixes from StyleCI [`#264`](https://github.com/hydephp/framework/pull/264) +- Update Changelog [`#263`](https://github.com/hydephp/framework/pull/263) +- Create the filecache at runtime, resolves #243, #246 [`#243`](https://github.com/hydephp/framework/issues/243) +- Remove deprecated filecache store and generator [`7a1eb32`](https://github.com/hydephp/framework/commit/7a1eb32aae22f749611ed95bc6b2fb1fce36bd20) +- Remove "Update Filecache" workflow [`81564c0`](https://github.com/hydephp/framework/commit/81564c0d19ca6d622a4949830e2007ed10731e99) +- Remove legacy try/catch [`34733dd`](https://github.com/hydephp/framework/commit/34733ddfb5a53463688ae20f1357f09b8aec33f2) + +#### [v0.21.0-beta](https://github.com/hydephp/framework/compare/v0.20.0-beta...v0.21.0-beta) + +> 3 May 2022 + +- Always empty the _site directory when running the static site build command [`#262`](https://github.com/hydephp/framework/pull/262) +- Update Changelog [`#261`](https://github.com/hydephp/framework/pull/261) +- Always purge output directory when running builder [`a86ad7d`](https://github.com/hydephp/framework/commit/a86ad7d56cbe42bc4541224d951cdf349b5a84ed) +- Automatic Changelog Update [`43fa595`](https://github.com/hydephp/framework/commit/43fa595d9204d46b8900998f9e0cf734fedc3361) + +#### [v0.20.0-beta](https://github.com/hydephp/framework/compare/v0.19.0-beta...v0.20.0-beta) + +> 2 May 2022 + +- Apply fixes from StyleCI [`#260`](https://github.com/hydephp/framework/pull/260) +- Update Filecache [`#258`](https://github.com/hydephp/framework/pull/258) +- Remove HydeFront from being bundled as a subrepo [`#257`](https://github.com/hydephp/framework/pull/257) +- Apply fixes from StyleCI [`#256`](https://github.com/hydephp/framework/pull/256) +- Change the action used to create pull requests [`#255`](https://github.com/hydephp/framework/pull/255) +- Exclude files starting with an underscore from being compiled into pages, fix #220 [`#254`](https://github.com/hydephp/framework/pull/254) +- Apply fixes from StyleCI [`#251`](https://github.com/hydephp/framework/pull/251) +- Create .gitattributes, fixes #223 [`#250`](https://github.com/hydephp/framework/pull/250) +- Deprecate filecache.json and related services [`#248`](https://github.com/hydephp/framework/pull/248) +- Apply fixes from StyleCI [`#247`](https://github.com/hydephp/framework/pull/247) +- Allow documentation sidebar header name to be changed [`#245`](https://github.com/hydephp/framework/pull/245) +- Apply fixes from StyleCI [`#244`](https://github.com/hydephp/framework/pull/244) +- Update Filecache [`#242`](https://github.com/hydephp/framework/pull/242) +- Fix bugs in article and excerpts not fluently constructing descriptions [`#241`](https://github.com/hydephp/framework/pull/241) +- Apply fixes from StyleCI [`#239`](https://github.com/hydephp/framework/pull/239) +- Handle undefined array key title in article-excerpt.blade.php [`#238`](https://github.com/hydephp/framework/pull/238) +- Apply fixes from StyleCI [`#237`](https://github.com/hydephp/framework/pull/237) +- Fix test matrix not fetching proper branch on PRs [`#235`](https://github.com/hydephp/framework/pull/235) +- Fix sidebar ordering bug by using null coalescing operator instead of elvis operator [`#234`](https://github.com/hydephp/framework/pull/234) +- Update Changelog [`#227`](https://github.com/hydephp/framework/pull/227) +- Add unit test for hasDarkmode, fix #259 [`#259`](https://github.com/hydephp/framework/issues/259) +- Add the test, resolves #259 [`#259`](https://github.com/hydephp/framework/issues/259) +- Merge pull request #254 from hydephp/220-exclude-files-starting-with-an-_underscore-from-being-compiled-into-pages [`#220`](https://github.com/hydephp/framework/issues/220) +- Merge pull request #250 from hydephp/add-gitattributes [`#223`](https://github.com/hydephp/framework/issues/223) +- Create .gitattributes, fixes #223 [`#223`](https://github.com/hydephp/framework/issues/223) +- Make category nullable, fixes #230 [`#230`](https://github.com/hydephp/framework/issues/230) +- Fix #240 [`#240`](https://github.com/hydephp/framework/issues/240) +- Handle undefined array key, fixes #229 [`#229`](https://github.com/hydephp/framework/issues/229) +- Remove the HydeFront subrepo [`d406202`](https://github.com/hydephp/framework/commit/d406202d5f24d0cb543ac02fd2b9dc980c86d966) +- Add test to ensure that post front matter can be omitted [`875c6d4`](https://github.com/hydephp/framework/commit/875c6d46b822a7e5d02b1f281ca00189a222d06b) +- Exclude files starting with an _underscore from being discovered [`0dcdcb6`](https://github.com/hydephp/framework/commit/0dcdcb6a35969094533429345a0108915db388f4) + +#### [v0.19.0-beta](https://github.com/hydephp/framework/compare/v0.18.0-beta...v0.19.0-beta) + +> 1 May 2022 + +- Update Filecache [`#226`](https://github.com/hydephp/framework/pull/226) +- Add config option to disable dark mode [`#225`](https://github.com/hydephp/framework/pull/225) +- Apply fixes from StyleCI [`#224`](https://github.com/hydephp/framework/pull/224) +- Update Filecache [`#222`](https://github.com/hydephp/framework/pull/222) +- Refactor assets managing, allowing for Laravel Mix, removing CDN support for Tailwind [`#221`](https://github.com/hydephp/framework/pull/221) +- Apply fixes from StyleCI [`#219`](https://github.com/hydephp/framework/pull/219) +- Apply fixes from StyleCI [`#212`](https://github.com/hydephp/framework/pull/212) +- Apply fixes from StyleCI [`#210`](https://github.com/hydephp/framework/pull/210) +- Apply fixes from StyleCI [`#209`](https://github.com/hydephp/framework/pull/209) +- Apply fixes from StyleCI [`#208`](https://github.com/hydephp/framework/pull/208) +- Apply fixes from StyleCI [`#206`](https://github.com/hydephp/framework/pull/206) +- Apply fixes from StyleCI [`#205`](https://github.com/hydephp/framework/pull/205) +- Apply fixes from StyleCI [`#204`](https://github.com/hydephp/framework/pull/204) +- Update Changelog [`#202`](https://github.com/hydephp/framework/pull/202) +- Fix #211 [`#211`](https://github.com/hydephp/framework/issues/211) +- Add test and clean up docs for HasMetadata [`976cb6c`](https://github.com/hydephp/framework/commit/976cb6c39c2bc7fffcbe160987fa8ba08146f9b0) +- Revert "Update update-filecache.yml" [`abc21e7`](https://github.com/hydephp/framework/commit/abc21e7fcf07d28dc09b99afdafd2764c131936c) +- Update update-filecache.yml [`c25196a`](https://github.com/hydephp/framework/commit/c25196aebb77e8f052a604681523b54f3fc978b7) + +#### [v0.18.0-beta](https://github.com/hydephp/framework/compare/v0.17.0-beta...v0.18.0-beta) + +> 29 April 2022 + +- Update Filecache [`#201`](https://github.com/hydephp/framework/pull/201) +- Update Filecache [`#199`](https://github.com/hydephp/framework/pull/199) +- Apply fixes from StyleCI [`#198`](https://github.com/hydephp/framework/pull/198) +- Update Filecache [`#197`](https://github.com/hydephp/framework/pull/197) +- Change priority of stylesheets [`#195`](https://github.com/hydephp/framework/pull/195) +- Update Filecache [`#194`](https://github.com/hydephp/framework/pull/194) +- Apply fixes from StyleCI [`#193`](https://github.com/hydephp/framework/pull/193) +- Update Changelog [`#192`](https://github.com/hydephp/framework/pull/192) +- Switch jsDelivr source to NPM, fix #200 [`#200`](https://github.com/hydephp/framework/issues/200) +- Update dependencies [`b505726`](https://github.com/hydephp/framework/commit/b5057268abd0a9b0aa128cc169e606d1a7a4ebfb) +- Switch to using TypeScript [`6fa9e6c`](https://github.com/hydephp/framework/commit/6fa9e6c4a762f16eac328648d9ad15dc977e4097) +- Create service class to help with #182 [`fb0033c`](https://github.com/hydephp/framework/commit/fb0033c4a9da66e7ee6dcdd9b8a137fe37c82a2f) + +#### [v0.17.0-beta](https://github.com/hydephp/framework/compare/v0.16.1-beta...v0.17.0-beta) + +> 28 April 2022 + +- Add the code reports workflow [`#191`](https://github.com/hydephp/framework/pull/191) +- Move test suite actions to framework [`#190`](https://github.com/hydephp/framework/pull/190) +- Merge with master [`#189`](https://github.com/hydephp/framework/pull/189) +- Add matrix tests [`#188`](https://github.com/hydephp/framework/pull/188) +- Move part one of the test suite [`#187`](https://github.com/hydephp/framework/pull/187) +- Move Framework tests from Hyde/Hyde to the Hyde/Framework package [`#185`](https://github.com/hydephp/framework/pull/185) +- Apply fixes from StyleCI [`#184`](https://github.com/hydephp/framework/pull/184) +- Update Changelog [`#180`](https://github.com/hydephp/framework/pull/180) +- Move tests from Hyde to Framework [`22ca673`](https://github.com/hydephp/framework/commit/22ca6731a489b576f578186cd777df4bda9e52d0) +- Format YAML [`e6da9ad`](https://github.com/hydephp/framework/commit/e6da9ada1f83c3e2540dec9f719ce59f2169bcf0) +- Add the workflow [`b20cbd6`](https://github.com/hydephp/framework/commit/b20cbd6c9341c5f0666fdda25ebb472bc512654a) + +#### [v0.16.1-beta](https://github.com/hydephp/framework/compare/v0.16.0-beta...v0.16.1-beta) + +> 28 April 2022 + +- Apply fixes from StyleCI [`#179`](https://github.com/hydephp/framework/pull/179) +- Update Changelog [`#176`](https://github.com/hydephp/framework/pull/176) +- Manage asset logic in service class [`c72905f`](https://github.com/hydephp/framework/commit/c72905fcbe8bfd748ec84536e836e8fe154230ec) +- Automatic Changelog Update [`32348a4`](https://github.com/hydephp/framework/commit/32348a4ecb97be689db312269218f883eb971e01) + +#### [v0.16.0-beta](https://github.com/hydephp/framework/compare/v0.15.0-beta...v0.16.0-beta) + +> 27 April 2022 + +- Refactor internal codebase by sorting traits into relevant namespaces [`#175`](https://github.com/hydephp/framework/pull/175) +- Apply fixes from StyleCI [`#174`](https://github.com/hydephp/framework/pull/174) +- Apply fixes from StyleCI [`#173`](https://github.com/hydephp/framework/pull/173) +- Update Changelog [`#172`](https://github.com/hydephp/framework/pull/172) +- Refactor: Move Hyde facade methods to traits [`9b5e4ca`](https://github.com/hydephp/framework/commit/9b5e4ca31a21a858c26c712f73021504ab99b019) +- Refactor: Update namespaces [`96c73aa`](https://github.com/hydephp/framework/commit/96c73aa01946e5f6b862dbf66ffd974d65a3b97f) +- Docs: Remove PHPDocs [`ef2f446`](https://github.com/hydephp/framework/commit/ef2f44604e61e109dcf6d03e96a4ab20cbce8b81) + +#### [v0.15.0-beta](https://github.com/hydephp/framework/compare/v0.14.0-beta...v0.15.0-beta) + +> 27 April 2022 + +- Apply fixes from StyleCI [`#171`](https://github.com/hydephp/framework/pull/171) +- Update Filecache [`#170`](https://github.com/hydephp/framework/pull/170) +- Update Changelog [`#167`](https://github.com/hydephp/framework/pull/167) +- Merge HydeFront v1.3.1 [`727c8f3`](https://github.com/hydephp/framework/commit/727c8f3b96f595b6b8a13ba7427106765583ce4c) +- Remove asset publishing commands [`0f49d16`](https://github.com/hydephp/framework/commit/0f49d16105d211df7990ec6f75c042c4bf530071) +- Rework internals, loading styles from CDN [`c5283c0`](https://github.com/hydephp/framework/commit/c5283c011b078a28117477a201ac56a1179dcf1b) + +#### [v0.14.0-beta](https://github.com/hydephp/framework/compare/v0.13.0-beta...v0.14.0-beta) + +> 21 April 2022 + +- Apply fixes from StyleCI [`#166`](https://github.com/hydephp/framework/pull/166) +- Apply fixes from StyleCI [`#165`](https://github.com/hydephp/framework/pull/165) +- Apply fixes from StyleCI [`#163`](https://github.com/hydephp/framework/pull/163) +- Apply fixes from StyleCI [`#162`](https://github.com/hydephp/framework/pull/162) +- Update Filecache [`#154`](https://github.com/hydephp/framework/pull/154) +- Change update:resources command signature to update:assets [`#153`](https://github.com/hydephp/framework/pull/153) +- Update Filecache [`#152`](https://github.com/hydephp/framework/pull/152) +- Change resources/frontend to resources/assets [`#151`](https://github.com/hydephp/framework/pull/151) +- Update Filecache [`#148`](https://github.com/hydephp/framework/pull/148) +- Update Filecache [`#147`](https://github.com/hydephp/framework/pull/147) +- Overhaul the Markdown Converter Service to make it easier to customize and extend [`#146`](https://github.com/hydephp/framework/pull/146) +- Apply fixes from StyleCI [`#145`](https://github.com/hydephp/framework/pull/145) +- Apply fixes from StyleCI [`#144`](https://github.com/hydephp/framework/pull/144) +- Update Changelog [`#142`](https://github.com/hydephp/framework/pull/142) +- Refactor to fix https://github.com/hydephp/framework/issues/161 [`#161`](https://github.com/hydephp/framework/issues/161) +- Fix https://github.com/hydephp/framework/issues/156 [`#156`](https://github.com/hydephp/framework/issues/156) +- Move frontend files to resources/assets [`e850367`](https://github.com/hydephp/framework/commit/e85036765df5ce1398da370c50b489bd72bef797) +- Add back asset files [`bd218df`](https://github.com/hydephp/framework/commit/bd218df813c8f1496edc09500016bb21be5164b5) +- Merge with Hydefront [`8b477de`](https://github.com/hydephp/framework/commit/8b477de5793194bb9e5c4c39dee762b0f7934930) + +#### [v0.13.0-beta](https://github.com/hydephp/framework/compare/v0.12.0-beta...v0.13.0-beta) + +> 20 April 2022 + +- Update Filecache [`#141`](https://github.com/hydephp/framework/pull/141) +- Add table of contents to the documentation page sidebar [`#140`](https://github.com/hydephp/framework/pull/140) +- Apply fixes from StyleCI [`#139`](https://github.com/hydephp/framework/pull/139) +- Update Changelog [`#136`](https://github.com/hydephp/framework/pull/136) +- Add the table of contents to the frontend [`f728810`](https://github.com/hydephp/framework/commit/f728810ff34cb6a5b9f88552f5ca58b27d61e0dc) +- Add the table of contents generation [`2c4c1b9`](https://github.com/hydephp/framework/commit/2c4c1b9a7a45d527a876474af4c692bdeec1b502) +- Allow table of contents to be disabled in config [`fc9cba1`](https://github.com/hydephp/framework/commit/fc9cba16e92baf584c360e0b6a230a7e99c605e9) + +#### [v0.12.0-beta](https://github.com/hydephp/framework/compare/v0.11.0-beta...v0.12.0-beta) + +> 19 April 2022 + +- Update Filecache [`#135`](https://github.com/hydephp/framework/pull/135) +- Update Filecache [`#134`](https://github.com/hydephp/framework/pull/134) +- Allow author array data to be added in front matter [`#133`](https://github.com/hydephp/framework/pull/133) +- Apply fixes from StyleCI [`#132`](https://github.com/hydephp/framework/pull/132) +- Strip front matter from documentation pages [`#130`](https://github.com/hydephp/framework/pull/130) +- Update Changelog [`#127`](https://github.com/hydephp/framework/pull/127) +- Add trait to handle Authors in the data layer [`62f3793`](https://github.com/hydephp/framework/commit/62f3793138a108478a72e8e8176c8ca0c680be20) +- Update the views to move logic to data layer [`2ebc62c`](https://github.com/hydephp/framework/commit/2ebc62c0927ee13e1a01395e59a2316b0f826427) +- Parse the documentation pages using the fileservice [`041bf98`](https://github.com/hydephp/framework/commit/041bf98d8b20b6874cfd8c2edc7fa43bb88d2844) + +#### [v0.11.0-beta](https://github.com/hydephp/framework/compare/v0.10.0-beta...v0.11.0-beta) + +> 17 April 2022 + +- Apply fixes from StyleCI [`#126`](https://github.com/hydephp/framework/pull/126) +- Update Changelog [`#125`](https://github.com/hydephp/framework/pull/125) +- Add command for the new realtime compiler [`9be80eb`](https://github.com/hydephp/framework/commit/9be80eb34ed1415654465d4cd1b485d17086f59d) +- Automatic Changelog Update [`a1c9ce0`](https://github.com/hydephp/framework/commit/a1c9ce0dc39a0b25c670e92d894285a009fbbdd8) +- Allow the host and port to be specified [`e54a394`](https://github.com/hydephp/framework/commit/e54a394665213d712a6ce30cec98a84045d42738) + +#### [v0.10.0-beta](https://github.com/hydephp/framework/compare/v0.9.0-beta...v0.10.0-beta) + +> 12 April 2022 + +- Update Filecache [`#124`](https://github.com/hydephp/framework/pull/124) +- Update Filecache [`#122`](https://github.com/hydephp/framework/pull/122) +- Apply fixes from StyleCI [`#121`](https://github.com/hydephp/framework/pull/121) +- Update Filecache [`#120`](https://github.com/hydephp/framework/pull/120) +- Apply fixes from StyleCI [`#119`](https://github.com/hydephp/framework/pull/119) +- Update Filecache [`#118`](https://github.com/hydephp/framework/pull/118) +- Update Filecache [`#117`](https://github.com/hydephp/framework/pull/117) +- Add darkmode support and refactor blade components [`#116`](https://github.com/hydephp/framework/pull/116) +- Add skip to content link [`#113`](https://github.com/hydephp/framework/pull/113) +- Update the welcome page to be more accessible [`#112`](https://github.com/hydephp/framework/pull/112) +- Apply fixes from StyleCI [`#110`](https://github.com/hydephp/framework/pull/110) +- Remove the deprecated and unused service provider [`#108`](https://github.com/hydephp/framework/pull/108) +- Update Blade components, internal data handling, add a11y features [`#102`](https://github.com/hydephp/framework/pull/102) +- Apply fixes from StyleCI [`#107`](https://github.com/hydephp/framework/pull/107) +- Apply fixes from StyleCI [`#101`](https://github.com/hydephp/framework/pull/101) +- Refactor tests [`#98`](https://github.com/hydephp/framework/pull/98) +- Deprecate internal abstract class HydeBasePublishingCommand [`#97`](https://github.com/hydephp/framework/pull/97) +- Apply fixes from StyleCI [`#96`](https://github.com/hydephp/framework/pull/96) +- Update and simplify the command and rename signature from publish:configs to update:configs, making overwriting files the default. [`#95`](https://github.com/hydephp/framework/pull/95) +- Apply fixes from StyleCI [`#94`](https://github.com/hydephp/framework/pull/94) +- Update Changelog [`#92`](https://github.com/hydephp/framework/pull/92) +- Change blade source directory to _pages [`#90`](https://github.com/hydephp/framework/pull/90) +- Apply fixes from StyleCI [`#89`](https://github.com/hydephp/framework/pull/89) +- Fix line ending sequence issue in checksums [`#86`](https://github.com/hydephp/framework/pull/86) +- Apply fixes from StyleCI [`#87`](https://github.com/hydephp/framework/pull/87) +- Refactor internal file handling logic to be more intelligent to provide a safer, more intuitive, user experience [`#84`](https://github.com/hydephp/framework/pull/84) +- Apply fixes from StyleCI [`#83`](https://github.com/hydephp/framework/pull/83) +- Apply fixes from StyleCI [`#82`](https://github.com/hydephp/framework/pull/82) +- Fix improper article ID usage - remember to re-publish styles [`#81`](https://github.com/hydephp/framework/pull/81) +- Fix #63, update component to show formatted dates [`#80`](https://github.com/hydephp/framework/pull/80) +- Update Spatie YAML Front Matter Package to fix #36 [`#79`](https://github.com/hydephp/framework/pull/79) +- Apply fixes from StyleCI [`#78`](https://github.com/hydephp/framework/pull/78) +- Add base styles to documentation layout [`#77`](https://github.com/hydephp/framework/pull/77) +- Refactor code to extend base classes and remove shared code [`#74`](https://github.com/hydephp/framework/pull/74) +- Apply fixes from StyleCI [`#75`](https://github.com/hydephp/framework/pull/75) +- Refactor the backend structure of the static page builder command process [`#72`](https://github.com/hydephp/framework/pull/72) +- Apply fixes from StyleCI [`#73`](https://github.com/hydephp/framework/pull/73) +- Supply `_media` as the path argument in the `hyde:rebuild` command to copy all media files. [`#71`](https://github.com/hydephp/framework/pull/71) +- Add more relevant targets for the skip to content link, fix #123 [`#123`](https://github.com/hydephp/framework/issues/123) +- Add the image model, fix #100 [`#100`](https://github.com/hydephp/framework/issues/100) +- Merge pull request #80 from hydephp/63-fix-up-the-post-date-component-to-show-the-readable-name [`#63`](https://github.com/hydephp/framework/issues/63) +- Fix #63, update component to show formatted dates [`#63`](https://github.com/hydephp/framework/issues/63) +- Merge pull request #79 from hydephp/36-spatie-yaml-front-matter-package-not-properly-handling-markdown-documents-with-markdown-inside [`#36`](https://github.com/hydephp/framework/issues/36) +- Compress CSS, 5.48 KB to 3.37 KB (38.56%) [`d7f2054`](https://github.com/hydephp/framework/commit/d7f2054420f6c8a6ac786a705e2a0fc472bc4b92) +- Update dependencies [`f851978`](https://github.com/hydephp/framework/commit/f851978e0e2bf733a933504880333aebfd052fb1) +- Remove the deprecated and now unused base command [`0f137c8`](https://github.com/hydephp/framework/commit/0f137c8303cc6041011b82f80a67906b2bccfc8a) + +#### [v0.9.0-beta](https://github.com/hydephp/framework/compare/v0.8.1-beta...v0.9.0-beta) + +> 7 April 2022 + +- Rework how frontend assets (stylesheets and main script) are handled [`#69`](https://github.com/hydephp/framework/pull/69) +- Apply fixes from StyleCI [`#70`](https://github.com/hydephp/framework/pull/70) +- Apply fixes from StyleCI [`#68`](https://github.com/hydephp/framework/pull/68) +- Move the resource files [`7c70467`](https://github.com/hydephp/framework/commit/7c70467499c429d99813e095f0e775bf74ff0c68) +- Add the update frontend resources command [`551df0a`](https://github.com/hydephp/framework/commit/551df0a3813963aaecad3b11e5d7c1f15248241a) +- Add the action to publish the frontend resources [`e2c82fb`](https://github.com/hydephp/framework/commit/e2c82fbc6dda89c6144c949d92f1d8b147f4ab69) + +#### [v0.8.1-beta](https://github.com/hydephp/framework/compare/v0.8.0-beta...v0.8.1-beta) + +> 3 April 2022 + +- Apply fixes from StyleCI [`#61`](https://github.com/hydephp/framework/pull/61) +- Add --no-api option to disable Torchlight at runtime, fix #53 [`#53`](https://github.com/hydephp/framework/issues/53) +- Add Changelog.md [`fe2fdf8`](https://github.com/hydephp/framework/commit/fe2fdf8e4e3a43cfcde766ac84bbcbb2c55d4890) +- Create CODE_OF_CONDUCT.md [`9361d1d`](https://github.com/hydephp/framework/commit/9361d1df2615048f01448ab26ef09e5b2de75eb0) +- Create CONTRIBUTING.md [`a581146`](https://github.com/hydephp/framework/commit/a5811466c4ee67ea5ab4b819959ab80984da6770) + +#### [v0.8.0-beta](https://github.com/hydephp/framework/compare/v0.7.5-alpha...v0.8.0-beta) + +> 2 April 2022 + +- Rewrite main navigation menu [`#60`](https://github.com/hydephp/framework/pull/60) +- Fix #59, unify sidebar elements [`#59`](https://github.com/hydephp/framework/issues/59) +- Unify the navigation menu [`f0e6cfc`](https://github.com/hydephp/framework/commit/f0e6cfc28eae7c0325a89ab0cce4ab67329e3be5) +- Add the interaction [`c5b4f7e`](https://github.com/hydephp/framework/commit/c5b4f7eb71166bce556b19ddf84861798ea2bda4) + +#### [v0.7.5-alpha](https://github.com/hydephp/framework/compare/v0.7.4-alpha...v0.7.5-alpha) + +> 2 April 2022 + +- Fix broken meta url in schema prop [`b54cfe4`](https://github.com/hydephp/framework/commit/b54cfe4a1aa1441584cd0b209fcb89a99fa4ce7a) +- Fix broken meta url in schema prop [`80b5523`](https://github.com/hydephp/framework/commit/80b552305c3d5730a951ae2f5115bed21c9a4b84) + +#### [v0.7.4-alpha](https://github.com/hydephp/framework/compare/v0.7.3-alpha...v0.7.4-alpha) + +> 1 April 2022 + +- Fix bug #47 [`b7cdaf6`](https://github.com/hydephp/framework/commit/b7cdaf67e626855c3df7513dd0b58a563f9030be) + +#### [v0.7.3-alpha](https://github.com/hydephp/framework/compare/v0.7.2-alpha...v0.7.3-alpha) + +> 1 April 2022 + +- Fix #58 [`#58`](https://github.com/hydephp/framework/issues/58) + +#### [v0.7.2-alpha](https://github.com/hydephp/framework/compare/v0.7.1-alpha...v0.7.2-alpha) + +> 1 April 2022 + +- Create new command to scaffold pages [`#55`](https://github.com/hydephp/framework/pull/55) +- Apply fixes from StyleCI [`#56`](https://github.com/hydephp/framework/pull/56) +- Create the action [`b788de2`](https://github.com/hydephp/framework/commit/b788de22a3175c3b09eadf15249d152026f0a160) +- Create the command [`eac5258`](https://github.com/hydephp/framework/commit/eac5258268a152496cf10bff05a23aa2977617eb) +- Clean up and format code [`dc5c5ee`](https://github.com/hydephp/framework/commit/dc5c5eef20df88b729bf749004001a0832d31302) + +#### [v0.7.1-alpha](https://github.com/hydephp/framework/compare/v0.7.0-alpha...v0.7.1-alpha) + +> 1 April 2022 + +- Add a favicon link automatically if the file exists [`#54`](https://github.com/hydephp/framework/pull/54) +- Create LICENSE.md [`57d4a1b`](https://github.com/hydephp/framework/commit/57d4a1b6122e7fcef021d84bff76a97b53424d0a) +- Use getPrettyVersion for composer version [`7569fb7`](https://github.com/hydephp/framework/commit/7569fb7616bcbaa22b30aad00bf559cb81578feb) +- Change version to the (pretty) framework version [`973cc74`](https://github.com/hydephp/framework/commit/973cc7414c8a398801e2cb52364f9eb44269cf3e) + +#### [v0.7.0-alpha](https://github.com/hydephp/framework/compare/v0.6.2-alpha...v0.7.0-alpha) + +> 1 April 2022 + +- Fix bug #47 StaticPageBuilder not able to create nested documentation directories [`#51`](https://github.com/hydephp/framework/pull/51) +- Remove _authors and _drafts directories #48 [`#49`](https://github.com/hydephp/framework/pull/49) +- Apply fixes from StyleCI [`#50`](https://github.com/hydephp/framework/pull/50) +- Delete phpdoc.dist.xml [`b28afb7`](https://github.com/hydephp/framework/commit/b28afb712f7ea522e1fb9b2175223812d910b3a0) +- Remove _data directory [`a11ff92`](https://github.com/hydephp/framework/commit/a11ff9266ff3086c4e7a3ed17f7320e90cbd8788) +- Update author yml config path [`e0578bb`](https://github.com/hydephp/framework/commit/e0578bb8938c48b62540573fa88240932e629b4f) + +#### [v0.6.2-alpha](https://github.com/hydephp/framework/compare/v0.6.1-alpha...v0.6.2-alpha) + +> 30 March 2022 + +- Fix the documentation page header link [`#46`](https://github.com/hydephp/framework/pull/46) +- Use the indexpath basename for the doc header [`e188eb5`](https://github.com/hydephp/framework/commit/e188eb54f7d5c4fdc784fc16ffd7a60ad9ab458c) + +#### [v0.6.1-alpha](https://github.com/hydephp/framework/compare/v0.6.0-alpha...v0.6.1-alpha) + +> 30 March 2022 + +- Use relative path helper for links [`#45`](https://github.com/hydephp/framework/pull/45) +- Apply fixes from StyleCI [`#43`](https://github.com/hydephp/framework/pull/43) +- Add support for nesting the documentation pages [`#42`](https://github.com/hydephp/framework/pull/42) + +#### [v0.6.0-alpha](https://github.com/hydephp/framework/compare/v0.5.3-alpha...v0.6.0-alpha) + +> 30 March 2022 + +- Fix the 404 route bug [`#41`](https://github.com/hydephp/framework/pull/41) +- #38 Add a rebuild command to the Hyde CLI to rebuild a specific file [`#39`](https://github.com/hydephp/framework/pull/39) +- Apply fixes from StyleCI [`#40`](https://github.com/hydephp/framework/pull/40) +- Move scripts into app.js [`#35`](https://github.com/hydephp/framework/pull/35) +- #32 refactor command class names to be consistent [`#33`](https://github.com/hydephp/framework/pull/33) +- Add internal PHPDoc class descriptions [`#30`](https://github.com/hydephp/framework/pull/30) +- Apply fixes from StyleCI [`#31`](https://github.com/hydephp/framework/pull/31) +- Apply fixes from StyleCI [`#28`](https://github.com/hydephp/framework/pull/28) +- Require Torchlight [`#27`](https://github.com/hydephp/framework/pull/27) +- Restructure backend models [`#26`](https://github.com/hydephp/framework/pull/26) +- Rework how Markdown files are handled to improve maintainability and testing [`#25`](https://github.com/hydephp/framework/pull/25) +- 0.6.0 Remove support for Front Matter in Markdown Pages [`#24`](https://github.com/hydephp/framework/pull/24) +- Fix #21 by dynamically routing to the docs index [`#23`](https://github.com/hydephp/framework/pull/23) +- Merge pull request #23 from hydephp/21-bug-documentation-sidebar-header-should-link-to-readme-if-that-exists-but-an-index-does-not [`#21`](https://github.com/hydephp/framework/issues/21) +- Fix #21 by dynamically routing to the docs index [`#21`](https://github.com/hydephp/framework/issues/21) +- Add PHPUnit [`0d59ea0`](https://github.com/hydephp/framework/commit/0d59ea032a8b2be2f5c09db06563ab504e233d05) +- Create the HydeRebuildStaticSiteCommand [`92b1d20`](https://github.com/hydephp/framework/commit/92b1d20069482f851ee18629a0845a69e8f5a43a) +- Refactor to use the MarkdownFileService [`48a27a2`](https://github.com/hydephp/framework/commit/48a27a2799fd6a27e3bfa55417c2eb7fda3a4c43) + +#### [v0.5.3-alpha](https://github.com/hydephp/framework/compare/v0.5.2-alpha...v0.5.3-alpha) + +> 26 March 2022 + +- Remove deprecated methods [`#19`](https://github.com/hydephp/framework/pull/19) +- Make the command extend the base command [`eaba9da`](https://github.com/hydephp/framework/commit/eaba9dac5a9849804ccfdfc2798129fbe5cb0daf) +- Remove deprecated class [`24753c1`](https://github.com/hydephp/framework/commit/24753c1776c5f887baed82c93f02b632032ffde1) +- Format to PSR2 [`8307b65`](https://github.com/hydephp/framework/commit/8307b65087f73c3bbb40ecc7eb469db83c7777be) + +#### [v0.5.2-alpha](https://github.com/hydephp/framework/compare/v0.5.1-alpha...v0.5.2-alpha) + +> 25 March 2022 + +- Remove the Hyde installer [`#18`](https://github.com/hydephp/framework/pull/18) +- 0.6.x Remove deprecated command [`#17`](https://github.com/hydephp/framework/pull/17) +- Improve Docgen Feature by allowing the output directory to be dynamically changed [`#16`](https://github.com/hydephp/framework/pull/16) +- Rework installer prompts and fix wrong directory [`c15a4ac`](https://github.com/hydephp/framework/commit/c15a4acdf76e71221f3ba4c8d028ce2d0a7e3b0a) +- Allow the documentation output directory to be changed [`6cf07a3`](https://github.com/hydephp/framework/commit/6cf07a35aa3517d6691da3bb0ded266dea0e812a) +- Allow the homepage argument to be set from cli [`ab8dedd`](https://github.com/hydephp/framework/commit/ab8deddbebd73e458712cbde51a8c40a33fae38e) + +#### [v0.5.1-alpha](https://github.com/hydephp/framework/compare/v0.5.0-alpha...v0.5.1-alpha) + +> 24 March 2022 + +- Fix visual bug caused by setting max-width on body instead of article [`#15`](https://github.com/hydephp/framework/pull/15) +- Load commands in service provider instead of config/commands.php [`#13`](https://github.com/hydephp/framework/pull/13) +- Load commands in service provider instead of config [`46397fd`](https://github.com/hydephp/framework/commit/46397fd28e6cec25ec92ce44e047183b87346331) + +#### [v0.5.0-alpha](https://github.com/hydephp/framework/compare/v0.4.3-alpha...v0.5.0-alpha) + +> 24 March 2022 + +- Merge 0.5.0 into Master - Adds a multitude of new tests, code refactors and quality of life features [`#12`](https://github.com/hydephp/framework/pull/12) +- Sync branch with Master [`#11`](https://github.com/hydephp/framework/pull/11) +- Merge 0.5.x progress [`#10`](https://github.com/hydephp/framework/pull/10) +- Add _data directory and Authors object as well as stubs to aid in testing [`#9`](https://github.com/hydephp/framework/pull/9) +- Add required depedency to framework [`e5f0ec5`](https://github.com/hydephp/framework/commit/e5f0ec58df1163ef1de85a0b3233a347c45be136) +- Implement the Authors backend feature [`d7679f5`](https://github.com/hydephp/framework/commit/d7679f5b8d9ac900229a91d59099974cb82568e1) +- Add Commonmark as an explicit dependency [`bf915b1`](https://github.com/hydephp/framework/commit/bf915b130f418433ee2b47cc158229614883b090) + +#### [v0.4.3-alpha](https://github.com/hydephp/framework/compare/v0.4.2-alpha...v0.4.3-alpha) + +> 23 March 2022 + +- Add bindings for the package versions [`a9ce58d`](https://github.com/hydephp/framework/commit/a9ce58d2a9583866c05451ecf0da1dac4f84260b) +- Get version from facade [`465bafc`](https://github.com/hydephp/framework/commit/465bafc59fd0d20c5df91148d148d4c89a36e988) +- Replace Git version with Hyde version [`bcb7357`](https://github.com/hydephp/framework/commit/bcb7357f637138239bbee3ece007ff45564718bd) + +#### [v0.4.2-alpha](https://github.com/hydephp/framework/compare/v0.4.1-alpha...v0.4.2-alpha) + +> 23 March 2022 + +- v0.4.2-alpha Adds new meta tags and more data rich HTML [`#8`](https://github.com/hydephp/framework/pull/8) +- Add new meta tag options [`78a74c7`](https://github.com/hydephp/framework/commit/78a74c7c5342d6a8b528134022ba822e506cb12e) +- Add the Site URL feature, remember to update config! [`ee2f5c6`](https://github.com/hydephp/framework/commit/ee2f5c6b542ec3eb20412a8ef718b11cc1a9e23c) +- Add more rich HTML content [`8eb6778`](https://github.com/hydephp/framework/commit/8eb677849a655a30dffe5bfb3d48921ff4b24821) + +#### [v0.4.1-alpha](https://github.com/hydephp/framework/compare/v0.4.0-alpha...v0.4.1-alpha) + +> 22 March 2022 + +- Add the Hyde::getLatestPosts() shorthand to get the latest posts collection [`#4`](https://github.com/hydephp/framework/pull/4) +- Add new options to the build command to improve the user experience [`#3`](https://github.com/hydephp/framework/pull/3) +- Remove progress bar from empty collections [`40d3203`](https://github.com/hydephp/framework/commit/40d3203d5494d37cea1b921f2a4447bc924d18d7) +- Add option to remove old files before building [`2650997`](https://github.com/hydephp/framework/commit/26509974c02a0c2d14f6fec490bdedc89a9b7725) +- Add options to automatically build frontend assets [`f789c2f`](https://github.com/hydephp/framework/commit/f789c2fc840e5bbffbf1df2b6a56576a846d48f5) + +#### [v0.4.0-alpha](https://github.com/hydephp/framework/compare/v0.3.1-alpha...v0.4.0-alpha) + +> 22 March 2022 + +- Add the console logo font [`2683a4b`](https://github.com/hydephp/framework/commit/2683a4b06d6ea646d2d3f6eaab32746df8a02da0) +- Add the config files [`47e9044`](https://github.com/hydephp/framework/commit/47e9044c3f63a02c8c5858d0a32861031126387c) +- Add the 404 page [`962cbe2`](https://github.com/hydephp/framework/commit/962cbe2886f2815a5c46de56b73e594cd3b12d1b) + +#### [v0.3.1-alpha](https://github.com/hydephp/framework/compare/v0.3.0-alpha...v0.3.1-alpha) + +> 22 March 2022 + +- Delete vendor directory [`4f96627`](https://github.com/hydephp/framework/commit/4f96627679a2e6de95520010a6f1bc98f30bca9f) +- 0.3.1 Move commands to framework [`70dd8df`](https://github.com/hydephp/framework/commit/70dd8df956e7fc1bc1c9b67a14e2b23a8fea4d76) +- Add php 8 require, and suggest hyde/hyde [`a8ff6ad`](https://github.com/hydephp/framework/commit/a8ff6ad9b3db7fe5bf69c638dd03b21309b85e42) + +#### v0.3.0-alpha + +> 21 March 2022 + +- Add the Core files (with temporary namespace) [`816ad3a`](https://github.com/hydephp/framework/commit/816ad3a24e5f95dff5aa1f1cfd581764fd1a1389) +- Initial Commit [`fa00787`](https://github.com/hydephp/framework/commit/fa007876e36ca6588147b05d44f927d7e8fbf997) +- Successfully move namespace Core to Framework [`0c9160f`](https://github.com/hydephp/framework/commit/0c9160f33124701e6ed21a1e5b2bd70f46aaa65a) diff --git a/packages/framework/LICENSE.md b/packages/framework/LICENSE.md new file mode 100644 index 00000000000..761daf50778 --- /dev/null +++ b/packages/framework/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Caen De Silva + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/framework/README.md b/packages/framework/README.md new file mode 100644 index 00000000000..9828f16828a --- /dev/null +++ b/packages/framework/README.md @@ -0,0 +1,69 @@ +# HydePHP - The Core Framework + +![framework](https://user-images.githubusercontent.com/95144705/172196099-b5604f9b-90aa-4fac-bd17-b5a150ebf8f5.png) + +<p> + <a href="https://packagist.org/packages/hyde/framework"><img style="display: inline; margin: 4px 2px;" src="https://img.shields.io/packagist/v/hyde/framework?include_prereleases" alt="Latest Version on Packagist"></a> + <a href="https://packagist.org/packages/hyde/framework"><img style="display: inline; margin: 4px 2px;" src="https://img.shields.io/packagist/dt/hyde/framework" alt="Total Downloads on Packagist"></a> + <a href="https://github.com/hydephp/hyde/blob/master/LICENSE.md"> <img style="display: inline; margin: 4px 2px;" src="https://img.shields.io/github/license/hydephp/hyde" alt="License MIT"> </a> + <a href="https://hydephp.com/developer-tools/coverage-report/"><img style="display: inline; margin: 4px 2px;" src="https://cdn.desilva.se/microservices/coverbadges/badges/9b8f6a9a7a48a2df54e6751790bad8bd910015301e379f334d6ec74c4c3806d1.svg" alt="Test Coverage" title="Average coverage between categories"></a> + <img style="display: inline; margin: 4px 2px;" src="https://github.com/hydephp/framework/actions/workflows/test-suite.yml/badge.svg" alt="Test Suite"> <img style="display: inline; margin: 4px 2px;" src="https://github.styleci.io/repos/472503421/shield?branch=master" alt="StyleCI Status"> </a> +</p> + + +## About +HydePHP is a content-first Laravel-powered Static Site Builder that allows you to create static HTML pages, blog posts, and documentation sites using Markdown. +Hyde is all about letting you get started quickly by giving you a full-featured frontend starter kit, while also giving you the power and freedom of doing things the way you want to. + +Markdown purist? That's all you need. Blade artisan? Go for it. +Hyde comes with hand-crafted frontend templates so you can focus on your content. +You don't _need_ to customize anything. But you _can_ customize everything. + +> Note: This repository contains the core code of the Hyde framework. If you want to build an application using Hyde, visit the [main Hyde repository](https://github.com/hydephp/hyde) which also serves as a template to get started quickly. + +### Links & Media + +Hyde has already been featured in the [Laravel News Podcast](https://laravel-news.com/podcast/836911) and in the [Symfony Station Communique](https://www.symfonystation.com/Symfony-Station-Communique-13-May-2022). You can find several tutorials on [DEV Community](https://dev.to/t/hydephp) and learn much more on the [HydePHP website](https://hydephp.com) and [Official documentation](https://hydephp.com/docs). + +### Repositories + +- [HydePHP/Framework](https://github.com/hydephp/framework) - The core project contains the brains of Hyde and is not intended to be used as a standalone. Use together with Hyde/Hyde. <i>Equivalent to Laravel/Framework</i> +- [HydePHP/Hyde](https://github.com/hydephp/hyde) - The Hyde project repo. This is the repository you should start with if you want to build an application using Hyde. This repo is what the installer uses. <i>Equivalent to Laravel/Laravel</i> +- [HydePHP/Hydefront](https://github.com/hydephp/hydefront) - The Hyde frontend assets. Extracted from the HydePHP/Framework repo to improve testing and allow for easier deployment using a CDN. + + +## ⚠ Beta Software Warning +Heads up! HydePHP is very new and currently in beta. Please report any bugs and issues in the appropriate issue tracker. Versions in the 0.x series are not stable and may change at any time. No backwards compatibility guarantees are made and there will be breaking changes without notice. + +Please wait until v1.0 for production use and remember to back up your source files before updating (use Git!). +See https://hydephp.com/docs/master/updating-hyde.html for the upgrade guide. + +## Resources + +### Changelog + +Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. + +### Contributing + +Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. + +### Security + +If you discover any security-related issues, please email caen@desilva.se instead of using the issue tracker. +All vulnerabilities will be promptly addressed. + +### Credits + +- [Caen De Silva](https://github.com/caendesilva) +- [All Contributors](../../contributors) + +### License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. + +### Attributions + +- The HydeCLI and core internals is built with [Laravel Zero](https://laravel-zero.com/) which is based on [Laravel](https://laravel.com/) + +> Please see the respective authors' repositories for their license files diff --git a/packages/framework/SECURITY.md b/packages/framework/SECURITY.md new file mode 100644 index 00000000000..066536ad653 --- /dev/null +++ b/packages/framework/SECURITY.md @@ -0,0 +1,28 @@ +# Security Policy + +## Supported Versions + +### Hyde is currently in Beta and has no supported versions. + +Versions in the 0.x series are not stable and may change at any time. +No backwards compatibility guarantees are made and there will be breaking changes without notice. + +| Version | Supported | Classification | +|---------|-----------|---------------------------| +| > 0.20 | :warning: | Beta (active development) | +| < 0.20 | :x: | Beta (legacy) | +| < 0.8 | :x: | Alpha stage | + + +<!-- +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| < 4.0 | :x: | --> + + +## Reporting a Vulnerability + +If you discover a security vulnerability within this package, please send an e-mail to the creator, Caen De Silva, via caen@desilva.se. + +All security vulnerabilities will be promptly addressed. diff --git a/packages/framework/codecov.yml b/packages/framework/codecov.yml new file mode 100644 index 00000000000..9bc1dcf9215 --- /dev/null +++ b/packages/framework/codecov.yml @@ -0,0 +1,23 @@ +codecov: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "reach,diff,flags,files,footer" + behavior: default + require_changes: no + +fixes: + - "/home/runner/work/framework/framework/::" diff --git a/packages/framework/composer.json b/packages/framework/composer.json new file mode 100644 index 00000000000..83c43f3db45 --- /dev/null +++ b/packages/framework/composer.json @@ -0,0 +1,38 @@ +{ + "name": "hyde/framework", + "description": "The HydePHP Framework", + "license": "MIT", + "autoload": { + "psr-4": { + "Hyde\\Framework\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "authors": [ + { + "name": "Caen De Silva", + "email": "caen@desilva.se" + } + ], + "require": { + "php": "^8.0", + "illuminate/support": "^9.5", + "illuminate/view": "^9.0", + "symfony/yaml": "^6.0", + "laravel-zero/framework": "^9.1", + "league/commonmark": "^2.2", + "spatie/yaml-front-matter": "^2.0.7", + "torchlight/torchlight-commonmark": "^0.5.5" + }, + "require-dev": { + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0" + }, + "suggest": { + "hyde/hyde" : "The Framework package contains the Hyde Core. To create your site you should use the Hyde/Hyde project.", + "ext-simplexml": "Required to generate sitemaps and RSS feeds." + } +} diff --git a/packages/framework/composer.lock b/packages/framework/composer.lock new file mode 100644 index 00000000000..251712527d0 --- /dev/null +++ b/packages/framework/composer.lock @@ -0,0 +1,7819 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "65fd766b1d591d3027de18f71be117fe", + "packages": [ + { + "name": "brick/math", + "version": "0.9.3", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae", + "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0", + "vimeo/psalm": "4.9.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.9.3" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/brick/math", + "type": "tidelift" + } + ], + "time": "2021-08-15T20:50:18+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "0992cc19268b259a39e86f296da5f0677841f42c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/0992cc19268b259a39e86f296da5f0677841f42c", + "reference": "0992cc19268b259a39e86f296da5f0677841f42c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^3.14" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.1" + }, + "time": "2021-08-13T13:06:58+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "vimeo/psalm": "^4.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:16:43+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.1", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-01-18T15:43:28+00:00" + }, + { + "name": "facade/ignition-contracts", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/facade/ignition-contracts.git", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^v2.15.8", + "phpunit/phpunit": "^9.3.11", + "vimeo/psalm": "^3.17.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Facade\\IgnitionContracts\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://flareapp.io", + "role": "Developer" + } + ], + "description": "Solution contracts for Ignition", + "homepage": "https://github.com/facade/ignition-contracts", + "keywords": [ + "contracts", + "flare", + "ignition" + ], + "support": { + "issues": "https://github.com/facade/ignition-contracts/issues", + "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" + }, + "time": "2020-10-16T08:27:54+00:00" + }, + { + "name": "filp/whoops", + "version": "2.14.5", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/a63e5e8f26ebbebf8ed3c5c691637325512eb0dc", + "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc", + "shasum": "" + }, + "require": { + "php": "^5.5.9 || ^7.0 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^0.9 || ^1.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.14.5" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2022-01-07T12:00:00+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/58571acbaa5f9f462c9c77e911700ac66f446d4e", + "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2022-02-20T15:07:15+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "0690bde05318336c7221785f2a932467f98b64ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", + "reference": "0690bde05318336c7221785f2a932467f98b64ca", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "phpoption/phpoption": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2021-11-21T21:41:47+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.4.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "74a8602c6faec9ef74b7a9391ac82c5e65b1cdab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/74a8602c6faec9ef74b7a9391ac82c5e65b1cdab", + "reference": "74a8602c6faec9ef74b7a9391ac82c5e65b1cdab", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.4-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.4.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-05-25T13:24:33+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2", + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.2.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-03-20T21:55:58+00:00" + }, + { + "name": "illuminate/bus", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/bus.git", + "reference": "405a2d6858fc2e19d576c1844d08bd9e77a10ad9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/bus/zipball/405a2d6858fc2e19d576c1844d08bd9e77a10ad9", + "reference": "405a2d6858fc2e19d576c1844d08bd9e77a10ad9", + "shasum": "" + }, + "require": { + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/pipeline": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "suggest": { + "illuminate/queue": "Required to use closures when chaining jobs (^7.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Bus\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Bus package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-04-26T14:45:19+00:00" + }, + { + "name": "illuminate/cache", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/cache.git", + "reference": "dcf787ca2e026e838970b73857a3265c93fb80c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/cache/zipball/dcf787ca2e026e838970b73857a3265c93fb80c3", + "reference": "dcf787ca2e026e838970b73857a3265c93fb80c3", + "shasum": "" + }, + "require": { + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "provide": { + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "suggest": { + "ext-memcached": "Required to use the memcache cache driver.", + "illuminate/database": "Required to use the database cache driver (^9.0).", + "illuminate/filesystem": "Required to use the file cache driver (^9.0).", + "illuminate/redis": "Required to use the redis cache driver (^9.0).", + "symfony/cache": "Required to use PSR-6 cache bridge (^6.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Cache package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-23T15:54:33+00:00" + }, + { + "name": "illuminate/collections", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "5aac36506247e4f0e50afe815ba711c16e65bcef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/5aac36506247e4f0e50afe815ba711c16e65bcef", + "reference": "5aac36506247e4f0e50afe815ba711c16e65bcef", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "php": "^8.0.2" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^6.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-30T13:16:45+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "46b7beed47948bd2e67f523d0a76daa62775031e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/46b7beed47948bd2e67f523d0a76daa62775031e", + "reference": "46b7beed47948bd2e67f523d0a76daa62775031e", + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-31T14:47:50+00:00" + }, + { + "name": "illuminate/config", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/config.git", + "reference": "d2dc74fdcc89239e1910a8d08b2b2e5ad26a043c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/config/zipball/d2dc74fdcc89239e1910a8d08b2b2e5ad26a043c", + "reference": "d2dc74fdcc89239e1910a8d08b2b2e5ad26a043c", + "shasum": "" + }, + "require": { + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Config\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Config package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-02-01T16:16:50+00:00" + }, + { + "name": "illuminate/console", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/console.git", + "reference": "4de4c1de7c76e7ac9a1d28618ea6a616e597db55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/console/zipball/4de4c1de7c76e7ac9a1d28618ea6a616e597db55", + "reference": "4de4c1de7c76e7ac9a1d28618ea6a616e597db55", + "shasum": "" + }, + "require": { + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2", + "symfony/console": "^6.0", + "symfony/process": "^6.0" + }, + "suggest": { + "dragonmantank/cron-expression": "Required to use scheduler (^3.1).", + "guzzlehttp/guzzle": "Required to use the ping methods on schedules (^7.2).", + "illuminate/bus": "Required to use the scheduled job dispatcher (^9.0).", + "illuminate/container": "Required to use the scheduler (^9.0).", + "illuminate/filesystem": "Required to use the generator command (^9.0).", + "illuminate/queue": "Required to use closures for scheduled jobs (^9.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Console package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-16T15:53:09+00:00" + }, + { + "name": "illuminate/container", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/container.git", + "reference": "d86b073cae04713cf28def54417fa771621bc4f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/container/zipball/d86b073cae04713cf28def54417fa771621bc4f1", + "reference": "d86b073cae04713cf28def54417fa771621bc4f1", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.0", + "php": "^8.0.2", + "psr/container": "^1.1.1|^2.0.1" + }, + "provide": { + "psr/container-implementation": "1.1|2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Container\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Container package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-16T15:53:09+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "e354ef98f3c59e5c8b5ba87299999220270f3da5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/e354ef98f3c59e5c8b5ba87299999220270f3da5", + "reference": "e354ef98f3c59e5c8b5ba87299999220270f3da5", + "shasum": "" + }, + "require": { + "php": "^8.0.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-04-28T13:05:07+00:00" + }, + { + "name": "illuminate/events", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/events.git", + "reference": "2dea521665d295f6cefef78f1b5abeea6b94e35f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/events/zipball/2dea521665d295f6cefef78f1b5abeea6b94e35f", + "reference": "2dea521665d295f6cefef78f1b5abeea6b94e35f", + "shasum": "" + }, + "require": { + "illuminate/bus": "^9.0", + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Illuminate\\Events\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Events package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-02T13:59:45+00:00" + }, + { + "name": "illuminate/filesystem", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/filesystem.git", + "reference": "fd8963dddcb053b918bd1d851eccb887180a2c83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/filesystem/zipball/fd8963dddcb053b918bd1d851eccb887180a2c83", + "reference": "fd8963dddcb053b918bd1d851eccb887180a2c83", + "shasum": "" + }, + "require": { + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2", + "symfony/finder": "^6.0" + }, + "suggest": { + "ext-ftp": "Required to use the Flysystem FTP driver.", + "illuminate/http": "Required for handling uploaded files (^7.0).", + "league/flysystem": "Required to use the Flysystem local driver (^3.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.0).", + "symfony/mime": "Required to enable support for guessing extensions (^6.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Filesystem package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-31T14:47:24+00:00" + }, + { + "name": "illuminate/http", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/http.git", + "reference": "ddbd52406c9662a0b8ef39f38ddabb7d5d23cf3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/http/zipball/ddbd52406c9662a0b8ef39f38ddabb7d5d23cf3e", + "reference": "ddbd52406c9662a0b8ef39f38ddabb7d5d23cf3e", + "shasum": "" + }, + "require": { + "ext-json": "*", + "fruitcake/php-cors": "^1.2", + "illuminate/collections": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/session": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2", + "symfony/http-foundation": "^6.0", + "symfony/http-kernel": "^6.0", + "symfony/mime": "^6.0" + }, + "suggest": { + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "guzzlehttp/guzzle": "Required to use the HTTP Client (^7.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Http\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Http package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-06-02T02:50:50+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "25a2c6dac2b7541ecbadef952702e84ae15f5354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/25a2c6dac2b7541ecbadef952702e84ae15f5354", + "reference": "25a2c6dac2b7541ecbadef952702e84ae15f5354", + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-02-01T14:44:21+00:00" + }, + { + "name": "illuminate/pipeline", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/pipeline.git", + "reference": "6d448699cc440cfe7696d65c62313ef2a02961b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/pipeline/zipball/6d448699cc440cfe7696d65c62313ef2a02961b1", + "reference": "6d448699cc440cfe7696d65c62313ef2a02961b1", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Pipeline\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Pipeline package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-02-28T17:10:42+00:00" + }, + { + "name": "illuminate/session", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/session.git", + "reference": "9096180db6a1195e50c13afb3db2035915b52628" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/session/zipball/9096180db6a1195e50c13afb3db2035915b52628", + "reference": "9096180db6a1195e50c13afb3db2035915b52628", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/filesystem": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2", + "symfony/finder": "^6.0", + "symfony/http-foundation": "^6.0" + }, + "suggest": { + "illuminate/console": "Required to use the session:table command (^9.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Session\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Session package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-04-22T18:57:23+00:00" + }, + { + "name": "illuminate/support", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/support.git", + "reference": "f87ac1cc6dd3f9f03c9f7b48c7d94b7a53933a0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/support/zipball/f87ac1cc6dd3f9f03c9f7b48c7d94b7a53933a0a", + "reference": "f87ac1cc6dd3f9f03c9f7b48c7d94b7a53933a0a", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^2.0", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/collections": "^9.0", + "illuminate/conditionable": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "nesbot/carbon": "^2.53.1", + "php": "^8.0.2", + "voku/portable-ascii": "^2.0" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "suggest": { + "illuminate/filesystem": "Required to use the composer class (^9.0).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).", + "ramsey/uuid": "Required to use Str::uuid() (^4.2.2).", + "symfony/process": "Required to use the composer class (^6.0).", + "symfony/var-dumper": "Required to use the dd function (^6.0).", + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Support package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-06-02T14:10:45+00:00" + }, + { + "name": "illuminate/testing", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/testing.git", + "reference": "e050d72d314e16492ec08401b295a214adf2d7ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/testing/zipball/e050d72d314e16492ec08401b295a214adf2d7ee", + "reference": "e050d72d314e16492ec08401b295a214adf2d7ee", + "shasum": "" + }, + "require": { + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "suggest": { + "brianium/paratest": "Required to run tests in parallel (^6.0).", + "illuminate/console": "Required to assert console commands (^9.0).", + "illuminate/database": "Required to assert databases (^9.0).", + "illuminate/http": "Required to assert responses (^9.0).", + "mockery/mockery": "Required to use mocking (^1.4.4).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Testing\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Testing package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-24T14:00:57+00:00" + }, + { + "name": "illuminate/view", + "version": "v9.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/view.git", + "reference": "dea48b6f93933a941fd6313e5229d56038c5165e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/view/zipball/dea48b6f93933a941fd6313e5229d56038c5165e", + "reference": "dea48b6f93933a941fd6313e5229d56038c5165e", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/events": "^9.0", + "illuminate/filesystem": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\View\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate View package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-05-31T14:53:10+00:00" + }, + { + "name": "jolicode/jolinotif", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/jolicode/JoliNotif.git", + "reference": "a15bfc0d5aef432f150385924ede4e099643edb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jolicode/JoliNotif/zipball/a15bfc0d5aef432f150385924ede4e099643edb7", + "reference": "a15bfc0d5aef432f150385924ede4e099643edb7", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "symfony/process": "^4.0|^5.0|^6.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "symfony/finder": "^5.0", + "symfony/phpunit-bridge": "^5.0" + }, + "bin": [ + "jolinotif" + ], + "type": "library", + "autoload": { + "psr-4": { + "Joli\\JoliNotif\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Loïck Piera", + "email": "pyrech@gmail.com" + } + ], + "description": "Send desktop notifications on Windows, Linux, MacOS.", + "keywords": [ + "MAC", + "growl", + "linux", + "notification", + "windows" + ], + "support": { + "issues": "https://github.com/jolicode/JoliNotif/issues", + "source": "https://github.com/jolicode/JoliNotif/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/jolicode/jolinotif", + "type": "tidelift" + } + ], + "time": "2021-12-01T16:20:42+00:00" + }, + { + "name": "laravel-zero/foundation", + "version": "v9.13.0", + "source": { + "type": "git", + "url": "https://github.com/laravel-zero/foundation.git", + "reference": "dfe43bce2e1c935f9c0a0e2d74d00bcb7049ceda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel-zero/foundation/zipball/dfe43bce2e1c935f9c0a0e2d74d00bcb7049ceda", + "reference": "dfe43bce2e1c935f9c0a0e2d74d00bcb7049ceda", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Foundation/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "This is a mirror from illuminate/foundation.", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "source": "https://github.com/laravel-zero/foundation/tree/v9.13.0" + }, + "time": "2022-05-18T15:59:33+00:00" + }, + { + "name": "laravel-zero/framework", + "version": "v9.1.1", + "source": { + "type": "git", + "url": "https://github.com/laravel-zero/framework.git", + "reference": "e9bdb5e1338c4c874be9cb6c902c5fbc5a58e02e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel-zero/framework/zipball/e9bdb5e1338c4c874be9cb6c902c5fbc5a58e02e", + "reference": "e9bdb5e1338c4c874be9cb6c902c5fbc5a58e02e", + "shasum": "" + }, + "require": { + "dragonmantank/cron-expression": "^3.2.4", + "ext-json": "*", + "illuminate/cache": "^9.0.0", + "illuminate/collections": "^9.0.0", + "illuminate/config": "^9.0.0", + "illuminate/console": "^9.0.0", + "illuminate/container": "^9.0.0", + "illuminate/contracts": "^9.0.0", + "illuminate/events": "^9.0.0", + "illuminate/filesystem": "^9.0.0", + "illuminate/support": "^9.0.0", + "illuminate/testing": "^9.0.0", + "laravel-zero/foundation": "^9.0.0", + "league/flysystem": "^3.0.0", + "nunomaduro/collision": "^6.0.0", + "nunomaduro/laravel-console-summary": "^1.8.0", + "nunomaduro/laravel-console-task": "^1.7.0", + "nunomaduro/laravel-desktop-notifier": "^2.6.0", + "php": "^8.0.2", + "psr/log": "^1.1.4|^2.0.0|^3.0.0", + "ramsey/uuid": "^4.2.3", + "symfony/console": "^6.0.0", + "symfony/error-handler": "^6.0.0", + "symfony/finder": "^6.0.0", + "symfony/process": "^6.0.0", + "symfony/var-dumper": "^6.0.0", + "vlucas/phpdotenv": "^5.4.1" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4.1", + "illuminate/bus": "^9.0.0", + "illuminate/database": "^9.0.0", + "illuminate/http": "^9.0.0", + "illuminate/log": "^9.0.0", + "illuminate/queue": "^9.0.0", + "illuminate/redis": "^9.0.0", + "illuminate/view": "^9.0.0", + "laminas/laminas-text": "^2.9.0", + "laravel-zero/phar-updater": "^1.2", + "nunomaduro/laravel-console-dusk": "^1.10.0", + "nunomaduro/laravel-console-menu": "^3.3.0", + "nunomaduro/termwind": "^1.3", + "pestphp/pest": "^1.21.1", + "phpstan/phpstan": "^1.4.6" + }, + "suggest": { + "ext-pcntl": "Required to ensure that data is cleared when cancelling the build process." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "LaravelZero\\Framework\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The Laravel Zero Framework.", + "homepage": "https://laravel-zero.com", + "keywords": [ + "Laravel Zero", + "cli", + "console", + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel-zero/laravel-zero/issues", + "source": "https://github.com/laravel-zero/laravel-zero" + }, + "time": "2022-03-01T15:09:55+00:00" + }, + { + "name": "league/commonmark", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "6eddb90a9e4a1a8c5773226068fcfb48cb36812a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6eddb90a9e4a1a8c5773226068fcfb48cb36812a", + "reference": "6eddb90a9e4a1a8c5773226068fcfb48cb36812a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.30.0", + "commonmark/commonmark.js": "0.30.0", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^0.12.88 || ^1.0.0", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2022-06-03T14:07:39+00:00" + }, + { + "name": "league/config", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e", + "reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.90", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2021-08-14T12:15:32+00:00" + }, + { + "name": "league/flysystem", + "version": "3.0.20", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "42a2f47dcf39944e2aee1b660ee55ab6ef69b535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/42a2f47dcf39944e2aee1b660ee55ab6ef69b535", + "reference": "42a2f47dcf39944e2aee1b660ee55ab6ef69b535", + "shasum": "" + }, + "require": { + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5", + "async-aws/simple-s3": "^1.0", + "aws/aws-sdk-php": "^3.198.1", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "microsoft/azure-storage-blob": "^1.1", + "phpseclib/phpseclib": "^2.0", + "phpstan/phpstan": "^0.12.26", + "phpunit/phpunit": "^9.5.11", + "sabre/dav": "^4.3.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.0.20" + }, + "funding": [ + { + "url": "https://offset.earth/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2022-05-25T19:18:39+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2022-04-17T13:12:02+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.58.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "97a34af22bde8d0ac20ab34b29d7bfe360902055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/97a34af22bde8d0ac20ab34b29d7bfe360902055", + "reference": "97a34af22bde8d0ac20ab34b29d7bfe360902055", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.0", + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.54 || ^1.0", + "phpunit/php-file-iterator": "^2.0.5", + "phpunit/phpunit": "^7.5.20 || ^8.5.23", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2022-04-25T19:31:17+00:00" + }, + { + "name": "nette/schema", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/9a39cef03a5b34c7de64f551538cbba05c2be5df", + "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df", + "shasum": "" + }, + "require": { + "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", + "php": ">=7.1 <8.2" + }, + "require-dev": { + "nette/tester": "^2.3 || ^2.4", + "phpstan/phpstan-nette": "^0.12", + "tracy/tracy": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.2.2" + }, + "time": "2021-10-15T11:40:02+00:00" + }, + { + "name": "nette/utils", + "version": "v3.2.7", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/0af4e3de4df9f1543534beab255ccf459e7a2c99", + "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99", + "shasum": "" + }, + "require": { + "php": ">=7.2 <8.2" + }, + "conflict": { + "nette/di": "<3.0.6" + }, + "require-dev": { + "nette/tester": "~2.0", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.3" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", + "ext-xml": "to use Strings::length() etc. when mbstring is not available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v3.2.7" + }, + "time": "2022-01-24T11:29:14+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v6.2.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "c379636dc50e829edb3a8bcb944a01aa1aed8f25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/c379636dc50e829edb3a8bcb944a01aa1aed8f25", + "reference": "c379636dc50e829edb3a8bcb944a01aa1aed8f25", + "shasum": "" + }, + "require": { + "facade/ignition-contracts": "^1.0.2", + "filp/whoops": "^2.14.5", + "php": "^8.0.0", + "symfony/console": "^6.0.2" + }, + "require-dev": { + "brianium/paratest": "^6.4.1", + "laravel/framework": "^9.7", + "nunomaduro/larastan": "^1.0.2", + "nunomaduro/mock-final-classes": "^1.1.0", + "orchestra/testbench": "^7.3.0", + "phpunit/phpunit": "^9.5.11" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "6.x-dev" + }, + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2022-04-05T15:31:38+00:00" + }, + { + "name": "nunomaduro/laravel-console-summary", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/laravel-console-summary.git", + "reference": "1b32af3f39a744223c4ed6d2a5080fc5baa037da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/laravel-console-summary/zipball/1b32af3f39a744223c4ed6d2a5080fc5baa037da", + "reference": "1b32af3f39a744223c4ed6d2a5080fc5baa037da", + "shasum": "" + }, + "require": { + "illuminate/console": "^7.0|^8.0|^9.0", + "illuminate/support": "^7.0|^8.0|^9.0", + "php": "^7.2.5|^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\LaravelConsoleSummary\\LaravelConsoleSummaryServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\LaravelConsoleSummary\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "A Beautiful Laravel Console Summary for your Laravel/Laravel Zero commands.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/laravel-console-summary/issues", + "source": "https://github.com/nunomaduro/laravel-console-summary" + }, + "time": "2022-01-13T14:34:23+00:00" + }, + { + "name": "nunomaduro/laravel-console-task", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/laravel-console-task.git", + "reference": "7613432d2eb77498d5c7bdce560a33b7d82d8eeb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/laravel-console-task/zipball/7613432d2eb77498d5c7bdce560a33b7d82d8eeb", + "reference": "7613432d2eb77498d5c7bdce560a33b7d82d8eeb", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0", + "php": "^7.2.5|^8.0" + }, + "require-dev": { + "pestphp/pest": "^1.20" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\LaravelConsoleTask\\LaravelConsoleTaskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\LaravelConsoleTask\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Laravel Console Task is a output method for your Laravel/Laravel Zero commands.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/laravel-console-task/issues", + "source": "https://github.com/nunomaduro/laravel-console-task" + }, + "time": "2022-01-13T14:40:41+00:00" + }, + { + "name": "nunomaduro/laravel-desktop-notifier", + "version": "v2.6.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/laravel-desktop-notifier.git", + "reference": "f70febce1c6cc931bc71fd9c61049eb6b8d3c302" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/laravel-desktop-notifier/zipball/f70febce1c6cc931bc71fd9c61049eb6b8d3c302", + "reference": "f70febce1c6cc931bc71fd9c61049eb6b8d3c302", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.20|^7.29|^8.12|^9.0", + "illuminate/support": "^6.20|^7.29|^8.12|^9.0", + "jolicode/jolinotif": "^2.0", + "php": "^7.2.5|^8.0" + }, + "require-dev": { + "graham-campbell/testbench": "^5.5", + "phpunit/phpunit": "^8.5.8|^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\LaravelDesktopNotifier\\LaravelDesktopNotifierServiceProvider" + ], + "aliases": { + "Notifier": "NunoMaduro\\LaravelDesktopNotifier\\Facaces\\Notifier" + } + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\LaravelDesktopNotifier\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Send notifications to your desktop from your Laravel commands. An JoliNotif wrapper for Laravel 5.", + "keywords": [ + "JoliNotif", + "Nuno Maduro", + "NunoMaduro", + "artisan", + "console", + "framework", + "laravel", + "notification", + "notifier", + "php", + "wrapper" + ], + "support": { + "issues": "https://github.com/nunomaduro/laravel-desktop-notifier/issues", + "source": "https://github.com/nunomaduro/laravel-desktop-notifier/tree/v2.6.0" + }, + "time": "2022-01-13T15:10:14+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2021-12-04T23:24:31+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a", + "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8", + "symfony/polyfill-php81": "^1.23" + }, + "require-dev": { + "captainhook/captainhook": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "ergebnis/composer-normalize": "^2.6", + "fakerphp/faker": "^1.5", + "hamcrest/hamcrest-php": "^2", + "jangregor/phpstan-prophecy": "^0.8", + "mockery/mockery": "^1.3", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^0.12.32", + "phpstan/phpstan-mockery": "^0.12.5", + "phpstan/phpstan-phpunit": "^0.12.11", + "phpunit/phpunit": "^8.5 || ^9", + "psy/psysh": "^0.10.4", + "slevomat/coding-standard": "^6.3", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/1.2.2" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2021-10-10T03:01:02+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.3.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", + "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", + "shasum": "" + }, + "require": { + "brick/math": "^0.8 || ^0.9", + "ext-ctype": "*", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "moontoast/math": "^1.1", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-mockery": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^8.5 || ^9", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-ctype": "Enables faster processing of character classification using ctype functions.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.3.1" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2022-03-27T21:42:02+00:00" + }, + { + "name": "spatie/yaml-front-matter", + "version": "2.0.7", + "source": { + "type": "git", + "url": "https://github.com/spatie/yaml-front-matter.git", + "reference": "f49f228994de70827ca857efffdd3bd7703aea34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/f49f228994de70827ca857efffdd3bd7703aea34", + "reference": "f49f228994de70827ca857efffdd3bd7703aea34", + "shasum": "" + }, + "require": { + "php": "^7.0|^8.0", + "symfony/yaml": "^3.0|^4.0|^5.0|^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\YamlFrontMatter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A to the point yaml front matter parser", + "homepage": "https://github.com/sebastiandedeyne/yaml-front-matter", + "keywords": [ + "front matter", + "jekyll", + "spatie", + "yaml" + ], + "support": { + "source": "https://github.com/spatie/yaml-front-matter/tree/2.0.7" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-04-06T12:03:55+00:00" + }, + { + "name": "symfony/console", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "9b190bc7a19d19add1dbb3382721973836e59b50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/9b190bc7a19d19add1dbb3382721973836e59b50", + "reference": "9b190bc7a19d19add1dbb3382721973836e59b50", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-27T06:40:13+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "732ca203b3222cde3378d5ddf5e2883211acc53e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/732ca203b3222cde3378d5ddf5e2883211acc53e", + "reference": "732ca203b3222cde3378d5ddf5e2883211acc53e", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.0" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-23T10:32:42+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "5c85b58422865d42c6eb46f7693339056db098a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/5c85b58422865d42c6eb46f7693339056db098a8", + "reference": "5c85b58422865d42c6eb46f7693339056db098a8", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/event-dispatcher-contracts": "^2|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^5.4|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-05T16:45:52+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", + "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "af7edab28d17caecd1f40a9219fc646ae751c21f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/af7edab28d17caecd1f40a9219fc646ae751c21f", + "reference": "af7edab28d17caecd1f40a9219fc646ae751c21f", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-15T08:07:58+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "05abe9aab47decfd793632787d0c6a25268e2a5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/05abe9aab47decfd793632787d0c6a25268e2a5b", + "reference": "05abe9aab47decfd793632787d0c6a25268e2a5b", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/cache": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0" + }, + "suggest": { + "symfony/mime": "To use the file extension guesser" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-21T13:33:31+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "e78407f2a7b683fd1269057aa39355d77ddbcff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/e78407f2a7b683fd1269057aa39355d77ddbcff9", + "reference": "e78407f2a7b683fd1269057aa39355d77ddbcff9", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "psr/log": "^1|^2|^3", + "symfony/error-handler": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.4", + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<5.4", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/css-selector": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/dom-crawler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/process": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-27T07:14:30+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "e17bae63d437b3e21942dcc47ccca802d3573dd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/e17bae63d437b3e21942dcc47ccca802d3573dd8", + "reference": "e17bae63d437b3e21942dcc47ccca802d3573dd8", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-21T13:33:31+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "433d05519ce6990bf3530fba6957499d327395c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", + "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8", + "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/bf44a9fd41feaac72b074de600314a93e2ae78e2", + "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-10T07:21:04+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/process", + "version": "v6.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "d074154ea8b1443a96391f6e39f9e547b2dd01b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/d074154ea8b1443a96391f6e39f9e547b2dd01b9", + "reference": "d074154ea8b1443a96391f6e39f9e547b2dd01b9", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-12T16:11:42+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e517458f278c2131ca9f262f8fbaf01410f2c65c", + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-03-13T20:10:05+00:00" + }, + { + "name": "symfony/string", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "df9f03d595aa2d446498ba92fe803a519b2c43cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/df9f03d595aa2d446498ba92fe803a519b2c43cc", + "reference": "df9f03d595aa2d446498ba92fe803a519b2c43cc", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-22T08:18:02+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "9ba011309943955a3807b8236c17cff3b88f67b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/9ba011309943955a3807b8236c17cff3b88f67b6", + "reference": "9ba011309943955a3807b8236c17cff3b88f67b6", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.3|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-06T14:27:17+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9", + "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "ac81072464221e73ee994d12f0b8a2af4a9ed798" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/ac81072464221e73ee994d12f0b8a2af4a9ed798", + "reference": "ac81072464221e73ee994d12f0b8a2af4a9ed798", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-21T13:33:31+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "e77f3ea0b21141d771d4a5655faa54f692b34af5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e77f3ea0b21141d771d4a5655faa54f692b34af5", + "reference": "e77f3ea0b21141d771d4a5655faa54f692b34af5", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-26T17:23:29+00:00" + }, + { + "name": "torchlight/torchlight-commonmark", + "version": "v0.5.5", + "source": { + "type": "git", + "url": "https://github.com/torchlight-api/torchlight-commonmark-php.git", + "reference": "eb618ae6187090126a9ef881ccaf9c315d49c99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/torchlight-api/torchlight-commonmark-php/zipball/eb618ae6187090126a9ef881ccaf9c315d49c99b", + "reference": "eb618ae6187090126a9ef881ccaf9c315d49c99b", + "shasum": "" + }, + "require": { + "league/commonmark": "^1.5|^2.0", + "php": "^7.2|^8.0", + "torchlight/torchlight-laravel": "^0.5.10" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "orchestra/testbench": "^5.0|^6.0", + "phpunit/phpunit": "^8.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Torchlight\\Commonmark\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Francis", + "email": "aaron@hammerstone.dev" + } + ], + "description": "A Commonmark extension for Torchlight, the syntax highlighting API.", + "homepage": "https://torchlight.dev", + "keywords": [ + "Code highlighting", + "commonmark", + "laravel", + "markdown", + "syntax highlighting" + ], + "support": { + "issues": "https://github.com/torchlight-api/torchlight-commonmark-php/issues", + "source": "https://github.com/torchlight-api/torchlight-commonmark-php/tree/v0.5.5" + }, + "time": "2022-02-23T17:09:44+00:00" + }, + { + "name": "torchlight/torchlight-laravel", + "version": "v0.5.11", + "source": { + "type": "git", + "url": "https://github.com/torchlight-api/torchlight-laravel.git", + "reference": "e69530e720d09f6791eff2a13e4d408ba0eaf6dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/torchlight-api/torchlight-laravel/zipball/e69530e720d09f6791eff2a13e4d408ba0eaf6dc", + "reference": "e69530e720d09f6791eff2a13e4d408ba0eaf6dc", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.2", + "illuminate/cache": "^7.26.0|^8.0|^9.0", + "illuminate/console": "^7.26.0|^8.0|^9.0", + "illuminate/http": "^7.26.0|^8.0|^9.0", + "illuminate/support": "^7.26.0|^8.0|^9.0", + "illuminate/view": "^7.26.0|^8.0|^9.0", + "php": "^7.3|^8.0", + "ramsey/uuid": "^3.7|^4.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "orchestra/testbench": "^5.0|^6.0|^7.0", + "phpunit/phpunit": "^8.5.23|^9.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Torchlight\\TorchlightServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Torchlight\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Francis", + "email": "aaron@hammerstone.dev" + } + ], + "description": "A Laravel Client for Torchlight, the syntax highlighting API.", + "homepage": "https://torchlight.dev", + "keywords": [ + "Code highlighting", + "laravel", + "syntax highlighting" + ], + "support": { + "issues": "https://github.com/torchlight-api/torchlight-laravel/issues", + "source": "https://github.com/torchlight-api/torchlight-laravel/tree/v0.5.11" + }, + "time": "2022-02-13T15:30:32+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.4.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2021-12-12T23:22:04+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b56450eed252f6801410d810c8e1727224ae0743" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2022-03-08T17:03:00+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-03-03T08:28:38+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.14.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", + "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" + }, + "time": "2022-05-31T20:59:12+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "77a32518733312af16a44300404e945338981de3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + }, + "time": "2022-03-15T21:29:03+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + }, + "time": "2021-12-08T12:19:24+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.15", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.13.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-03-07T09:28:20+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:49:45+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-03T09:37:03+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-11-11T14:18:36+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-03-15T09:54:48+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^8.0" + }, + "platform-dev": [], + "plugin-api-version": "2.1.0" +} diff --git a/packages/framework/config/app.php b/packages/framework/config/app.php new file mode 100644 index 00000000000..efdeb3e5221 --- /dev/null +++ b/packages/framework/config/app.php @@ -0,0 +1,80 @@ +<?php + +/* +|-------------------------------------------------------------------------- +| Application Config +|-------------------------------------------------------------------------- +| +| This configuration file is for the Hyde Core, which is useful if you +| want to contribute to the source code. However, if you are looking +| to customize your static site, you are probably looking for the +| Hyde config located in `config/hyde.php`! +| +*/ +return [ + + /* + |-------------------------------------------------------------------------- + | Application Name + |-------------------------------------------------------------------------- + | + | This value is the name of your application. This value is used when the + | framework needs to place the application's name in a notification or + | any other location as required by the application or its packages. + | + */ + + 'name' => config('hyde.name', 'HydePHP'), + + /* + |-------------------------------------------------------------------------- + | Application Version + |-------------------------------------------------------------------------- + | + | This value determines the "version" your application is currently running + | in. You may want to follow the "Semantic Versioning" - Given a version + | number MAJOR.MINOR.PATCH when an update happens: https://semver.org. + | + */ + + 'version' => Hyde\Framework\Hyde::version(), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. This can be overridden using + | the global command line "--env" option when calling commands. + | + | When using Hyde this setting should always be set to `production`. + | However, when developing the Hyde Core, set it to `development` + | in your .env to unlock the development commands. + | + */ + + 'env' => env('ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => [ + App\Providers\AppServiceProvider::class, + Hyde\Framework\HydeServiceProvider::class, + ], + + 'aliases' => [ + 'Hyde' => Hyde\Framework\Hyde::class, + ], + +]; diff --git a/packages/framework/config/cache.php b/packages/framework/config/cache.php new file mode 100644 index 00000000000..0ddcdd43c05 --- /dev/null +++ b/packages/framework/config/cache.php @@ -0,0 +1,12 @@ +<?php + +return [ + 'default' => 'file', + + 'stores' => [ + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + ], + ], +]; diff --git a/packages/framework/config/commands.php b/packages/framework/config/commands.php new file mode 100644 index 00000000000..2b45dcc1be5 --- /dev/null +++ b/packages/framework/config/commands.php @@ -0,0 +1,83 @@ +<?php + +return [ + + /* + |-------------------------------------------------------------------------- + | Default Command + |-------------------------------------------------------------------------- + | + | Laravel Zero will always run the command specified below when no command name is + | provided. Consider update the default command for single command applications. + | You cannot pass arguments to the default command because they are ignored. + | + */ + + 'default' => NunoMaduro\LaravelConsoleSummary\SummaryCommand::class, + + /* + |-------------------------------------------------------------------------- + | Commands Paths + |-------------------------------------------------------------------------- + | + | This value determines the "paths" that should be loaded by the console's + | kernel. Foreach "path" present on the array provided below the kernel + | will extract all "Illuminate\Console\Command" based class commands. + | + */ + + 'paths' => [app_path('Commands')], + + /* + |-------------------------------------------------------------------------- + | Added Commands + |-------------------------------------------------------------------------- + | + | You may want to include a single command class without having to load an + | entire folder. Here you can specify which commands should be added to + | your list of commands. The console's kernel will try to load them. + | + */ + + 'add' => [ + // + ], + + /* + |-------------------------------------------------------------------------- + | Hidden Commands + |-------------------------------------------------------------------------- + | + | Your application commands will always be visible on the application list + | of commands. But you can still make them "hidden" specifying an array + | of commands below. All "hidden" commands can still be run/executed. + | + */ + + 'hidden' => [ + NunoMaduro\LaravelConsoleSummary\SummaryCommand::class, + Symfony\Component\Console\Command\DumpCompletionCommand::class, + Symfony\Component\Console\Command\HelpCommand::class, + Hyde\Framework\Commands\HydeDebugCommand::class, + ], + + /* + |-------------------------------------------------------------------------- + | Removed Commands + |-------------------------------------------------------------------------- + | + | Do you have a service provider that loads a list of commands that + | you don't need? No problem. Laravel Zero allows you to specify + | below a list of commands that you don't to see in your app. + | + */ + + 'remove' => [ + Illuminate\Console\Scheduling\ScheduleRunCommand::class, + Illuminate\Console\Scheduling\ScheduleListCommand::class, + Illuminate\Console\Scheduling\ScheduleFinishCommand::class, + Illuminate\Console\Scheduling\ScheduleRunCommand::class, + LaravelZero\Framework\Commands\StubPublishCommand::class, + ], + +]; diff --git a/packages/framework/config/docs.php b/packages/framework/config/docs.php new file mode 100644 index 00000000000..cb00d53f4c0 --- /dev/null +++ b/packages/framework/config/docs.php @@ -0,0 +1,105 @@ +<?php + +/* +|-------------------------------------------------------------------------- +| Documentation Module Settings +|-------------------------------------------------------------------------- +| +| Since the Hyde documentation module has many configuration options, +| they have now been broken out into their own configuration file. +| +*/ + +return [ + /* + |-------------------------------------------------------------------------- + | Sidebar Header Name + |-------------------------------------------------------------------------- + | + | By default, the sidebar title shown in the documentation page layouts uses + | the app name suffixed with "docs". You can change it with this setting. + | + */ + + 'header_title' => config('hyde.name', 'HydePHP').' Docs', + + /* + |-------------------------------------------------------------------------- + | Documentation Site Output Directory + |-------------------------------------------------------------------------- + | + | If you want to store the compiled documentation pages in a different + | directory than the default 'docs' directory, for example to set the + | specified version, you can specify the directory here. + | + | Note that you need to take care as to not set it to something that + | may conflict with other parts, such as media or posts directories. + | + | The default value is 'docs'. For easy versioning you can do what + | HydePHP.com does, setting it to 'docs/master'. + | + */ + + 'output_directory' => 'docs', + + /* + |-------------------------------------------------------------------------- + | Collaborative Source Editing Location + |-------------------------------------------------------------------------- + | + | @see https://hydephp.com/docs/master/documentation-pages#automatic-edit-page-button + | + | By adding a base URL here, Hyde will use it to create "edit source" links + | to your documentation pages. Hyde expects this to be a GitHub path, but + | it will probably work with other methods as well, if not, send a PR! + | + | You can also change the link text with the `edit_source_link_text` setting. + | + | Example: https://github.com/hydephp/docs/blob/master + | Do not specify the filename or extension, Hyde will do that for you. + | Setting the setting to null will disable the feature. + | + */ + + // 'source_file_location_base' => 'https://github.com/<user>/<repo>/<[blob/edit]>/<branch>', + 'edit_source_link_text' => 'Edit Source', + 'edit_source_link_position' => 'footer', // 'header', 'footer', or 'both' + + /* + |-------------------------------------------------------------------------- + | Sidebar Page Order + |-------------------------------------------------------------------------- + | + | In the generated Documentation pages the navigation links in the sidebar + | are sorted alphabetically by default. As this rarely makes sense, you + | can reorder the page slugs in the list and the links will be sorted + | in that order. Link items without an entry here will have fall + | back to the default priority of 999, putting them last. + | + | You can also set explicit priorities in front matter. + | + */ + + 'sidebar_order' => [ + 'readme', + 'installation', + 'getting-started', + ], + + /* + |-------------------------------------------------------------------------- + | Table of Contents Settings + |-------------------------------------------------------------------------- + | + | The Hyde Documentation Module comes with a fancy Sidebar that, by default, + | has a Table of Contents included. Here, you can configure its behavior, + | content, look and feel. You can also disable the feature completely. + | + */ + + 'table_of_contents' => [ + 'enabled' => true, + 'min_heading_level' => 2, + 'max_heading_level' => 4, + ], +]; diff --git a/packages/framework/config/hyde.php b/packages/framework/config/hyde.php new file mode 100644 index 00000000000..9df62fc99d4 --- /dev/null +++ b/packages/framework/config/hyde.php @@ -0,0 +1,238 @@ +<?php + +/* +|-------------------------------------------------------------------------- +| __ __ __ ___ __ _____ +| / // /_ _____/ /__ / _ \/ // / _ \ +| / _ / // / _ / -_) ___/ _ / ___/ +| /_//_/\_, /\_,_/\__/_/ /_//_/_/ +| /___/ +|-------------------------------------------------------------------------- +| +| Welcome to HydePHP! In this file, you can customize your new Static Site! +| +| HydePHP favours convention over configuration and as such requires virtually +| no configuration out of the box to get started. Though, you may want to +| change the options to personalize your site and make it your own! +| +*/ + +use Hyde\Framework\Helpers\Author; +use Hyde\Framework\Helpers\Features; +use Hyde\Framework\Helpers\Meta; + +return [ + + /* + |-------------------------------------------------------------------------- + | Site Name + |-------------------------------------------------------------------------- + | + | This value sets the name of your site and is, for example, used in + | the compiled page titles and more. The default value is HydePHP. + | + | The name is stored in the $siteName variable so it can be + | used again later on in this config. + | + */ + + 'name' => $siteName = env('SITE_NAME', 'HydePHP'), + + /* + |-------------------------------------------------------------------------- + | Site URL Configuration + |-------------------------------------------------------------------------- + | + | Here are some configuration options for URL generation. + | + | A site_url is required to use sitemaps and RSS feeds. + | + | `site_url` is used to create canonical URLs and permalinks. + | `prettyUrls` will when enabled create links that do not end in .html. + | `generateSitemap` determines if a sitemap.xml file should be generated. + | + | To see the full documentation, please visit the (temporary link) below. + | https://github.com/hydephp/framework/wiki/Documentation-Page-Drafts + | + | + */ + + 'site_url' => env('SITE_URL', null), + + 'pretty_urls' => false, + + 'generate_sitemap' => true, + + /* + |-------------------------------------------------------------------------- + | Site Language + |-------------------------------------------------------------------------- + | + | This value sets the language of your site and is used for the + | <html lang=""> element in the app layout. Default is 'en'. + | + */ + + 'language' => 'en', + + /* + |-------------------------------------------------------------------------- + | Global Site Meta Tags + |-------------------------------------------------------------------------- + | + | While you can add any number of meta tags in the meta.blade.php component + | using standard HTML, you can also use the Meta helper. To add a regular + | meta tag, use Meta::name() helper. To add an Open Graph property, use + | Meta::property() helper which also adds the `og:` prefix for you. + | + | Please note that some pages like blog posts contain dynamic meta tags + | which may override these globals when present in the front matter. + | + */ + + 'meta' => [ + // Meta::name('author', 'Mr. Hyde'), + // Meta::name('twitter:creator', '@hyde_php'), + // Meta::name('description', 'My Hyde Blog'), + // Meta::name('keywords', 'Static Sites, Blogs, Documentation'), + Meta::name('generator', 'HydePHP '.Hyde\Framework\Hyde::version()), + Meta::property('site_name', $siteName), + ], + + /* + |-------------------------------------------------------------------------- + | Features + |-------------------------------------------------------------------------- + | + | Some of Hyde's features are optional. Feel free to disable the features + | you don't need by removing or commenting them out from this array. + | This config concept is directly inspired by Laravel Jetstream. + | + */ + + 'features' => [ + // Page Modules + Features::blogPosts(), + Features::bladePages(), + Features::markdownPages(), + Features::documentationPages(), + + // Frontend Features + Features::darkmode(), + Features::documentationSearch(), + + // Integrations + Features::torchlight(), + ], + + /* + |-------------------------------------------------------------------------- + | Blog Post Authors + |-------------------------------------------------------------------------- + | + | Hyde has support for adding authors in front matter, for example to + | automatically add a link to your website or social media profiles. + | However, it's tedious to have to add those to each and every + | post you make, and keeping them updated is even harder. + | + | Here you can add predefined authors. When writing posts, + | just specify the username in the front matter, and the + | rest of the data will be pulled from a matching entry. + | + */ + + 'authors' => [ + Author::create( + username: 'mr_hyde', // Required username + name: 'Mr. Hyde', // Optional display name + website: 'https://hydephp.com' // Optional website URL + ), + ], + + /* + |-------------------------------------------------------------------------- + | Footer Text + |-------------------------------------------------------------------------- + | + | Most websites have a footer with copyright details and contact information. + | You probably want to change the Markdown to include your information, + | though you are of course welcome to keep the attribution link! + | + | You can also customize the blade view if you want a more complex footer. + | You can disable it completely by setting `enabled` to `false`. + | + */ + + 'footer' => [ + 'enabled' => true, + 'markdown' => 'Site proudly built with [HydePHP](https://github.com/hydephp/hyde) 🎩', + ], + + /* + |-------------------------------------------------------------------------- + | Custom Navigation Menu Links + |-------------------------------------------------------------------------- + | + | If you are looking to add custom navigation menu links, this is the place! + | + | Linking to an external site? Supply the full URI to the 'destination'. + | Keeping it internal? Pass the 'slug' relative to the document root. + | + | To get started quickly, you can uncomment the defaults here. + | Tip: Only the title and slug parameters are required. + | + */ + + 'navigation_menu_links' => [ + // [ + // 'title' => 'GitHub', + // 'destination' => 'https://github.com/hydephp/hyde', + // 'priority' => 1200, + // ], + // [ + // 'title' => 'Featured Blog Post', + // 'slug' => 'posts/hello-world', + // ] + ], + + /* + |-------------------------------------------------------------------------- + | Navigation Menu Blacklist + |-------------------------------------------------------------------------- + | There may be pages you want to exclude from the automatic navigation menu, + | such as error pages. Add their slugs here and they will not be included. + | + */ + + 'navigation_menu_blacklist' => [ + '404', + ], + + /* + |-------------------------------------------------------------------------- + | Site Output Directory (Experimental 🧪) + |-------------------------------------------------------------------------- + | + | If you want to store your compiled website in a different directory than + | the default `_pages`, you can change the path here. The Hyde::path() + | helper ensures the path is relative to your Hyde project. While + | you can set the path to an absolute path outside the project, + | this is not officially supported and may be unstable. + | + */ + + 'site_output_path' => Hyde\Framework\Hyde::path('_site'), + + /* + |-------------------------------------------------------------------------- + | Warn about outdated config? + |-------------------------------------------------------------------------- + | + | If your config needs updating, a message will be shown in the + | HydeCLI info screen, unless disabled below. + | + */ + + 'warn_about_outdated_config' => true, + +]; diff --git a/packages/framework/config/logo.php b/packages/framework/config/logo.php new file mode 100644 index 00000000000..32c9672668d --- /dev/null +++ b/packages/framework/config/logo.php @@ -0,0 +1,85 @@ +<?php + +return [ + + /* + |-------------------------------------------------------------------------- + | Enabled + |-------------------------------------------------------------------------- + | + | This value determines if the app name should be represented as an + | ASCII logo. This file provides a sane default location for all + | information concerning the logo and is display customization. + | + */ + + 'enabled' => true, + + /* + |-------------------------------------------------------------------------- + | Logo Name + |-------------------------------------------------------------------------- + | + | This value determines the text that is rendered for the logo. + | It defaults to the app name, but it can be any other text + | value if the logo should be different to the app name. + | + */ + 'name' => ' '.config('app.name'), + + /* + |-------------------------------------------------------------------------- + | Default Font + |-------------------------------------------------------------------------- + | + | This option defines the font which should be used for rendering. + | By default, one default font is shipped. However, you are free + | to download and use additional fonts: http://www.figlet.org. + | + */ + + // 'font' => \LaravelZero\Framework\Components\Logo\FigletString::DEFAULT_FONT, + 'font' => base_path('vendor/hyde/framework/resources/fonts/smslant.flf'), + + /* + |-------------------------------------------------------------------------- + | Output Width + |-------------------------------------------------------------------------- + | + | This option defines the maximum width of the output string. This is + | used for word-wrap as well as justification. Be careful when using + | small values, because they may result in an undefined behavior. + | + */ + + 'outputWidth' => 80, + + /* + |-------------------------------------------------------------------------- + | Justification + |-------------------------------------------------------------------------- + | + | This option defines the justification of the logo text. By default, + | justification is provided, which will work well on most of your + | console apps. Of course, you are free to change this value. + | + */ + + 'justification' => null, + + /* + |-------------------------------------------------------------------------- + | Right To Left + |-------------------------------------------------------------------------- + | + | This option defines the option in which the text is written. By, default + | the setting of the font-file is used. When justification is not defined, + | a text written from right-to-left is automatically right-aligned. + | + | Possible values: "right-to-left", "left-to-right", null + | + */ + + 'rightToLeft' => null, + +]; diff --git a/packages/framework/config/markdown.php b/packages/framework/config/markdown.php new file mode 100644 index 00000000000..508ea3cb897 --- /dev/null +++ b/packages/framework/config/markdown.php @@ -0,0 +1,69 @@ +<?php + +/* +|-------------------------------------------------------------------------- +| Markdown Configuration +|-------------------------------------------------------------------------- +| +| HydePHP makes heavy use of Markdown. In this file you can configure +| Markdown related services, as well as change the extensions used. +| +*/ + +return [ + + /* + |-------------------------------------------------------------------------- + | Markdown Extensions + |-------------------------------------------------------------------------- + | + | Define any extra extensions that should be loaded into the CommonMark + | converter. Should be fully qualified class names to the extension. + | + | Remember that you may need to install any third party extensions + | through Composer before you can use them. + | + | Hyde ships with the Github Flavored Markdown extension. + | The Torchlight extension is enabled automatically when needed. + | + */ + + 'extensions' => [ + \League\CommonMark\Extension\GithubFlavoredMarkdownExtension::class, + \League\CommonMark\Extension\Attributes\AttributesExtension::class, + ], + + /* + |-------------------------------------------------------------------------- + | Configuration Options + |-------------------------------------------------------------------------- + | + | Define any options that should be passed to the CommonMark converter. + | + | Hyde handles many of the options automatically, but you may want to + | override some of them and/or add your own. Any custom options + | will be merged with the Hyde defaults during runtime. + | + */ + + 'config' => [ + // + ], + + /* + |-------------------------------------------------------------------------- + | Blade-supported Markdown + |-------------------------------------------------------------------------- + | + | Since Hyde v0.30.x you can use Laravel Blade in Markdown files. + | + | It's disabled by default since can be a security risk as it allows + | arbitrary PHP to run. But if your Markdown is trusted, try it out! + | + | To see the syntax and usage, see the documentation: + | @see https://hydephp.com/docs/master/advanced-markdown#blade-support + | + */ + + 'enable_blade' => false, +]; diff --git a/packages/framework/config/torchlight.php b/packages/framework/config/torchlight.php new file mode 100644 index 00000000000..668c6be8bca --- /dev/null +++ b/packages/framework/config/torchlight.php @@ -0,0 +1,93 @@ +<?php + + /* +|-------------------------------------------------------------------------- +| Torchlight +|-------------------------------------------------------------------------- +| +| Hyde comes with built-in support for Torchlight. +| +| All you need to do is supply your API token in the TORCHLIGHT_TOKEN .env +| variable and the extension will be enabled automatically for you. +| This config also comes with an extra option to automatically +| inject an attribution badge on pages that use Torchlight. +| +| @see https://torchlight.dev/ +| +*/ +return [ + // Should an attribution badge be automatically injected, + // and if so, what (Markdown) text should be used? + 'attribution' => [ + 'enabled' => true, + 'markdown' => ' +<p> + <i> + Syntax highlighting by <a href="https://torchlight.dev/" rel="noopener nofollow">Torchlight.dev</a> + </i> +</p>', + ], + + // The Torchlight client caches highlighted code blocks. Here + // you can define which cache driver you'd like to use. If + // leave this blank your default app cache will be used. + 'cache' => env('TORCHLIGHT_CACHE_DRIVER'), + + // Cache blocks for 30 days. + 'cache_seconds' => env('TORCHLIGHT_CACHE_TTL', 60 * 60 * 24 * 30), + + // Which theme you want to use. You can find all the themes at + // https://torchlight.dev/docs/themes. + 'theme' => env('TORCHLIGHT_THEME', 'material-theme-palenight'), + + // If you want to use two separate themes for dark and light modes, + // you can use an array to define both themes. Torchlight renders + // both on the page, and you will be responsible for hiding one + // or the other depending on the dark / light mode via CSS. + // 'theme' => [ + // 'dark' => 'github-dark', + // 'light' => 'github-light', + // ], + + // Your API token from torchlight.dev. + 'token' => env('TORCHLIGHT_TOKEN'), + + // If you want to register the blade directives, set this to true. + 'blade_components' => true, + + // The Host of the API. + 'host' => env('TORCHLIGHT_HOST', 'https://api.torchlight.dev'), + + // We replace tabs in your code blocks with spaces in HTML. Set + // the number of spaces you'd like to use per tab. Set to + // `false` to leave literal tabs in the HTML. + 'tab_width' => 4, + + // If you pass a filename to the code component or in a markdown + // block, Torchlight will look for code snippets in the + // following directories. + 'snippet_directories' => [ + resource_path(), + ], + + // Global options to control blocks-level settings. + // https://torchlight.dev/docs/options + 'options' => [ + // Turn line numbers on or off globally. + // 'lineNumbers' => false, + + // Control the `style` attribute applied to line numbers. + // 'lineNumbersStyle' => '', + + // Turn on +/- diff indicators. + // 'diffIndicators' => true, + + // If there are any diff indicators for a line, put them + // in place of the line number to save horizontal space. + // 'diffIndicatorsInPlaceOfLineNumbers' => true, + + // When lines are collapsed, this is the text that will + // be shown to indicate that they can be expanded. + // 'summaryCollapsedIndicator' => '...', + ], +]; diff --git a/packages/framework/config/view.php b/packages/framework/config/view.php new file mode 100644 index 00000000000..569cc2797aa --- /dev/null +++ b/packages/framework/config/view.php @@ -0,0 +1,13 @@ +<?php + +return [ + 'paths' => [ + resource_path('views'), + base_path('_pages'), + ], + + 'compiled' => env( + 'VIEW_COMPILED_PATH', + realpath(storage_path('framework/views')) + ), +]; diff --git a/packages/framework/resources/fonts/smslant.flf b/packages/framework/resources/fonts/smslant.flf new file mode 100644 index 00000000000..9d19838313f --- /dev/null +++ b/packages/framework/resources/fonts/smslant.flf @@ -0,0 +1,1097 @@ +flf2a$ 5 4 14 15 10 0 22415 +SmSlant by Glenn Chappell 6/93 - based on Small & Slant +Includes ISO Latin-1 +figlet release 2.1 -- 12 Aug 1994 +Permission is hereby given to modify this font, as long as the +modifier's name is placed on a comment line. + +Modified by Paul Burton <solution@earthlink.net> 12/96 to include new parameter +supported by FIGlet and FIGWin. May also be slightly modified for better use +of new full-width/kern/smush alternatives, but default output is NOT changed. + + $@ + $ @ + $ @ + $ @ +$ @@ + __@ + / /@ + /_/ @ +(_) @ + @@ + _ _ @ +( | )@ +|/|/ @ +$ @ + @@ + ____ @ + __/ / /_@ + /_ . __/@ +/_ __/ @ + /_/_/ @@ + @ + _//@ + (_-<@ +/ __/@ +// @@ + _ __@ +(_)_/_/@ + _/_/_ @ +/_/ (_)@ + @@ + ____ @ + / __/___@ + > _/_ _/@ +|_____/ @ + @@ + _ @ +( )@ +|/ @ +$ @ + @@ + __@ + _/_/@ + / / @ +/ / @ +|_| @@ + _ @ + | |@ + / /@ + _/_/ @ +/_/ @@ + @ + _/|@ +> _<@ +|/ @ + @@ + __ @ + __/ /_@ +/_ __/@ + /_/ @ + @@ + @ + @ + _ @ +( )@ +|/ @@ + @ + ____@ +/___/@ + $ @ + @@ + @ + @ + _ @ +(_)@ + @@ + __@ + _/_/@ + _/_/ @ +/_/ @ + @@ + ___ @ + / _ \@ +/ // /@ +\___/ @ + @@ + ___@ + < /@ + / / @ +/_/ @ + @@ + ___ @ + |_ |@ + / __/ @ +/____/ @ + @@ + ____@ + |_ /@ + _/_ < @ +/____/ @ + @@ + ____@ + / / /@ +/_ _/@ + /_/ @ + @@ + ____@ + / __/@ + /__ \ @ +/____/ @ + @@ + ____@ + / __/@ +/ _ \ @ +\___/ @ + @@ + ____@ +/_ /@ + / / @ +/_/ @ + @@ + ___ @ + ( _ )@ +/ _ |@ +\___/ @ + @@ + ___ @ + / _ \@ + \_, /@ +/___/ @ + @@ + _ @ + (_)@ + _ @ +(_) @ + @@ + _ @ + (_)@ + _ @ +( ) @ +|/ @@ + __@ + / /@ +< < @ + \_\@ + @@ + @ + ____@ + /___/@ +/___/ @ + @@ +__ @ +\ \ @ + > >@ +/_/ @ + @@ + ___ @ +/__ \@ + /__/@ +(_) @ + @@ + _____ @ + / ___ \@ +/ / _ `/@ +\ \_,_/ @ + \___/ @@ + ___ @ + / _ |@ + / __ |@ +/_/ |_|@ + @@ + ___ @ + / _ )@ + / _ |@ +/____/ @ + @@ + _____@ + / ___/@ +/ /__ @ +\___/ @ + @@ + ___ @ + / _ \@ + / // /@ +/____/ @ + @@ + ____@ + / __/@ + / _/ @ +/___/ @ + @@ + ____@ + / __/@ + / _/ @ +/_/ @ + @@ + _____@ + / ___/@ +/ (_ / @ +\___/ @ + @@ + __ __@ + / // /@ + / _ / @ +/_//_/ @ + @@ + ____@ + / _/@ + _/ / @ +/___/ @ + @@ + __@ + __ / /@ +/ // / @ +\___/ @ + @@ + __ __@ + / //_/@ + / ,< @ +/_/|_| @ + @@ + __ @ + / / @ + / /__@ +/____/@ + @@ + __ ___@ + / |/ /@ + / /|_/ / @ +/_/ /_/ @ + @@ + _ __@ + / |/ /@ + / / @ +/_/|_/ @ + @@ + ____ @ + / __ \@ +/ /_/ /@ +\____/ @ + @@ + ___ @ + / _ \@ + / ___/@ +/_/ @ + @@ + ____ @ + / __ \@ +/ /_/ /@ +\___\_\@ + @@ + ___ @ + / _ \@ + / , _/@ +/_/|_| @ + @@ + ____@ + / __/@ + _\ \ @ +/___/ @ + @@ + ______@ +/_ __/@ + / / @ +/_/ @ + @@ + __ __@ + / / / /@ +/ /_/ / @ +\____/ @ + @@ + _ __@ + | | / /@ + | |/ / @ + |___/ @ + @@ + _ __@ + | | /| / /@ + | |/ |/ / @ + |__/|__/ @ + @@ + _ __@ + | |/_/@ + _> < @ +/_/|_| @ + @@ + __ __@ + \ \/ /@ + \ / @ + /_/ @ + @@ + ____@ + /_ /@ + / /_@ + /___/@ + @@ + ___@ + / _/@ + / / @ + / / @ +/__/ @@ +__ @ +\ \ @ + \ \ @ + \_\@ + @@ + ___@ + / /@ + / / @ + _/ / @ +/__/ @@ + //|@ +|/||@ + $ @ +$ @ + @@ + @ + @ + @ + ____@ +/___/@@ + _ @ +( )@ + V @ +$ @ + @@ + @ + ___ _@ +/ _ `/@ +\_,_/ @ + @@ + __ @ + / / @ + / _ \@ +/_.__/@ + @@ + @ + ____@ +/ __/@ +\__/ @ + @@ + __@ + ___/ /@ +/ _ / @ +\_,_/ @ + @@ + @ + ___ @ +/ -_)@ +\__/ @ + @@ + ___@ + / _/@ + / _/ @ +/_/ @ + @@ + @ + ___ _@ + / _ `/@ + \_, / @ +/___/ @@ + __ @ + / / @ + / _ \@ +/_//_/@ + @@ + _ @ + (_)@ + / / @ +/_/ @ + @@ + _ @ + (_)@ + / / @ + __/ / @ +|___/ @@ + __ @ + / /__@ + / '_/@ +/_/\_\ @ + @@ + __@ + / /@ + / / @ +/_/ @ + @@ + @ + __ _ @ + / ' \@ +/_/_/_/@ + @@ + @ + ___ @ + / _ \@ +/_//_/@ + @@ + @ + ___ @ +/ _ \@ +\___/@ + @@ + @ + ___ @ + / _ \@ + / .__/@ +/_/ @@ + @ + ___ _@ +/ _ `/@ +\_, / @ + /_/ @@ + @ + ____@ + / __/@ +/_/ @ + @@ + @ + ___@ + (_-<@ +/___/@ + @@ + __ @ + / /_@ +/ __/@ +\__/ @ + @@ + @ + __ __@ +/ // /@ +\_,_/ @ + @@ + @ + _ __@ +| |/ /@ +|___/ @ + @@ + @ + _ __@ +| |/|/ /@ +|__,__/ @ + @@ + @ + __ __@ + \ \ /@ +/_\_\ @ + @@ + @ + __ __@ + / // /@ + \_, / @ +/___/ @@ + @ + ___@ +/_ /@ +/__/@ + @@ + __@ + _/_/@ +_/ / @ +/ / @ +\_\ @@ + __@ + / /@ + / / @ + / / @ +/_/ @@ + __ @ + \ \ @ + / /_@ + _/_/ @ +/_/ @@ + /\//@ +//\/ @ + $ @ +$ @ + @@ + _ _ @ + (_)(_)@ + / - | @ +/_/|_| @ + @@ + _ _ @ + (_)_(_)@ +/ __ \ @ +\____/ @ + @@ + _ _ @ + (_) (_)@ +/ /_/ / @ +\____/ @ + @@ + _ _ @ + (_)(_)@ +/ _ `/ @ +\_,_/ @ + @@ + _ _ @ + (_)(_)@ +/ _ \ @ +\___/ @ + @@ + _ _ @ + (_)(_)@ +/ // / @ +\_,_/ @ + @@ + ____ @ + / _ )@ + / /< < @ + / //__/ @ +/_/ @@ +160 NO-BREAK SPACE + $@ + $ @ + $ @ + $ @ +$ @@ +161 INVERTED EXCLAMATION MARK + _ @ + (_)@ + / / @ +/_/ @ + @@ +162 CENT SIGN + @ + __//@ +/ __/@ +\ _/ @ +// @@ +163 POUND SIGN + __ @ + __/__|@ + /_ _/_ @ +(_,___/ @ + @@ +164 CURRENCY SIGN + /|_/|@ + | . / @ + /_ | @ +|/ |/ @ + @@ +165 YEN SIGN + ____@ + _| / /@ + /_ __/@ +/_ __/ @ + /_/ @@ +166 BROKEN BAR + __@ + / /@ + /_/ @ + / / @ +/_/ @@ +167 SECTION SIGN + __ @ + _/ _)@ + / | | @ + | |_/ @ +(__/ @@ +168 DIAERESIS + _ _ @ +(_) (_)@ + $ $ @ +$ $ @ + @@ +169 COPYRIGHT SIGN + ____ @ + / ___\ @ + / / _/ |@ +| |__/ / @ + \____/ @@ +170 FEMININE ORDINAL INDICATOR + ___ _@ + / _ `/@ + _\_,_/ @ +/____/ @ + @@ +171 LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + ____@ + / / /@ +< < < @ + \_\_\@ + @@ +172 NOT SIGN + @ + ____@ +/_ /@ + /_/ @ + @@ +173 SOFT HYPHEN + @ + ___@ +/__/@ + $ @ + @@ +174 REGISTERED SIGN + ____ @ + / __ \ @ + / / -) |@ +| //\\ / @ + \____/ @@ +175 MACRON + ____@ +/___/@ + $ @ +$ @ + @@ +176 DEGREE SIGN + __ @ + /. |@ +|__/ @ + $ @ + @@ +177 PLUS-MINUS SIGN + __ @ + __/ /_@ + /_ __/@ + __/_/_ @ +/_____/ @@ +178 SUPERSCRIPT TWO + __ @ + |_ )@ +/__| @ + $ @ + @@ +179 SUPERSCRIPT THREE + ___@ + |_ /@ +/__) @ + $ @ + @@ +180 ACUTE ACCENT + __@ +/_/@ + $ @ +$ @ + @@ +181 MICRO SIGN + @ + __ __@ + / // /@ + / .,_/ @ +/_/ @@ +182 PILCROW SIGN + _____@ + / /@ +|_ / / @ +/_/_/ @ + @@ +183 MIDDLE DOT + @ + _ @ +(_)@ +$ @ + @@ +184 CEDILLA + @ + @ + @ + _ @ +/_)@@ +185 SUPERSCRIPT ONE + __@ + < /@ +/_/ @ +$ @ + @@ +186 MASCULINE ORDINAL INDICATOR + ___ @ + / _ \@ + _\___/@ +/____/ @ + @@ +187 RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +____ @ +\ \ \ @ + > > >@ +/_/_/ @ + @@ +188 VULGAR FRACTION ONE QUARTER + __ __ @ + < /_/_/___@ +/_//_//_' /@ + /_/ /_/ @ + @@ +189 VULGAR FRACTION ONE HALF + __ __ @ + < /_/_/_ @ +/_//_/|_ )@ + /_/ /__| @ + @@ +190 VULGAR FRACTION THREE QUARTERS + ___ __ @ + |_ /_/_/___@ +/__)/_//_' /@ + /_/ /_/ @ + @@ +191 INVERTED QUESTION MARK + _ @ + _(_)@ +/ _/_@ +\___/@ + @@ +192 LATIN CAPITAL LETTER A WITH GRAVE + __ @ + _\_\@ + / - |@ +/_/|_|@ + @@ +193 LATIN CAPITAL LETTER A WITH ACUTE + __@ + _/_/@ + / - |@ +/_/|_|@ + @@ +194 LATIN CAPITAL LETTER A WITH CIRCUMFLEX + //|@ + _|/||@ + / - | @ +/_/|_| @ + @@ +195 LATIN CAPITAL LETTER A WITH TILDE + /\//@ + _//\/ @ + / - | @ +/_/|_| @ + @@ +196 LATIN CAPITAL LETTER A WITH DIAERESIS + _ _ @ + (_)(_)@ + / - | @ +/_/|_| @ + @@ +197 LATIN CAPITAL LETTER A WITH RING ABOVE + (())@ + / _ |@ + / __ |@ +/_/ |_|@ + @@ +198 LATIN CAPITAL LETTER AE + _______@ + / _ __/@ + / _ _/ @ +/_//___/ @ + @@ +199 LATIN CAPITAL LETTER C WITH CEDILLA + _____@ + / ___/@ +/ /__ @ +\___/ @ +/_) @@ +200 LATIN CAPITAL LETTER E WITH GRAVE + __ @ + \_\@ + / -<@ +/__< @ + @@ +201 LATIN CAPITAL LETTER E WITH ACUTE + __@ + _/_/@ + / -< @ +/__< @ + @@ +202 LATIN CAPITAL LETTER E WITH CIRCUMFLEX + //|@ + |/||@ + / -< @ +/__< @ + @@ +203 LATIN CAPITAL LETTER E WITH DIAERESIS + _ _ @ + (_)(_)@ + / -< @ +/__< @ + @@ +204 LATIN CAPITAL LETTER I WITH GRAVE + __ @ + _\_\ @ + /_ __/@ +/____/ @ + @@ +205 LATIN CAPITAL LETTER I WITH ACUTE + __@ + __/_/@ + /_ __/@ +/____/ @ + @@ +206 LATIN CAPITAL LETTER I WITH CIRCUMFLEX + //|@ + _|/||@ + /_ __/@ +/____/ @ + @@ +207 LATIN CAPITAL LETTER I WITH DIAERESIS + _ _ @ + (_)(_)@ + /_ __/ @ +/____/ @ + @@ +208 LATIN CAPITAL LETTER ETH + ____ @ + _/ __ \@ +/_ _// /@ +/_____/ @ + @@ +209 LATIN CAPITAL LETTER N WITH TILDE + /\//@ + __//\/ @ + / |/ / @ +/_/|__/ @ + @@ +210 LATIN CAPITAL LETTER O WITH GRAVE + __ @ + _\_\ @ +/ __ \@ +\____/@ + @@ +211 LATIN CAPITAL LETTER O WITH ACUTE + __@ + __/_/@ +/ __ \@ +\____/@ + @@ +212 LATIN CAPITAL LETTER O WITH CIRCUMFLEX + //|@ + _|/||@ +/ __ \@ +\____/@ + @@ +213 LATIN CAPITAL LETTER O WITH TILDE + /\//@ + _//\/ @ +/ __ \ @ +\____/ @ + @@ +214 LATIN CAPITAL LETTER O WITH DIAERESIS + _ _ @ + (_)_(_)@ +/ __ \ @ +\____/ @ + @@ +215 MULTIPLICATION SIGN + @ + /|/|@ + > < @ +|/|/ @ + @@ +216 LATIN CAPITAL LETTER O WITH STROKE + _____ @ + / _// \@ +/ //// /@ +\_//__/ @ + @@ +217 LATIN CAPITAL LETTER U WITH GRAVE + __ @ + __\_\ @ +/ /_/ /@ +\____/ @ + @@ +218 LATIN CAPITAL LETTER U WITH ACUTE + __@ + __ /_/@ +/ /_/ /@ +\____/ @ + @@ +219 LATIN CAPITAL LETTER U WITH CIRCUMFLEX + //|@ + __|/||@ +/ /_/ /@ +\____/ @ + @@ +220 LATIN CAPITAL LETTER U WITH DIAERESIS + _ _ @ + (_) (_)@ +/ /_/ / @ +\____/ @ + @@ +221 LATIN CAPITAL LETTER Y WITH ACUTE + __@ +__/_/@ +\ V /@ + /_/ @ + @@ +222 LATIN CAPITAL LETTER THORN + __ @ + / / @ + / -_)@ +/_/ @ + @@ +223 LATIN SMALL LETTER SHARP S + ____ @ + / _ )@ + / /< < @ + / //__/ @ +/_/ @@ +224 LATIN SMALL LETTER A WITH GRAVE + __ @ + _\_\_@ +/ _ `/@ +\_,_/ @ + @@ +225 LATIN SMALL LETTER A WITH ACUTE + __@ + __/_/@ +/ _ `/@ +\_,_/ @ + @@ +226 LATIN SMALL LETTER A WITH CIRCUMFLEX + //|@ + _|/||@ +/ _ `/@ +\_,_/ @ + @@ +227 LATIN SMALL LETTER A WITH TILDE + /\//@ + _//\/ @ +/ _ `/ @ +\_,_/ @ + @@ +228 LATIN SMALL LETTER A WITH DIAERESIS + _ _ @ + (_)(_)@ +/ _ `/ @ +\_,_/ @ + @@ +229 LATIN SMALL LETTER A WITH RING ABOVE + __ @ + _(())@ +/ _ `/@ +\_,_/ @ + @@ +230 LATIN SMALL LETTER AE + @ + ___ ___ @ +/ _ ` -_)@ +\_,____/ @ + @@ +231 LATIN SMALL LETTER C WITH CEDILLA + @ + ____@ +/ __/@ +\__/ @ +/_) @@ +232 LATIN SMALL LETTER E WITH GRAVE + __ @ + _\_\@ +/ -_)@ +\__/ @ + @@ +233 LATIN SMALL LETTER E WITH ACUTE + __@ + _/_/@ +/ -_)@ +\__/ @ + @@ +234 LATIN SMALL LETTER E WITH CIRCUMFLEX + //|@ + |/||@ +/ -_)@ +\__/ @ + @@ +235 LATIN SMALL LETTER E WITH DIAERESIS + _ _ @ +(_)(_)@ +/ -_) @ +\__/ @ + @@ +236 LATIN SMALL LETTER I WITH GRAVE + __ @ + \_\@ + / / @ +/_/ @ + @@ +237 LATIN SMALL LETTER I WITH ACUTE + __@ + /_/@ + / / @ +/_/ @ + @@ +238 LATIN SMALL LETTER I WITH CIRCUMFLEX + //|@ + |/||@ + / / @ +/_/ @ + @@ +239 LATIN SMALL LETTER I WITH DIAERESIS + _ _ @ +(_)_(_)@ + / / @ +/_/ @ + @@ +240 LATIN SMALL LETTER ETH + _||_@ + __ || @ +/ _` | @ +\___/ @ + @@ +241 LATIN SMALL LETTER N WITH TILDE + /\//@ + _//\/ @ + / _ \ @ +/_//_/ @ + @@ +242 LATIN SMALL LETTER O WITH GRAVE + __ @ + _\_\@ +/ _ \@ +\___/@ + @@ +243 LATIN SMALL LETTER O WITH ACUTE + __@ + _/_/@ +/ _ \@ +\___/@ + @@ +244 LATIN SMALL LETTER O WITH CIRCUMFLEX + //|@ + _|/||@ +/ _ \ @ +\___/ @ + @@ +245 LATIN SMALL LETTER O WITH TILDE + /\//@ + _//\/ @ +/ _ \ @ +\___/ @ + @@ +246 LATIN SMALL LETTER O WITH DIAERESIS + _ _ @ + (_)(_)@ +/ _ \ @ +\___/ @ + @@ +247 DIVISION SIGN + _ @ + _(_)@ +/___/@ +(_) @ + @@ +248 LATIN SMALL LETTER O WITH STROKE + @ + ___ @ +/ //\@ +\//_/@ + @@ +249 LATIN SMALL LETTER U WITH GRAVE + __ @ + __\_\@ +/ // /@ +\_,_/ @ + @@ +250 LATIN SMALL LETTER U WITH ACUTE + __@ + __/_/@ +/ // /@ +\_,_/ @ + @@ +251 LATIN SMALL LETTER U WITH CIRCUMFLEX + //|@ + _|/||@ +/ // /@ +\_,_/ @ + @@ +252 LATIN SMALL LETTER U WITH DIAERESIS + _ _ @ + (_)(_)@ +/ // / @ +\_,_/ @ + @@ +253 LATIN SMALL LETTER Y WITH ACUTE + __@ + __/_/@ + / // /@ + \_, / @ +/___/ @@ +254 LATIN SMALL LETTER THORN + __ @ + / / @ + / _ \@ + / .__/@ +/_/ @@ +255 LATIN SMALL LETTER Y WITH DIAERESIS + _ _ @ + (_)(_)@ + / // / @ + \_, / @ +/___/ @@ diff --git a/packages/framework/resources/views/components/article-excerpt.blade.php b/packages/framework/resources/views/components/article-excerpt.blade.php new file mode 100644 index 00000000000..2bd4e095677 --- /dev/null +++ b/packages/framework/resources/views/components/article-excerpt.blade.php @@ -0,0 +1,47 @@ +@php +/** @var \Hyde\Framework\Models\MarkdownPost $post */ +@endphp + +<article class="mt-4 mb-8" itemscope itemtype="http://schema.org/Article"> + <meta itemprop="identifier" content="{{ $post->slug }}"> +@if(Hyde::uriPath()) + <meta itemprop="url" content="{{ Hyde::uriPath('posts/' . $post->slug) }}"> +@endif + + <header> + <a href="posts/{{ Hyde::pageLink($post->slug . '.html') }}" class="block w-fit"> + <h2 class="text-2xl font-bold text-gray-700 hover:text-gray-900 dark:text-gray-200 dark:hover:text-white transition-colors duration-75"> + {{ $post->matter['title'] ?? $post->title }} + </h2> + </a> + </header> + + <footer> +@isset($post->date) + <span class="opacity-75"> + <span itemprop="dateCreated datePublished"> + {{ $post->date->short }}</span>{{ isset($post->author) ? ',' : '' }} + </span> +@endisset +@isset($post->author) + <span itemprop="author" itemscope itemtype="http://schema.org/Person"> + <span class="opacity-75">by</span> + <span itemprop="name"> + {{ $post->author->name ?? $post->author->username }} + </span> + </span> +@endisset + </footer> + +@isset($post->matter['description']) + <section role="doc-abstract" aria-label="Excerpt"> + <p class="leading-relaxed my-1"> + {{ $post->matter['description'] }} + </p> + </section> +@endisset + + <footer> + <a href="posts/{{ Hyde::pageLink($post->slug . '.html') }}" class="text-indigo-500 hover:underline font-medium">Read post</a> + </footer> +</article> \ No newline at end of file diff --git a/packages/framework/resources/views/components/blog-post-feed.blade.php b/packages/framework/resources/views/components/blog-post-feed.blade.php new file mode 100644 index 00000000000..f7a720a3c32 --- /dev/null +++ b/packages/framework/resources/views/components/blog-post-feed.blade.php @@ -0,0 +1,3 @@ +@foreach(\Hyde\Framework\Models\MarkdownPost::getLatestPosts() as $post) + @include('hyde::components.article-excerpt') +@endforeach \ No newline at end of file diff --git a/packages/framework/resources/views/components/docs/labeled-sidebar-navigation-menu.blade.php b/packages/framework/resources/views/components/docs/labeled-sidebar-navigation-menu.blade.php new file mode 100644 index 00000000000..5faa2ac34a2 --- /dev/null +++ b/packages/framework/resources/views/components/docs/labeled-sidebar-navigation-menu.blade.php @@ -0,0 +1,22 @@ +<ul id="sidebar-navigation-menu" role="list"> + @foreach ($sidebar->getCategories() as $category) + <li class="sidebar-category" role="listitem"> + <h4 class="sidebar-category-heading">{{ Hyde::titleFromSlug($category) }}</h4> + <ul class="sidebar-category-list" role="list"> + @foreach ($sidebar->getItemsInCategory($category) as $item) + <li @class([ 'sidebar-navigation-item' , 'active'=> $item->destination === basename($currentPage)]) role="listitem"> + @if($item->destination === basename($currentPage)) + <a href="{{ Hyde::pageLink($item->destination . '.html') }}" aria-current="true">{{ $item->label }}</a> + @if(isset($page->tableOfContents)) + <span class="sr-only">Table of contents</span> + {!! ($page->tableOfContents) !!} + @endif + @else + <a href="{{ Hyde::pageLink($item->destination . '.html') }}">{{ $item->label }}</a> + @endif + </li> + @endforeach + </ul> + </li> + @endforeach +</ul> diff --git a/packages/framework/resources/views/components/docs/search-input.blade.php b/packages/framework/resources/views/components/docs/search-input.blade.php new file mode 100644 index 00000000000..0b4b9d8016b --- /dev/null +++ b/packages/framework/resources/views/components/docs/search-input.blade.php @@ -0,0 +1,6 @@ +<div id="hyde-search"> + <noscript> + The search feature requires JavaScript to be enabled in your browser. + </noscript> + <input type="search" name="search" id="search-input" placeholder="Search..." autocomplete="off" autofocus> +</div> \ No newline at end of file diff --git a/packages/framework/resources/views/components/docs/search.blade.php b/packages/framework/resources/views/components/docs/search.blade.php new file mode 100644 index 00000000000..d7bb87d0940 --- /dev/null +++ b/packages/framework/resources/views/components/docs/search.blade.php @@ -0,0 +1,88 @@ +<button id="searchMenuButton" onclick="toggleSearchMenu()" aria-label="Toggle search menu"> + Search + <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" role="presentation"><path d="M0 0h24v24H0z" fill="none"/><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg> +</button> +<button id="searchMenuButtonMobile" onclick="toggleSearchMenu()" aria-label="Toggle search menu"> + <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" role="presentation"><path d="M0 0h24v24H0z" fill="none"/><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg> +</button> +@push('scripts') + +<dialog id="searchMenu" class="prose dark:prose-invert bg-gray-100 dark:bg-gray-800 fixed"> + @include('hyde::components.docs.search-input') + <footer> + <small> + Press <code><kbd title="Forward slash">/</kbd></code> to open search window. + Use <code><kbd title="Escape key">esc</kbd></code> to close. + </small> + </footer> +</dialog> + +<script> +const searchMenu = document.getElementById('searchMenu'); + +function toggleSearchMenu() { + if (searchMenu.hasAttribute('open')) { + closeSearchMenu(); + } else { + openSearchMenu(); + } +} + +function closeSearchMenu() { + searchMenu.removeAttribute('open'); + + document.getElementById('searchMenuBackdrop').remove(); + document.getElementById('searchMenuCloseButton').remove(); + + document.getElementById('searchMenuButton').style.visibility = 'visible'; +} + +function openSearchMenu() { + searchMenu.setAttribute('open', ''); + + createBackdrop(); + createCloseButton(); + document.getElementById('searchMenuButton').style.visibility = 'hidden'; + + document.getElementById('search-input').focus(); + + function createBackdrop() { + const backdrop = document.createElement('div'); + backdrop.id = 'searchMenuBackdrop'; + backdrop.classList.add('backdrop', 'active'); + backdrop.addEventListener('click', () => { + closeSearchMenu(); + }); + document.body.appendChild(backdrop); + } + + function createCloseButton() { + const closeButton = document.createElement('button'); + closeButton.id = 'searchMenuCloseButton'; + closeButton.classList.add('fixed'); + closeButton.setAttribute('aria-label', 'Close search menu'); + closeButton.addEventListener('click', () => { + closeSearchMenu(); + }); + closeButton.innerHTML = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"> + <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" /> + </svg>`; + + document.body.appendChild(closeButton); + } +} + +document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && searchMenu.hasAttribute('open')) { + closeSearchMenu(); + } +}); + +document.addEventListener('keypress', (e) => { + if (e.key === '/' && !searchMenu.hasAttribute('open')) { + e.preventDefault(); + openSearchMenu(); + } +}); +</script> +@endpush \ No newline at end of file diff --git a/packages/framework/resources/views/components/docs/sidebar-navigation-menu.blade.php b/packages/framework/resources/views/components/docs/sidebar-navigation-menu.blade.php new file mode 100644 index 00000000000..61c391ebcc6 --- /dev/null +++ b/packages/framework/resources/views/components/docs/sidebar-navigation-menu.blade.php @@ -0,0 +1,17 @@ +<ul id="sidebar-navigation-menu" role="list"> + @foreach ($sidebar->getSortedSidebar() as $item) + <li @class([ 'sidebar-navigation-item' , 'active'=> $item->destination === basename($currentPage)])> + @if($item->destination === basename($currentPage)) + <a href="{{ Hyde::pageLink($item->destination . '.html') }}" aria-current="true">{{ + $item->label }}</a> + + @if(isset($page->tableOfContents)) + <span class="sr-only">Table of contents</span> + {!! ($page->tableOfContents) !!} + @endif + @else + <a href="{{ Hyde::pageLink($item->destination . '.html') }}">{{ $item->label }}</a> + @endif + </li> + @endforeach +</ul> \ No newline at end of file diff --git a/packages/framework/resources/views/components/navigation/navigation-brand.blade.php b/packages/framework/resources/views/components/navigation/navigation-brand.blade.php new file mode 100644 index 00000000000..e1abc11ba3f --- /dev/null +++ b/packages/framework/resources/views/components/navigation/navigation-brand.blade.php @@ -0,0 +1,3 @@ +<a aria-label="Home page link" href="{{ $homeRoute }}" class="font-bold px-4"> + {{ config('hyde.name', 'HydePHP') }} +</a> \ No newline at end of file diff --git a/packages/framework/resources/views/components/navigation/navigation-link.blade.php b/packages/framework/resources/views/components/navigation/navigation-link.blade.php new file mode 100644 index 00000000000..dc58cd710b9 --- /dev/null +++ b/packages/framework/resources/views/components/navigation/navigation-link.blade.php @@ -0,0 +1,6 @@ +<a href="{{ $item['route'] }}" {{ $item['current'] ? 'aria-current="page"' : '' }} + @class(['block my-2 md:my-0 md:inline-block py-1 text-gray-700 hover:text-gray-900 dark:text-gray-100' + , 'border-l-4 border-indigo-500 md:border-none font-medium -ml-6 pl-5 md:ml-0 md:pl-0 bg-gray-100 dark:bg-gray-800 md:bg-transparent dark:md:bg-transparent'=> $item['current'] + ])> + {{ $item['title'] }} +</a> \ No newline at end of file diff --git a/packages/framework/resources/views/components/navigation/navigation-toggle-button.blade.php b/packages/framework/resources/views/components/navigation/navigation-toggle-button.blade.php new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/framework/resources/views/components/navigation/theme-toggle-button.blade.php b/packages/framework/resources/views/components/navigation/theme-toggle-button.blade.php new file mode 100644 index 00000000000..9a04132fd90 --- /dev/null +++ b/packages/framework/resources/views/components/navigation/theme-toggle-button.blade.php @@ -0,0 +1,6 @@ +@if(Hyde::hasFeature('darkmode')) +<button class="theme-toggle-button flex items-center px-3 py-1 hover:text-gray-700 dark:text-gray-200" title="Toggle theme"> + <span class="sr-only">Toggle dark theme</span> + <figure class="theme-icon" role="presentation"></figure> +</button> +@endif \ No newline at end of file diff --git a/packages/framework/resources/views/components/post/article.blade.php b/packages/framework/resources/views/components/post/article.blade.php new file mode 100644 index 00000000000..3d1476a0363 --- /dev/null +++ b/packages/framework/resources/views/components/post/article.blade.php @@ -0,0 +1,21 @@ +<article aria-label="Article" id="{{ Hyde::uriPath() ?? '' }}posts/{{ $page->slug }}" itemscope itemtype="http://schema.org/Article" + @class(['post-article mx-auto prose dark:prose-invert', 'torchlight-enabled' => Hyde\Framework\Helpers\Features::hasTorchlight()])> + <meta itemprop="identifier" content="{{ $page->slug }}"> + @if(Hyde::uriPath()) + <meta itemprop="url" content="{{ Hyde::uriPath('posts/' . $page->slug) }}"> + @endif + + <header aria-label="Header section" role="doc-pageheader"> + <h1 itemprop="headline" class="mb-4">{{ $title ?? 'Blog Post' }}</h1> + <div id="byline" aria-label="About the post" role="doc-introduction"> + @includeWhen(isset($page->date), 'hyde::components.post.date') + @includeWhen(isset($page->author), 'hyde::components.post.author') + @includeWhen(isset($page->category), 'hyde::components.post.category') + </div> + </header> + @includeWhen(isset($page->image), 'hyde::components.post.image') + <div aria-label="Article body" itemprop="articleBody"> + {!! $markdown !!} + </div> + <span class="sr-only">End of article</span> +</article> diff --git a/packages/framework/resources/views/components/post/author.blade.php b/packages/framework/resources/views/components/post/author.blade.php new file mode 100644 index 00000000000..a644dca7ec0 --- /dev/null +++ b/packages/framework/resources/views/components/post/author.blade.php @@ -0,0 +1,10 @@ +by author +<address itemprop="author" itemscope itemtype="http://schema.org/Person" aria-label="The post author" style="display: inline;"> + @if($page->author->website) + <a href="{{ $page->author->website }}" rel="author" itemprop="url" aria-label="The author's website"> + @endif + <span itemprop="name" aria-label="The author's name" {{ ($page->author->username && ($page->author->username !== $page->author->name)) ? 'title=@'. urlencode($page->author->username) .'' : '' }}>{{ $page->author->name ?? $page->author->username }}</span> + @if($page->author->website) + </a> + @endif +</address> diff --git a/packages/framework/resources/views/components/post/category.blade.php b/packages/framework/resources/views/components/post/category.blade.php new file mode 100644 index 00000000000..17615661470 --- /dev/null +++ b/packages/framework/resources/views/components/post/category.blade.php @@ -0,0 +1 @@ +in the category "{{ $page->category }}" diff --git a/packages/framework/resources/views/components/post/date.blade.php b/packages/framework/resources/views/components/post/date.blade.php new file mode 100644 index 00000000000..010d9d3390a --- /dev/null +++ b/packages/framework/resources/views/components/post/date.blade.php @@ -0,0 +1 @@ +Posted <time itemprop="dateCreated datePublished" datetime="{{ $page->date->datetime }}" title="{{ $page->date->sentence }}">{{ $page->date->short }}</time> diff --git a/packages/framework/resources/views/components/post/description.blade.php b/packages/framework/resources/views/components/post/description.blade.php new file mode 100644 index 00000000000..bebb3b6a3e0 --- /dev/null +++ b/packages/framework/resources/views/components/post/description.blade.php @@ -0,0 +1,3 @@ +<p itemprop="abstract"> + {{ $page->matter['description'] }} +</p> \ No newline at end of file diff --git a/packages/framework/resources/views/components/post/image.blade.php b/packages/framework/resources/views/components/post/image.blade.php new file mode 100644 index 00000000000..7e0fbb5d8c9 --- /dev/null +++ b/packages/framework/resources/views/components/post/image.blade.php @@ -0,0 +1,9 @@ +<figure aria-label="Cover image" itemprop="image" itemscope itemtype="http://schema.org/ImageObject" role="doc-cover"> + <img src="{{ $page->image->getLink($currentPage) }}" alt="{{ $page->image->description ?? '' }}" title="{{ $page->image->title ?? '' }}" itemprop="image" class="mb-0"> + <figcaption aria-label="Image caption" itemprop="caption"> + {!! $page->image->getFluentAttribution() !!} + </figcaption> + @foreach ($page->image->getMetadataArray() as $name => $value) + <meta itemprop="{{ $name }}" content="{{ $value }}"> + @endforeach +</figure> diff --git a/packages/framework/resources/views/homepages/blank.blade.php b/packages/framework/resources/views/homepages/blank.blade.php new file mode 100644 index 00000000000..cf92aa1adfd --- /dev/null +++ b/packages/framework/resources/views/homepages/blank.blade.php @@ -0,0 +1,8 @@ +@extends('hyde::layouts.app') +@section('content') + +<main id="content"> + <h1 class="text-center text-3xl font-bold">Hello World!</h1> +</main> + +@endsection diff --git a/packages/framework/resources/views/homepages/post-feed.blade.php b/packages/framework/resources/views/homepages/post-feed.blade.php new file mode 100644 index 00000000000..cd17feb42d3 --- /dev/null +++ b/packages/framework/resources/views/homepages/post-feed.blade.php @@ -0,0 +1,17 @@ +@php($title = 'Latest Posts') +@extends('hyde::layouts.app') +@section('content') + +<main id="content" class="mx-auto max-w-7xl py-12 px-8"> + <header class="lg:mb-12 xl:mb-16"> + <h1 + class="text-3xl text-left leading-10 tracking-tight font-extrabold sm:leading-none mb-8 md:mb-12 md:text-4xl md:text-center lg:text-5xl text-gray-700 dark:text-gray-200"> + Latest Posts</h1> + </header> + + <div class="max-w-3xl mx-auto"> + @include('hyde::components.blog-post-feed') + </div> +</main> + +@endsection diff --git a/packages/framework/resources/views/homepages/welcome.blade.php b/packages/framework/resources/views/homepages/welcome.blade.php new file mode 100644 index 00000000000..27e1f7127dc --- /dev/null +++ b/packages/framework/resources/views/homepages/welcome.blade.php @@ -0,0 +1,87 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <title>HydePHP + + + + + + + +
+
+ +
+

+ You're running on HydePHP +

+
+
+

+ Leap into the future of static HTML blogs and documentation with the tools you already know and love. + Made with Tailwind, Laravel, and Coffee. +

+
+ +
+

+ This is the default homepage stored as index.blade.php, however you can publish any of the built-in views using the following command: + + +

php hyde publish:homepage
+

+
+ +
+ Resources for getting started + +
+
+
+ +
+
+ + + \ No newline at end of file diff --git a/packages/framework/resources/views/layouts/app.blade.php b/packages/framework/resources/views/layouts/app.blade.php new file mode 100644 index 00000000000..d5788128ee2 --- /dev/null +++ b/packages/framework/resources/views/layouts/app.blade.php @@ -0,0 +1,18 @@ + + + + @include('hyde::layouts.head') + + + Skip to content + @includeUnless($withoutNavigation ?? false, 'hyde::layouts.navigation') + +
+ @yield('content') +
+ + @includeUnless(config('hyde.footer.enabled', true) && ($withoutNavigation ?? false), 'hyde::layouts.footer') + + @include('hyde::layouts.scripts') + + diff --git a/packages/framework/resources/views/layouts/docs.blade.php b/packages/framework/resources/views/layouts/docs.blade.php new file mode 100644 index 00000000000..7a91c721859 --- /dev/null +++ b/packages/framework/resources/views/layouts/docs.blade.php @@ -0,0 +1,97 @@ + + + + @include('hyde::layouts.head') + + + + Skip to content + + + + + +
+ @php + $document = \Hyde\Framework\Services\HydeSmartDocs::create($page, $markdown); + @endphp +
$document->hasTorchlight()])> +
+ {!! $document->renderHeader() !!} +
+
+ {!! $document->renderBody() !!} +
+
+ {!! $document->renderFooter() !!} +
+
+
+ + @if(Hyde\Framework\Helpers\Features::hasDocumentationSearch()) + @include('hyde::components.docs.search') + + + @endif + + @include('hyde::layouts.scripts') + + \ No newline at end of file diff --git a/packages/framework/resources/views/layouts/footer.blade.php b/packages/framework/resources/views/layouts/footer.blade.php new file mode 100644 index 00000000000..1c50b80505c --- /dev/null +++ b/packages/framework/resources/views/layouts/footer.blade.php @@ -0,0 +1,12 @@ +
+
+ {!! Hyde\Framework\Actions\ConvertsFooterMarkdown::execute() !!} +
+ + + +
diff --git a/packages/framework/resources/views/layouts/head.blade.php b/packages/framework/resources/views/layouts/head.blade.php new file mode 100644 index 00000000000..529d6ca99fb --- /dev/null +++ b/packages/framework/resources/views/layouts/head.blade.php @@ -0,0 +1,19 @@ + + +{{ isset($title) ? config('hyde.name', 'HydePHP') . ' - ' . $title : config('hyde.name', 'HydePHP') }} + +@if (file_exists(Hyde::path('_media/favicon.ico'))) + +@endif + +{{-- App Meta Tags --}} +@include('hyde::layouts.meta') + +{{-- App Stylesheets --}} +@include('hyde::layouts.styles') + +@if(Hyde::hasFeature('darkmode')) +{{-- Check the local storage for theme preference to avoid FOUC --}} + + +@endif \ No newline at end of file diff --git a/packages/framework/resources/views/layouts/meta.blade.php b/packages/framework/resources/views/layouts/meta.blade.php new file mode 100644 index 00000000000..b9c4b8c2e8e --- /dev/null +++ b/packages/framework/resources/views/layouts/meta.blade.php @@ -0,0 +1,5 @@ +{{-- Render the config defined and dynamic page meta tags --}} +{!! $page->renderPageMetadata() !!} + +{{-- Add any extra tags to include in the section --}} +@stack('meta') diff --git a/packages/framework/resources/views/layouts/navigation.blade.php b/packages/framework/resources/views/layouts/navigation.blade.php new file mode 100644 index 00000000000..9df79fdebc3 --- /dev/null +++ b/packages/framework/resources/views/layouts/navigation.blade.php @@ -0,0 +1,31 @@ +@php +$links = Hyde\Framework\Actions\GeneratesNavigationMenu::getNavigationLinks($currentPage); +$homeRoute = ($links[array_search('Home', array_column($links, 'title'))])['route'] ?? Hyde::pageLink('index.html'); +@endphp + + diff --git a/packages/framework/resources/views/layouts/page.blade.php b/packages/framework/resources/views/layouts/page.blade.php new file mode 100644 index 00000000000..df1fca295ed --- /dev/null +++ b/packages/framework/resources/views/layouts/page.blade.php @@ -0,0 +1,11 @@ +{{-- The Markdown Page Layout --}} +@extends('hyde::layouts.app') +@section('content') + +
+
Hyde\Framework\Helpers\Features::hasTorchlight()])> + {!! $markdown !!} +
+
+ +@endsection diff --git a/packages/framework/resources/views/layouts/post.blade.php b/packages/framework/resources/views/layouts/post.blade.php new file mode 100644 index 00000000000..7859e01e8d8 --- /dev/null +++ b/packages/framework/resources/views/layouts/post.blade.php @@ -0,0 +1,9 @@ +{{-- The Post Page Layout --}} +@extends('hyde::layouts.app') +@section('content') + +
+ @include('hyde::components.post.article') +
+ +@endsection \ No newline at end of file diff --git a/packages/framework/resources/views/layouts/scripts.blade.php b/packages/framework/resources/views/layouts/scripts.blade.php new file mode 100644 index 00000000000..7ad4d665567 --- /dev/null +++ b/packages/framework/resources/views/layouts/scripts.blade.php @@ -0,0 +1,12 @@ +{{-- The core HydeFront scripts --}} +@if(Hyde::scripts()) + +@endif + +{{-- The compiled Laravel Mix scripts --}} +@if(Hyde::assetManager()->hasMediaFile('app.js')) + +@endif + +{{-- Add any extra scripts to include before the closing tag --}} +@stack('scripts') \ No newline at end of file diff --git a/packages/framework/resources/views/layouts/styles.blade.php b/packages/framework/resources/views/layouts/styles.blade.php new file mode 100644 index 00000000000..1d78b9f55f5 --- /dev/null +++ b/packages/framework/resources/views/layouts/styles.blade.php @@ -0,0 +1,12 @@ +{{-- The core HydeFront stylesheet --}} +@if(Hyde::styles()) + +@endif + +{{-- The compiled Tailwind/App styles --}} +@if(Hyde::assetManager()->hasMediaFile('app.css')) + +@endif + +{{-- Add any extra styles to include after the others --}} +@stack('styles') \ No newline at end of file diff --git a/packages/framework/resources/views/pages/404.blade.php b/packages/framework/resources/views/pages/404.blade.php new file mode 100644 index 00000000000..fbecee7f586 --- /dev/null +++ b/packages/framework/resources/views/pages/404.blade.php @@ -0,0 +1,51 @@ + + + + + 404 - Page not found + + + + + + + + + + + + + + + +
+
+
+
+ 404 +
+ +
+ +

+ Sorry, the page you are looking for could not be found. +

+ + + + +
+ +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/packages/framework/resources/views/pages/documentation-search.blade.php b/packages/framework/resources/views/pages/documentation-search.blade.php new file mode 100644 index 00000000000..a6b6ca5ca4b --- /dev/null +++ b/packages/framework/resources/views/pages/documentation-search.blade.php @@ -0,0 +1,3 @@ +

Search the documentation site

+ +@include('hyde::components.docs.search-input') \ No newline at end of file diff --git a/packages/framework/src/Actions/ChecksIfConfigIsUpToDate.php b/packages/framework/src/Actions/ChecksIfConfigIsUpToDate.php new file mode 100644 index 00000000000..1307f0a6e0f --- /dev/null +++ b/packages/framework/src/Actions/ChecksIfConfigIsUpToDate.php @@ -0,0 +1,34 @@ +hydeConfig = file_get_contents(Hyde::path('config/hyde.php')); + $this->frameworkConfig = file_get_contents(Hyde::vendorPath('config/hyde.php')); + } + + public function execute(): bool + { + return $this->findOptions($this->hydeConfig) === $this->findOptions($this->frameworkConfig); + } + + public function findOptions(string $config): int + { + return substr_count($config, '--------------------------------------------------------------------------'); + } +} diff --git a/packages/framework/src/Actions/ConvertsArrayToFrontMatter.php b/packages/framework/src/Actions/ConvertsArrayToFrontMatter.php new file mode 100644 index 00000000000..a63bad52708 --- /dev/null +++ b/packages/framework/src/Actions/ConvertsArrayToFrontMatter.php @@ -0,0 +1,38 @@ + $value) { + $yaml[] = "$key: $value"; + } + + // Set the closing block + $yaml[] = '---'; + + // Add an extra line + $yaml[] = ''; + + // Return the array imploded into a string with newline characters + return implode("\n", $yaml); + } +} diff --git a/packages/framework/src/Actions/ConvertsFooterMarkdown.php b/packages/framework/src/Actions/ConvertsFooterMarkdown.php new file mode 100644 index 00000000000..702fb06009c --- /dev/null +++ b/packages/framework/src/Actions/ConvertsFooterMarkdown.php @@ -0,0 +1,28 @@ +requiredDirectories as $directory) { + // Does the directory exist? // Otherwise, create it. + is_dir(Hyde::path($directory)) || mkdir(Hyde::path($directory), recursive: true); + } + } + + /** + * Retrieve the array of default directories. + * + * @return array + */ + #[Pure] + public static function getRequiredDirectories(): array + { + return (new CreatesDefaultDirectories)->requiredDirectories; + } +} diff --git a/packages/framework/src/Actions/CreatesNewMarkdownPostFile.php b/packages/framework/src/Actions/CreatesNewMarkdownPostFile.php new file mode 100644 index 00000000000..4909fda0af4 --- /dev/null +++ b/packages/framework/src/Actions/CreatesNewMarkdownPostFile.php @@ -0,0 +1,117 @@ +title = $title ?? 'My Awesome Blog Post'; + $this->description = $description ?? 'A short description used in previews and SEO'; + $this->category = $category ?? 'blog'; + $this->author = $author ?? 'Mr. Hyde'; + if ($date === null) { + $this->date = date('Y-m-d H:i'); + } + if ($slug === null) { + $this->slug = Str::slug($title); + } + } + + /** + * Save the class object to a Markdown file. + * + * @param bool $force Should the file be created even if a file with the same path already exists? + * @return string|false Returns the path to the file if successful, or false if the file could not be saved. + * + * @throws FileConflictException if a file with the same slug already exists and the force flag is not set. + */ + public function save(bool $force = false): string|false + { + $path = Hyde::path("_posts/$this->slug.md"); + + if ($force !== true && file_exists($path)) { + throw new FileConflictException($path); + } + + $arrayWithoutSlug = ((array) $this); + + unset($arrayWithoutSlug['slug']); + + $contents = (new ConvertsArrayToFrontMatter)->execute($arrayWithoutSlug). + "\n## Write something awesome.\n\n"; + + return file_put_contents($path, $contents) ? $path : false; + } +} diff --git a/packages/framework/src/Actions/CreatesNewPageSourceFile.php b/packages/framework/src/Actions/CreatesNewPageSourceFile.php new file mode 100644 index 00000000000..143bd2cbc76 --- /dev/null +++ b/packages/framework/src/Actions/CreatesNewPageSourceFile.php @@ -0,0 +1,101 @@ +title = $title; + $this->slug = Str::slug($title); + + $this->createPage($type); + } + + public function canSaveFile(string $path): void + { + if (file_exists($path) && ! $this->force) { + throw new FileConflictException($path); + } + } + + public function createPage(string $type): int|false + { + if ($type === MarkdownPage::class) { + return $this->createMarkdownFile(); + } + if ($type === BladePage::class) { + return $this->createBladeFile(); + } + + if ($type === DocumentationPage::class) { + return $this->createDocumentationFile(); + } + + throw new UnsupportedPageTypeException('The page type must be either "markdown", "blade", or "documentation"'); + } + + public function createMarkdownFile(): int|false + { + $this->outputPath = Hyde::path("_pages/$this->slug.md"); + + $this->canSaveFile($this->outputPath); + + return file_put_contents( + $this->outputPath, + "---\ntitle: $this->title\n---\n\n# $this->title\n" + ); + } + + public function createBladeFile(): int|false + { + $this->outputPath = Hyde::path("_pages/$this->slug.blade.php"); + + $this->canSaveFile($this->outputPath); + + return file_put_contents( + $this->outputPath, + << +

$this->title

+ + +@endsection + +EOF + ); + } + + public function createDocumentationFile(): int|false + { + $this->outputPath = Hyde::path("_docs/$this->slug.md"); + + $this->canSaveFile($this->outputPath); + + return file_put_contents( + $this->outputPath, + "# $this->title\n" + ); + } +} diff --git a/packages/framework/src/Actions/FindsContentLengthForImageObject.php b/packages/framework/src/Actions/FindsContentLengthForImageObject.php new file mode 100644 index 00000000000..f0b55fa96a4 --- /dev/null +++ b/packages/framework/src/Actions/FindsContentLengthForImageObject.php @@ -0,0 +1,81 @@ +image = $image; + + $this->output = new ConsoleOutput(); + } + + public function execute(): int + { + if ($this->isImageStoredRemotely()) { + return $this->fetchRemoteImageInformation(); + } + + return $this->fetchLocalImageInformation(); + } + + protected function isImageStoredRemotely(): bool + { + return str_starts_with($this->image->getSource(), 'http'); + } + + protected function fetchRemoteImageInformation(): int + { + $this->write(' > Fetching remote image information for '.basename($this->image->getSource()).'...'); + + $response = Http::withHeaders([ + 'User-Agent' => config('hyde.http_user_agent', 'RSS Request Client'), + ])->head($this->image->getSource()); + + $headers = $response->headers(); + + if (array_key_exists('Content-Length', $headers)) { + return (int) key(array_flip($headers['Content-Length'])); + } + + $this->write(' > Warning: Could not find content length in headers for '.basename($this->image->getSource().'!')); + $this->write(' Using default content length of 0. '.''); + $this->write(' Is the image path valid? '.($this->image->getSource()).''); + + return 0; + } + + protected function fetchLocalImageInformation(): int + { + if (! file_exists($this->image->getSource())) { + $this->write(' > Warning: Could not find image file at '.$this->image->getSource().'!'); + $this->write(' Using default content length of 0. '.''); + + return 0; + } + + return filesize($this->image->getSource()); + } + + protected function write(string $string): void + { + $this->output->writeln($string); + } +} diff --git a/packages/framework/src/Actions/GeneratesDocumentationSearchIndexFile.php b/packages/framework/src/Actions/GeneratesDocumentationSearchIndexFile.php new file mode 100644 index 00000000000..6e0cb0b6d2c --- /dev/null +++ b/packages/framework/src/Actions/GeneratesDocumentationSearchIndexFile.php @@ -0,0 +1,128 @@ +execute(); + } + + public function __construct() + { + $this->searchIndex = new Collection(); + + static::$filePath = '_site/'.config('docs.output_directory', 'docs').'/search.json'; + } + + public function execute(): void + { + $this->generate(); + $this->save(); + } + + public function generate(): self + { + foreach ($this->getSourceFileSlugs() as $page) { + $this->searchIndex->push( + $this->generatePageObject($page) + ); + } + + return $this; + } + + public function generatePageObject(string $slug): object + { + $page = (new DocumentationPageParser($slug))->get(); + + return (object) [ + 'slug' => $page->slug, + 'title' => trim($page->findTitleForDocument()), + 'content' => trim($this->getSearchContentForDocument($page)), + 'destination' => $this->getDestinationForSlug($page->slug), + ]; + } + + public function getSourceFileSlugs(): array + { + return CollectionService::getDocumentationPageList(); + } + + public function getObject(): object + { + return (object) $this->searchIndex; + } + + public function getJson(): string + { + return json_encode($this->getObject()); + } + + public function save(): self + { + file_put_contents(Hyde::path(static::$filePath), $this->getJson()); + + return $this; + } + + /** + * There are a few ways we could go about this. The goal is to allow the user + * to run a free-text search to find relevant documentation pages. + * + * The easiest way to do this is by adding the Markdown body to the search index. + * But this is of course not ideal as it may take an incredible amount of space + * for large documentation sites. The Hyde docs weight around 80kb of JSON. + * + * Another option is to assemble all the headings in a document and use that + * for the search basis. A truncated version of the body could also be included. + * + * A third option which might be the most space efficient (besides from just + * adding titles, which doesn't offer much help to the user since it is just + * a filterable sidebar at that point), would be to search for keywords + * in the document. This would however add complexity as well as extra + * computing time. + * + * Benchmarks: (for official Hyde docs) + * + * Returning $document->body as is: 500ms + * Returning $document->body as Str::markdown(): 920ms + 10ms for regex + */ + public function getSearchContentForDocument(DocumentationPage $document): string + { + // This is compiles the Markdown body into HTML, and then strips out all + // HTML tags to get a plain text version of the body. This takes a long + // site, but is the simplest implementation I've found so far. + return preg_replace('/<(.|\n)*?>/', ' ', Str::markdown($document->body)); + } + + public function getDestinationForSlug(string $slug): string + { + if ($slug === 'index' && config('hyde.pretty_urls', false)) { + $slug = ''; + } + + return (config('hyde.pretty_urls', false) === true) + ? $slug : $slug.'.html'; + } +} diff --git a/packages/framework/src/Actions/GeneratesNavigationMenu.php b/packages/framework/src/Actions/GeneratesNavigationMenu.php new file mode 100644 index 00000000000..e1cc06b03de --- /dev/null +++ b/packages/framework/src/Actions/GeneratesNavigationMenu.php @@ -0,0 +1,187 @@ +currentPage = $currentPage; + + $this->links = $this->getLinks(); + } + + /** + * Create the link array. + * + * @return array + */ + private function getLinks(): array + { + $links = $this->getLinksFromConfig(); + + // Automatically add top level pages + foreach ($this->getListOfCustomPages() as $slug) { + $title = $this->getTitleFromSlug($slug); + // Only add the automatic link if it is not present in the config array + if (! in_array($title, array_column($links, 'title'))) { + $links[] = [ + 'title' => $title, + 'route' => $this->getRelativeRoutePathForSlug($slug), + 'current' => $this->currentPage == $slug, + 'priority' => $slug == 'index' ? 100 : 999, + ]; + } + } + + // Add extra links + + // If the documentation feature is enabled... + if (Features::hasDocumentationPages()) { + // And there is no link to the docs... + if (! in_array('Docs', array_column($links, 'title'))) { + // But a suitable file exists... + if (file_exists(Hyde::getDocumentationPagePath('/index.md')) || file_exists(Hyde::getDocumentationPagePath('/readme.md'))) { + // Then we can add a link. + $links[] = [ + 'title' => 'Docs', + 'route' => $this->getRelativeRoutePathForSlug( + file_exists(Hyde::getDocumentationPagePath('/index.md')) + ? Hyde::docsDirectory().'/index' + : Hyde::docsDirectory().'/readme' + ), + 'current' => false, + 'priority' => 500, + ]; + } + } + } + + // Remove config defined blacklisted links + foreach ($links as $key => $link) { + if (in_array(Str::slug($link['title']), config('hyde.navigation_menu_blacklist', []))) { + unset($links[$key]); + } + } + + // Sort + + $columns = array_column($links, 'priority'); + array_multisort($columns, SORT_ASC, $links); + + return $links; + } + + /** + * Get the custom navigation links from the config, if there are any. + * + * @return array + */ + public function getLinksFromConfig(): array + { + $configLinks = config('hyde.navigation_menu_links', []); + + $links = []; + + if (sizeof($configLinks) > 0) { + foreach ($configLinks as $link) { + $links[] = [ + 'title' => $link['title'], + 'route' => $link['destination'] ?? $this->getRelativeRoutePathForSlug($link['slug']), + 'current' => isset($link['slug']) && $this->currentPage == $link['slug'], + 'priority' => $link['priority'] ?? 999, + ]; + } + } + + return $links; + } + + /** + * Get the page title. + * + * @param string $slug + * @return string + */ + public function getTitleFromSlug(string $slug): string + { + if ($slug == 'index') { + return 'Home'; + } + + return Hyde::titleFromSlug($slug); + } + + /** + * Get a list of all the top level pages. + * + * @return array + */ + private function getListOfCustomPages(): array + { + return array_unique( + array_merge( + CollectionService::getBladePageList(), + CollectionService::getMarkdownPageList() + ) + ); + } + + /** + * Inject the proper number of `../` before the links. + * + * @param string $slug + * @return string + */ + private function getRelativeRoutePathForSlug(string $slug): string + { + return Hyde::relativeLink($slug.'.html', $this->currentPage); + } + + /** + * Static helper to get the array of navigation links. + * + * @param string $currentPage + * @return array + */ + public static function getNavigationLinks(string $currentPage = 'index'): array + { + $generator = new self($currentPage); + + return $generator->links; + } +} diff --git a/packages/framework/src/Actions/GeneratesTableOfContents.php b/packages/framework/src/Actions/GeneratesTableOfContents.php new file mode 100644 index 00000000000..7b853902421 --- /dev/null +++ b/packages/framework/src/Actions/GeneratesTableOfContents.php @@ -0,0 +1,53 @@ +markdown = $markdown; + } + + public function execute(): string + { + $config = [ + 'table_of_contents' => [ + 'html_class' => 'table-of-contents', + 'position' => 'top', + 'style' => 'bullet', + 'min_heading_level' => config('docs.table_of_contents.min_heading_level', 2), + 'max_heading_level' => config('docs.table_of_contents.max_heading_level', 4), + 'normalize' => 'relative', + ], + 'heading_permalink' => [ + 'fragment_prefix' => '', + ], + ]; + + $environment = new Environment($config); + $environment->addExtension(new CommonMarkCoreExtension()); + $environment->addExtension(new HeadingPermalinkExtension()); + $environment->addExtension(new TableOfContentsExtension()); + + $converter = new MarkdownConverter($environment); + $html = $converter->convert("[[END_TOC]]\n".$this->markdown)->getContent(); + + // Return everything before the [[END_TOC]] marker. + return substr($html, 0, strpos($html, '

[[END_TOC]]')); + } +} diff --git a/packages/framework/src/Actions/MarkdownConverter.php b/packages/framework/src/Actions/MarkdownConverter.php new file mode 100644 index 00000000000..0190524f611 --- /dev/null +++ b/packages/framework/src/Actions/MarkdownConverter.php @@ -0,0 +1,21 @@ +parse(); + } +} diff --git a/packages/framework/src/Actions/PublishesHomepageView.php b/packages/framework/src/Actions/PublishesHomepageView.php new file mode 100644 index 00000000000..26bc9c3d59a --- /dev/null +++ b/packages/framework/src/Actions/PublishesHomepageView.php @@ -0,0 +1,54 @@ + [ + 'name' => 'Welcome', + 'path' => 'resources/views/homepages/welcome.blade.php', + 'description' => 'The default welcome page.', + ], + 'posts' => [ + 'name' => 'Posts Feed', + 'path' => 'resources/views/homepages/post-feed.blade.php', + 'description' => 'A feed of your latest posts. Perfect for a blog site!', + ], + 'blank' => [ + 'name' => 'Blank Starter', + 'path' => 'resources/views/homepages/blank.blade.php', + 'description' => 'A blank Blade template with just the base layout.', + ], + ]; + + protected string $selected; + protected bool $force = false; + + public function __construct(string $selected, bool $force = false) + { + $this->force = $force; + $this->selected = $selected; + } + + public function execute(): bool|int + { + if (! array_key_exists($this->selected, self::$homePages)) { + return 404; + } + + return Hyde::copy( + Hyde::vendorPath(static::$homePages[$this->selected]['path']), + Hyde::getBladePagePath('index.blade.php'), + $this->force + ); + } +} diff --git a/packages/framework/src/Actions/PublishesHydeViews.php b/packages/framework/src/Actions/PublishesHydeViews.php new file mode 100644 index 00000000000..16129117d36 --- /dev/null +++ b/packages/framework/src/Actions/PublishesHydeViews.php @@ -0,0 +1,62 @@ + [ + 'name' => 'Blade Layouts', + 'path' => 'resources/views/layouts', + 'destination' => 'resources/views/vendor/hyde/layouts', + 'description' => 'Shared layout views, such as the app layout, navigation menu, and Markdown page templates.', + ], + 'components' => [ + 'name' => 'Blade Components', + 'path' => 'resources/views/components', + 'destination' => 'resources/views/vendor/hyde/components', + 'description' => 'More or less self contained components, extracted for customizability and DRY code.', + ], + '404' => [ + 'name' => '404 Page', + 'path' => 'resources/views/pages/404.blade.php', + 'destination' => '_pages/404.blade.php', + 'description' => 'A beautiful 404 error page by the Laravel Collective.', + ], + ]; + + protected string $selected; + + public function __construct(string $selected) + { + $this->selected = $selected; + } + + public function execute(): bool|int + { + if (! array_key_exists($this->selected, self::$options)) { + return 404; + } + + if (is_dir(Hyde::vendorPath(static::$options[$this->selected]['path']))) { + return File::copyDirectory( + Hyde::vendorPath(static::$options[$this->selected]['path']), + Hyde::path(static::$options[$this->selected]['destination']) + ); + } + + return File::copy( + Hyde::vendorPath(static::$options[$this->selected]['path']), + Hyde::path(static::$options[$this->selected]['destination']) + ); + } +} diff --git a/packages/framework/src/Commands/HydeBuildRssFeedCommand.php b/packages/framework/src/Commands/HydeBuildRssFeedCommand.php new file mode 100644 index 00000000000..40b9f095eeb --- /dev/null +++ b/packages/framework/src/Commands/HydeBuildRssFeedCommand.php @@ -0,0 +1,65 @@ +runPreflightCheck()) { + return 1; + } + + $this->comment('Generating RSS feed...'); + file_put_contents(Hyde::getSiteOutputPath(RssFeedService::getDefaultOutputFilename()), RssFeedService::generateFeed()); + $this->line(' > Created '.RssFeedService::getDefaultOutputFilename().' in '.$this->getExecutionTimeInMs($actionTime)."ms\n"); + + return 0; + } + + protected function runPreflightCheck(): bool + { + if (! RssFeedService::canGenerateFeed()) { + $this->error('Cannot generate an RSS feed, please check your configuration.'); + + return false; + } + + return true; + } + + protected function getExecutionTimeInMs(float $timeStart): string + { + return number_format(((microtime(true) - $timeStart) * 1000), 2); + } +} diff --git a/packages/framework/src/Commands/HydeBuildSearchCommand.php b/packages/framework/src/Commands/HydeBuildSearchCommand.php new file mode 100644 index 00000000000..73084251e04 --- /dev/null +++ b/packages/framework/src/Commands/HydeBuildSearchCommand.php @@ -0,0 +1,83 @@ +comment('Generating documentation site search index...'); + $this->line(' > This will take an estimated '.round($this->guesstimateGenerationTime() / 1000).' seconds. Terminal may seem non-responsive.'); + GeneratesDocumentationSearchIndexFile::run(); + + $this->line(' > Created '.GeneratesDocumentationSearchIndexFile::$filePath.' in '. + $this->getExecutionTimeInMs($actionTime)."ms\n"); + + if (config('docs.create_search_page', true)) { + $this->createSearchPage(); + } + + return 0; + } + + protected function createSearchPage(): void + { + $actionTime = microtime(true); + + $this->comment('Generating search page...'); + file_put_contents(Hyde::path('_site/'.config('docs.output_directory', 'docs').'/search.html'), + view('hyde::layouts.docs')->with([ + 'page' => new DocumentationPage([], '', 'Search', 'search'), + 'title' => 'Search', + 'markdown' => view('hyde::pages.documentation-search')->render(), + 'currentPage' => ''.config('docs.output_directory', 'docs').'/search', + ])->render()); + + $this->line(' > Created _site/'.config('docs.output_directory', 'docs').'/search.html in '. + $this->getExecutionTimeInMs($actionTime)."ms\n"); + } + + protected function guesstimateGenerationTime(): float + { + return count(CollectionService::getDocumentationPageList()) * 52.5; + } + + protected function getExecutionTimeInMs(float $timeStart): string + { + return number_format(((microtime(true) - $timeStart) * 1000), 2); + } +} diff --git a/packages/framework/src/Commands/HydeBuildSitemapCommand.php b/packages/framework/src/Commands/HydeBuildSitemapCommand.php new file mode 100644 index 00000000000..a12cb4c30a9 --- /dev/null +++ b/packages/framework/src/Commands/HydeBuildSitemapCommand.php @@ -0,0 +1,65 @@ +runPreflightCheck()) { + return 1; + } + + $this->comment('Generating sitemap...'); + file_put_contents(Hyde::getSiteOutputPath('sitemap.xml'), SitemapService::generateSitemap()); + $this->line(' > Created sitemap.xml in '.$this->getExecutionTimeInMs($actionTime)."ms\n"); + + return 0; + } + + protected function runPreflightCheck(): bool + { + if (! SitemapService::canGenerateSitemap()) { + $this->error('Cannot generate sitemap.xml, please check your configuration.'); + + return false; + } + + return true; + } + + protected function getExecutionTimeInMs(float $timeStart): string + { + return number_format(((microtime(true) - $timeStart) * 1000), 2); + } +} diff --git a/packages/framework/src/Commands/HydeBuildStaticSiteCommand.php b/packages/framework/src/Commands/HydeBuildStaticSiteCommand.php new file mode 100644 index 00000000000..62988e36fee --- /dev/null +++ b/packages/framework/src/Commands/HydeBuildStaticSiteCommand.php @@ -0,0 +1,227 @@ +title('Building your static site!'); + + $this->runPreBuildActions(); + + $this->purge(); + + $this->transferMediaAssets(); + + if (Features::hasBladePages()) { + $this->runBuildAction(BladePage::class); + } + + if (Features::hasMarkdownPages()) { + $this->runBuildAction(MarkdownPage::class); + } + + if (Features::hasBlogPosts()) { + $this->runBuildAction(MarkdownPost::class); + } + + if (Features::hasDocumentationPages()) { + $this->runBuildAction(DocumentationPage::class); + } + + $this->runPostBuildActions(); + + $this->printFinishMessage($time_start); + + return 0; + } + + /** @internal */ + protected function runPreBuildActions(): void + { + if ($this->option('no-api')) { + $this->info('Disabling external API calls'); + $this->newLine(); + $config = config('hyde.features'); + unset($config[array_search('torchlight', $config)]); + Config::set(['hyde.features' => $config]); + } + + if ($this->option('pretty-urls')) { + $this->info('Generating site with pretty URLs'); + $this->newLine(); + Config::set(['hyde.pretty_urls' => true]); + } + } + + /** + * Run any post-build actions. + * + * @return void + */ + public function runPostBuildActions(): void + { + if ($this->option('run-prettier') || $this->option('pretty')) { + if ($this->option('pretty')) { + $this->warn('Warning: The --pretty option is deprecated, use --run-prettier instead'); + } + $this->runNodeCommand( + 'npx prettier '.Hyde::pathToRelative(Hyde::getSiteOutputPath()).'/ --write --bracket-same-line', + 'Prettifying code!', + 'prettify code' + ); + } + + if ($this->option('run-dev')) { + $this->runNodeCommand('npm run dev', 'Building frontend assets for development!'); + } + + if ($this->option('run-prod')) { + $this->runNodeCommand('npm run prod', 'Building frontend assets for production!'); + } + + if ($this->canGenerateSitemap()) { + Artisan::call('build:sitemap', outputBuffer: $this->output); + } + + if ($this->canGenerateFeed()) { + Artisan::call('build:rss', outputBuffer: $this->output); + } + + if ($this->canGenerateSearch()) { + Artisan::call('build:search', outputBuffer: $this->output); + } + } + + /** @internal */ + protected function printFinishMessage(float $time_start): void + { + $time_end = microtime(true); + $execution_time = ($time_end - $time_start); + $this->info('All done! Finished in '.number_format( + $execution_time, + 2 + ).' seconds. ('.number_format(($execution_time * 1000), 2).'ms)'); + + $this->info('Congratulations! 🎉 Your static site has been built!'); + $this->line( + 'Your new homepage is stored here -> '. + DiscoveryService::createClickableFilepath(Hyde::getSiteOutputPath('index.html')) + ); + } + + /** + * Clear the entire output directory before running the build. + * + * @return void + */ + public function purge(): void + { + $this->warn('Removing all files from build directory.'); + + File::deleteDirectory(Hyde::getSiteOutputPath()); + mkdir(Hyde::getSiteOutputPath()); + + $this->line(' > Directory purged'); + + $this->line(' > Recreating directories'); + (new CreatesDefaultDirectories)->__invoke(); + + $this->line(''); + } + + /** @internal */ + protected function getModelPluralName(string $model): string + { + return preg_replace('/([a-z])([A-Z])/', '$1 $2', class_basename($model)).'s'; + } + + /* @internal */ + private function runNodeCommand(string $command, string $message, ?string $actionMessage = null): void + { + $this->info($message.' This may take a second.'); + + if (app()->environment() === 'testing') { + $command = 'echo '.$command; + } + $output = shell_exec($command); + + $this->line( + $output ?? 'Could not '.($actionMessage ?? 'run script').'! Is NPM installed?' + ); + } + + protected function canGenerateSitemap(): bool + { + return SitemapService::canGenerateSitemap(); + } + + protected function canGenerateFeed(): bool + { + return RssFeedService::canGenerateFeed() + && count(CollectionService::getMarkdownPostList()) > 0; + } + + protected function canGenerateSearch(): bool + { + return Features::hasDocumentationSearch() + && count(CollectionService::getDocumentationPageList()) > 0; + } +} diff --git a/packages/framework/src/Commands/HydeDebugCommand.php b/packages/framework/src/Commands/HydeDebugCommand.php new file mode 100644 index 00000000000..eea63504d09 --- /dev/null +++ b/packages/framework/src/Commands/HydeDebugCommand.php @@ -0,0 +1,73 @@ +setHidden(); + } + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle(): int + { + $this->info('HydePHP Debug Screen'); + + $this->newLine(); + $this->comment('Git Version: '.app('git.version')); + $this->comment('Hyde Version: '.app('hyde.version')); + $this->comment('Framework Version: '.app('framework.version')); + + $this->newLine(); + $this->comment('App Env: '.app('env')); + + $this->newLine(); + if ($this->getOutput()->isVerbose()) { + $this->line('Project directory:'); + $this->line(' > '.realpath(Hyde::path())); + $this->line('Framework vendor path:'); + $this->line(' > '.(str_replace('/', DIRECTORY_SEPARATOR, Hyde::vendorPath()).' (vendor)')); + $this->line(' > '.realpath(Hyde::vendorPath()).' (real)'); + } else { + $this->comment('Project directory: '.Hyde::path()); + } + + $this->newLine(); + + $this->line('Enabled features:'); + foreach (config('hyde.features') as $feature) { + $this->line(" - $feature"); + } + + return 0; + } +} diff --git a/packages/framework/src/Commands/HydeInstallCommand.php b/packages/framework/src/Commands/HydeInstallCommand.php new file mode 100644 index 00000000000..a4af51295f0 --- /dev/null +++ b/packages/framework/src/Commands/HydeInstallCommand.php @@ -0,0 +1,159 @@ +isInstalled()) { + $this->setHidden(); + } + } + + public function handle(): int + { + if ($this->option('mark-installed')) { + $this->info('Marking Hyde as installed and hiding the command!'); + $this->markInstalled(); + + return 0; + } + + $this->title('Welcome to HydePHP!'); + + $this->info('This guided installer is optional, but can help you to get set up quickly.'); + + $this->warn('Please note that this installer should not be run in existing projects.'); + + if (! $this->confirm('Do you want to continue?', true)) { + $this->comment('Aborting installation.'); + + return 130; + } + + $this->info('Installing HydePHP...'); + $this->newLine(); + + $this->call('update:configs'); + + $this->promptForSiteName(); + + $this->promptForSiteUrl(); + + $this->promptForHomepage(); + + $this->askToRebuildSite(); + + $this->markInstalled(); + + $this->newLine(); + + $this->line(' '); + $this->line(' HydePHP has been installed successfully! '); + $this->line(' Go build something great! '); + $this->line(' '); + + $this->newLine(); + + $this->info('What\'s next?'); + $this->newLine(); + $this->line(' > Run `hyde build` to build your site.'); + $this->line(' > Run `hyde serve` to start a development server that rebuilds your site on the fly.'); + $this->line(' > Run `hyde help` to get help for one of the commands.'); + $this->line(' > You can run `npm install` and `npm run dev` to compile any TailwindCSS assets.'); + $this->newLine(); + $this->line(' > You may also want to check out the HydePHP Docs'); + $this->newLine(); + + return 0; + } + + protected function promptForSiteName() + { + if ($this->siteName = $this->ask('What is the name of your site? (leave blank to skip)')) { + $this->updateSiteName(); + $this->info('Site name set to: '.$this->siteName.''); + + return; + } + + $this->line('Skipping site name.'); + } + + protected function promptForSiteUrl() + { + if ($this->siteUrl = $this->ask('What is the URL of your site? (leave blank to skip)')) { + $this->updateSiteUrl(); + $this->info('Site URL set to: '.$this->siteUrl.''); + + return; + } + + $this->line('Skipping site URL.'); + } + + protected function promptForHomepage() + { + $this->newLine(); + + $this->info('Hyde has a few different homepage options.'); + if ($this->confirm('Would you like to select an index.blade.php file?')) { + $this->call('publish:homepage'); + } else { + $this->line('Okay, leaving the default homepage.'); + } + } + + protected function updateSiteName(): void + { + $config = file_get_contents(Hyde::path('config/hyde.php')); + $config = str_replace( + "'name' => ".'$siteName'." = env('SITE_NAME', 'HydePHP'),", + "'name' => ".'$siteName'." = env('SITE_NAME', '".$this->siteName."'),", + $config + ); + file_put_contents(Hyde::path('config/hyde.php'), $config); + } + + protected function updateSiteUrl(): void + { + $config = file_get_contents(Hyde::path('config/hyde.php')); + $config = str_replace( + "'site_url' => env('SITE_URL', null),", + "'site_url' => env('SITE_URL', '".$this->siteUrl."'),", + $config + ); + file_put_contents(Hyde::path('config/hyde.php'), $config); + } + + protected function markInstalled(): void + { + Cache::forever('hyde.installed', true); + } + + protected function isInstalled(): bool + { + return Cache::get('hyde.installed', false) === true; + } +} diff --git a/packages/framework/src/Commands/HydeMakePageCommand.php b/packages/framework/src/Commands/HydeMakePageCommand.php new file mode 100644 index 00000000000..5f75a4ab66e --- /dev/null +++ b/packages/framework/src/Commands/HydeMakePageCommand.php @@ -0,0 +1,105 @@ +title('Creating a new page!'); + + $this->title = $this->argument('title') + ?? $this->ask('What is the title of the page?') + ?? 'My New Page'; + + $this->line('Creating page with title: '.$this->title."\n"); + + $this->validateOptions(); + + $this->force = $this->option('force') ?? false; + + $creator = new CreatesNewPageSourceFile($this->title, $this->type, $this->force); + + $this->info("Created file $creator->outputPath"); + + return 0; + } + + /** + * Validate the options passed to the command. + * + * @return void + * + * @throws UnsupportedPageTypeException if the page type is invalid. + */ + protected function validateOptions(): void + { + $type = strtolower($this->option('type') ?? 'markdown'); + + // Set the type to the fully qualified class name + if ($type === 'markdown') { + $this->type = MarkdownPage::class; + + return; + } + if ($type === 'blade') { + $this->type = BladePage::class; + + return; + } + if ($type === 'docs' || $type === 'documentation') { + $this->type = DocumentationPage::class; + + return; + } + + throw new UnsupportedPageTypeException("Invalid page type: $type", 400); + } +} diff --git a/packages/framework/src/Commands/HydeMakePostCommand.php b/packages/framework/src/Commands/HydeMakePostCommand.php new file mode 100644 index 00000000000..427d6db9d0b --- /dev/null +++ b/packages/framework/src/Commands/HydeMakePostCommand.php @@ -0,0 +1,90 @@ +title('Creating a new post!'); + + $this->line( + $this->argument('title') + ? 'Selected title: '.$this->argument('title')."\n" + : 'Please enter the title of the post, it will be used to generate the slug.' + ); + + $title = $this->argument('title') + ?? $this->ask('What is the title of the post?') + ?? 'My New Post'; + + $this->line('Tip: You can just hit return to use the defaults.'); + $description = $this->ask('Write a short post excerpt/description'); + $author = $this->ask('What is your (the author\'s) name?'); + $category = $this->ask('What is the primary category of the post?'); + + $this->info('Creating a post with the following details:'); + $creator = new CreatesNewMarkdownPostFile( + title: $title, + description: $description, + category: $category, + author: $author + ); + + $this->line("Title: $creator->title"); + $this->line("Description: $creator->description"); + $this->line("Author: $creator->author"); + $this->line("Category: $creator->category"); + $this->line("Date: $creator->date"); + $this->line("Slug: $creator->slug"); + + if (! $this->confirm('Do you wish to continue?', true)) { + $this->info('Aborting.'); + + return 130; + } + + try { + $path = $creator->save($this->option('force')); + $this->info("Post created! File is saved to $path"); + + return 0; + } catch (Exception $exception) { + $this->error('Something went wrong when trying to save the file!'); + $this->warn($exception->getMessage()); + if ($exception->getCode() === 409) { + $this->comment('If you want to overwrite the file supply the --force flag.'); + } + + return $exception->getCode(); + } + } +} diff --git a/packages/framework/src/Commands/HydePublishHomepageCommand.php b/packages/framework/src/Commands/HydePublishHomepageCommand.php new file mode 100644 index 00000000000..027649d3a07 --- /dev/null +++ b/packages/framework/src/Commands/HydePublishHomepageCommand.php @@ -0,0 +1,101 @@ +selected = $this->argument('homepage') ?? $this->promptForHomepage(); + + $returnValue = (new PublishesHomepageView( + $this->selected, + $this->canExistingIndexFileBeOverwritten() + ))->execute(); + + // @deprecated version 0.10.0, can be removed as it should not be possible to select a homepage that does not exist, and we can make a pre-check for 409 case. + if ($returnValue === true) { + $this->info('Homepage published successfully!'); + } else { + if (is_numeric($returnValue)) { + if ($returnValue == 404) { + $this->error('Homepage '.$this->selected.' does not exist.'); + + return 404; + } + + if ($returnValue == 409) { + $this->error('A modified index.blade.php file already exists. Use --force to overwrite.'); + + return 409; + } + } + } + + $this->askToRebuildSite(); + + return 0; + } + + protected function promptForHomepage(): string + { + $choice = $this->choice( + 'Which homepage do you want to publish?', + $this->formatPublishableChoices(), + 0 + ); + + $choice = $this->parseChoiceIntoKey($choice); + + $this->line("Selected page [$choice]"); + $this->newLine(); + + return $choice; + } + + protected function formatPublishableChoices(): array + { + $keys = []; + foreach (PublishesHomepageView::$homePages as $key => $value) { + $keys[] = "$key: {$value['description']}"; + } + + return $keys; + } + + protected function parseChoiceIntoKey(string $choice): string + { + return strstr(str_replace(['', ''], '', $choice), ':', true); + } + + protected function canExistingIndexFileBeOverwritten(): bool + { + if (! file_exists(Hyde::getBladePagePath('index.blade.php')) || $this->option('force')) { + return true; + } + + return FileCacheService::checksumMatchesAny(FileCacheService::unixsumFile( + Hyde::getBladePagePath('index.blade.php') + )) ?? $this->option('force'); + } +} diff --git a/packages/framework/src/Commands/HydePublishViewsCommand.php b/packages/framework/src/Commands/HydePublishViewsCommand.php new file mode 100644 index 00000000000..c5426bc1233 --- /dev/null +++ b/packages/framework/src/Commands/HydePublishViewsCommand.php @@ -0,0 +1,78 @@ +selected = $this->argument('category') ?? $this->promptForCategory(); + + if ($this->selected === 'all' || $this->selected === '') { + foreach (PublishesHydeViews::$options as $key => $value) { + $this->publishOption($key); + } + } else { + $this->publishOption($this->selected); + } + + return 0; + } + + protected function publishOption($selected) + { + (new PublishesHydeViews($selected))->execute(); + + $from = Hyde::vendorPath(PublishesHydeViews::$options[$selected]['path']); + $from = substr($from, strpos($from, 'vendor')); + + $to = (PublishesHydeViews::$options[$selected]['destination']); + + $this->line('Copied ['."$from".'] to ['."$to".']'); + } + + protected function promptForCategory(): string + { + $choice = $this->choice( + 'Which category do you want to publish?', + $this->formatPublishableChoices(), + 0 + ); + + $choice = $this->parseChoiceIntoKey($choice); + + $this->line('Selected category ['.(empty($choice) ? 'all' : $choice).']'); + $this->newLine(); + + return $choice; + } + + protected function formatPublishableChoices(): array + { + $keys = []; + $keys[] = 'Publish all categories listed below'; + foreach (PublishesHydeViews::$options as $key => $value) { + $keys[] = "$key: {$value['description']}"; + } + + return $keys; + } + + protected function parseChoiceIntoKey(string $choice): string + { + return strstr(str_replace(['', ''], '', $choice), ':', true); + } +} diff --git a/packages/framework/src/Commands/HydeRebuildStaticSiteCommand.php b/packages/framework/src/Commands/HydeRebuildStaticSiteCommand.php new file mode 100644 index 00000000000..98b339a0f71 --- /dev/null +++ b/packages/framework/src/Commands/HydeRebuildStaticSiteCommand.php @@ -0,0 +1,158 @@ +argument('path') === '_media') { + $this->transferMediaAssets(); + + return 0; + } + + $this->path = $this->sanitizePathString($this->argument('path')); + + try { + $this->validate(); + } catch (Exception $exception) { + return $this->handleException($exception); + } + + (new RebuildService($this->path))->execute(); + + $time_end = microtime(true); + $execution_time = ($time_end - $time_start); + + $this->info(sprintf( + 'Created %s in %s seconds. (%sms)', + DiscoveryService::createClickableFilepath($this->getOutputPath($this->path)), + number_format( + $execution_time, + 2 + ), + number_format(($execution_time * 1000), 2) + )); + + return 0; + } + + /** + * Perform a basic sanitation to strip trailing characters. + * + * @param string $path + * @return string + */ + public function sanitizePathString(string $path): string + { + return str_replace('\\', '/', trim($path, '.\\/')); + } + + /** + * Validate the path to catch common errors. + * + * @throws Exception + */ + public function validate(): void + { + if (! ( + str_starts_with($this->path, Hyde::pathToRelative(Hyde::getDocumentationPagePath())) || + str_starts_with($this->path, Hyde::pathToRelative(Hyde::getMarkdownPostPath())) || + str_starts_with($this->path, Hyde::pathToRelative(Hyde::getBladePagePath())) || + str_starts_with($this->path, Hyde::pathToRelative(Hyde::getMarkdownPostPath())) + )) { + throw new Exception("Path [$this->path] is not in a valid source directory.", 400); + } + + if (! file_exists(Hyde::path($this->path))) { + throw new Exception("File [$this->path] not found.", 404); + } + } + + /** + * Output the contents of an exception. + * + * @param Exception $exception + * @return int Error code + */ + public function handleException(Exception $exception): int + { + $this->error('Something went wrong!'); + $this->warn($exception->getMessage()); + + return $exception->getCode(); + } + + /** + * Get the output path for the given source file path. + * Will fall back to the input path when using non-standard source paths. + * + * @deprecated reimplementing path information in StaticPageBuilder, + * alternatively, recreating in the DiscoveryService + * + * @param string $path + * @return string + */ + public function getOutputPath(string $path): string + { + $path = str_replace(Hyde::path(), '', $path); + + if (str_starts_with($path, '_posts')) { + return Hyde::path(str_replace('_posts', '_site/posts', rtrim($path, '.md').'.html')); + } + + if (str_starts_with($path, '_docs')) { + return Hyde::path(str_replace('_docs', '_site/docs', rtrim($path, '.md').'.html')); + } + + if (str_starts_with($path, '_pages')) { + $path = str_replace('.blade.php', '.md', $path); + + return Hyde::path(str_replace('_pages', '_site/', rtrim($path, '.md').'.html')); + } + + return $path; + } +} diff --git a/packages/framework/src/Commands/HydeServeCommand.php b/packages/framework/src/Commands/HydeServeCommand.php new file mode 100644 index 00000000000..3d3eb1829ac --- /dev/null +++ b/packages/framework/src/Commands/HydeServeCommand.php @@ -0,0 +1,48 @@ +line('Starting the server... Press Ctrl+C to stop'); + + $this->warn('Running experimental HydeRC 2.0. Please report any issues on GitHub.'); + + $port = $this->option('port'); + $host = $this->option('host'); + $command = "php -S $host:$port ".Hyde::path('vendor/hyde/realtime-compiler/bin/server.php'); + if (app()->environment('testing')) { + $command = 'echo '.$command; + } + passthru($command); + + return 0; + } +} diff --git a/packages/framework/src/Commands/HydeUpdateConfigsCommand.php b/packages/framework/src/Commands/HydeUpdateConfigsCommand.php new file mode 100644 index 00000000000..f0bbed21a29 --- /dev/null +++ b/packages/framework/src/Commands/HydeUpdateConfigsCommand.php @@ -0,0 +1,45 @@ +checkIfConfigIsOutOfDate() && config('hyde.warn_about_outdated_config', true)) { + $this->setDescription( + '⚠ Your configuration may be out of date. '. + 'Run this command to update them.' + ); + } + } + + public function handle(): int + { + File::copyDirectory(Hyde::vendorPath('config'), Hyde::path('config')); + + $this->line('Published config files to '.Hyde::path('config').''); + + return 0; + } + + protected function checkIfConfigIsOutOfDate(): bool + { + return ! (new ChecksIfConfigIsUpToDate)->execute(); + } +} diff --git a/packages/framework/src/Commands/HydeValidateCommand.php b/packages/framework/src/Commands/HydeValidateCommand.php new file mode 100644 index 00000000000..df86bc9b3c1 --- /dev/null +++ b/packages/framework/src/Commands/HydeValidateCommand.php @@ -0,0 +1,42 @@ +info('Running validation tests!'); + + $this->line(shell_exec(Hyde::path('vendor/bin/pest').' --group=validators')); + + $this->info('All done!'); + + return 0; + } +} diff --git a/packages/framework/src/Concerns/Commands/AsksToRebuildSite.php b/packages/framework/src/Concerns/Commands/AsksToRebuildSite.php new file mode 100644 index 00000000000..a85350e3a60 --- /dev/null +++ b/packages/framework/src/Concerns/Commands/AsksToRebuildSite.php @@ -0,0 +1,26 @@ +option('no-interaction')) { + return; + } + + if ($this->confirm('Would you like to rebuild the site?', 'Yes')) { + $this->line('Okay, building site!'); + Artisan::call('build'); + $this->info('Site is built!'); + } else { + $this->line('Okay, you can always run the build later!'); + } + } +} diff --git a/packages/framework/src/Concerns/FacadeHelpers/HydeSmartDocsFacade.php b/packages/framework/src/Concerns/FacadeHelpers/HydeSmartDocsFacade.php new file mode 100644 index 00000000000..a540e0cb6fa --- /dev/null +++ b/packages/framework/src/Concerns/FacadeHelpers/HydeSmartDocsFacade.php @@ -0,0 +1,32 @@ +process(); + } + + public function hasTorchlight(): bool + { + return Features::hasTorchlight() && str_contains($this->html, 'Syntax highlighted by torchlight.dev'); + } + + protected function canRenderSourceLink(string $inPosition): bool + { + $config = config('docs.edit_source_link_position', 'both'); + $positions = $config === 'both' ? ['header', 'footer'] : [$config]; + + return ($this->page->getOnlineSourcePath() !== false) && in_array($inPosition, $positions); + } +} diff --git a/packages/framework/src/Concerns/GeneratesPageMetadata.php b/packages/framework/src/Concerns/GeneratesPageMetadata.php new file mode 100644 index 00000000000..628d9b212a4 --- /dev/null +++ b/packages/framework/src/Concerns/GeneratesPageMetadata.php @@ -0,0 +1,104 @@ +parseFrontMatterMetadata(); + + if ($this instanceof MarkdownPost || $this instanceof TestCase) { + $this->makeOpenGraphPropertiesForArticle(); + } + } + + public function getMetadata(): array + { + return $this->metadata; + } + + public function getMetaProperties(): array + { + return $this->properties; + } + + /** + * Generate metadata from the front matter that can be used in standard tags. + * This helper is page type agnostic and works with any kind of model having front matter. + */ + protected function parseFrontMatterMetadata(): void + { + if (isset($this->matter['description'])) { + $this->metadata['description'] = $this->matter['description']; + } + + if (isset($this->matter['author'])) { + $this->metadata['author'] = $this->getAuthorName($this->matter['author']); + } + + if (isset($this->matter['category'])) { + $this->metadata['keywords'] = $this->matter['category']; + } + } + + /** + * Generate opengraph metadata from front matter for an og:article such as a blog post. + */ + protected function makeOpenGraphPropertiesForArticle(): void + { + $this->properties['og:type'] = 'article'; + if (Hyde::uriPath()) { + $this->properties['og:url'] = Hyde::uriPath(Hyde::pageLink('posts/'.$this->slug.'.html')); + } + + if (isset($this->matter['title'])) { + $this->properties['og:title'] = $this->matter['title']; + } + + if (isset($this->matter['date'])) { + $this->properties['og:article:published_time'] = date('c', strtotime($this->matter['date'])); + } + + if (isset($this->matter['image'])) { + if (is_string($this->matter['image'])) { + $this->properties['og:image'] = $this->matter['image']; + } else { + if (isset($this->matter['image']['path'])) { + $this->properties['og:image'] = $this->matter['image']['path']; + } + if (isset($this->matter['image']['uri'])) { + $this->properties['og:image'] = $this->matter['image']['uri']; + } + } + } + } + + /** + * Parse the author name string from front matter with support for both flat and array notation. + * + * @param string|array $author + * @return string + */ + protected function getAuthorName(string|array $author): string + { + if (is_string($author)) { + return $author; + } + + return $author['name'] ?? $author['username'] ?? 'Guest'; + } +} diff --git a/packages/framework/src/Concerns/HasAuthor.php b/packages/framework/src/Concerns/HasAuthor.php new file mode 100644 index 00000000000..9de225acf25 --- /dev/null +++ b/packages/framework/src/Concerns/HasAuthor.php @@ -0,0 +1,46 @@ +matter['author'])) { + if (is_string($this->matter['author'])) { + // If the author is a string, we assume it's a username + // and we'll try to find the author in the config + $this->author = $this->findAuthor($this->matter['author']); + } + if (is_array($this->matter['author'])) { + // If the author is an array, we'll assume it's a user + // with one-off custom data, so we create a new author. + // In the future we may want to merge config data with custom data + $this->author = $this->createAuthor($this->matter['author']); + } + } + } + + protected function findAuthor(string $author): Author + { + return AuthorHelper::get($author); + } + + protected function createAuthor(array $data): Author + { + $username = $data['username'] ?? $data['name'] ?? 'Guest'; + + return new Author($username, $data); + } +} diff --git a/packages/framework/src/Concerns/HasDateString.php b/packages/framework/src/Concerns/HasDateString.php new file mode 100644 index 00000000000..2b1ec719375 --- /dev/null +++ b/packages/framework/src/Concerns/HasDateString.php @@ -0,0 +1,25 @@ +matter['date'])) { + $this->date = new DateString($this->matter['date']); + } + } +} diff --git a/packages/framework/src/Concerns/HasDocumentationSidebarCategories.php b/packages/framework/src/Concerns/HasDocumentationSidebarCategories.php new file mode 100644 index 00000000000..376c8edaf21 --- /dev/null +++ b/packages/framework/src/Concerns/HasDocumentationSidebarCategories.php @@ -0,0 +1,65 @@ +assembleCategories(); + + return ! empty($this->categories); + } + + public function getCategories(): array + { + $this->assembleCategories(); + + return $this->categories; + } + + public function getItemsInCategory(string $category): DocumentationSidebar + { + return $this->sidebar->filter(function ($item) use ($category) { + return $item->category === Str::slug($category); + })->sortBy('priority')->values(); + } + + protected function assembleCategories(): void + { + foreach ($this->sidebar->sortItems() as $item) { + if (isset($item->category)) { + if (! in_array($item->category, $this->categories)) { + $this->categories[] = $item->category; + } + } + } + + if (! empty($this->categories)) { + $this->setCategoryOfUncategorizedItems(); + } + } + + protected function setCategoryOfUncategorizedItems(): void + { + foreach ($this->sidebar as $item) { + if (! isset($item->category)) { + $item->category = 'other'; + + if (! in_array('other', $this->categories)) { + $this->categories[] = 'other'; + } + } + } + } +} diff --git a/packages/framework/src/Concerns/HasDynamicTitle.php b/packages/framework/src/Concerns/HasDynamicTitle.php new file mode 100644 index 00000000000..090ea0bd30f --- /dev/null +++ b/packages/framework/src/Concerns/HasDynamicTitle.php @@ -0,0 +1,41 @@ +matter['title'])) { + return $this->matter['title']; + } + + return $this->findTitleTagInMarkdown($this->body) + ?: Hyde::titleFromSlug($this->slug); + } + + /** + * Attempt to find the title based on the first H1 tag. + */ + protected function findTitleTagInMarkdown(string $stream): string|false + { + $lines = explode("\n", $stream); + + foreach ($lines as $line) { + if (str_starts_with($line, '# ')) { + return trim(substr($line, 2), ' '); + } + } + + return false; + } +} diff --git a/packages/framework/src/Concerns/HasFeaturedImage.php b/packages/framework/src/Concerns/HasFeaturedImage.php new file mode 100644 index 00000000000..bf78b8e8519 --- /dev/null +++ b/packages/framework/src/Concerns/HasFeaturedImage.php @@ -0,0 +1,45 @@ +matter['image'])) { + if (is_string($this->matter['image'])) { + $this->image = $this->constructBaseImage($this->matter['image']); + } + if (is_array($this->matter['image'])) { + $this->image = $this->constructFullImage($this->matter['image']); + } + } + } + + public function constructBaseImage(string $image): Image + { + if (str_starts_with($image, 'http')) { + return new Image([ + 'uri' => $image, + ]); + } + + return new Image([ + 'path' => $image, + ]); + } + + public function constructFullImage(array $image): Image + { + return new Image($image); + } +} diff --git a/packages/framework/src/Concerns/HasPageMetadata.php b/packages/framework/src/Concerns/HasPageMetadata.php new file mode 100644 index 00000000000..502976a9045 --- /dev/null +++ b/packages/framework/src/Concerns/HasPageMetadata.php @@ -0,0 +1,113 @@ +getCurrentPagePath().'.html')); + } + + public function getDynamicMetadata(): array + { + $array = []; + + if ($this->canUseCanonicalUrl()) { + $array[] = ''; + } + + if ($this->canUseSitemapLink()) { + $array[] = ''; + } + + if ($this->canUseRssFeedlink()) { + $array[] = ''; + } + + if (isset($this->title)) { + if ($this->hasTwitterTitleInConfig()) { + $array[] = ''; + } + if ($this->hasOpenGraphTitleInConfig()) { + $array[] = ''; + } + } + + if ($this instanceof MarkdownPost) { + // Temporarily merge data with GeneratesPageMetadata trait for compatibility + $array[] = "\n"; + foreach ($this->getMetadata() as $name => $content) { + $array[] = Meta::name($name, $content); + } + foreach ($this->getMetaProperties() as $property => $content) { + $array[] = Meta::property($property, $content); + } + } + + return $array; + } + + public function renderPageMetadata(): string + { + $dynamicMetadata = $this->getDynamicMetadata(); + + return Meta::render( + $dynamicMetadata + ); + } + + public function canUseCanonicalUrl(): bool + { + return Hyde::uriPath() && isset($this->slug); + } + + public function canUseSitemapLink(): bool + { + return SitemapService::canGenerateSitemap(); + } + + public function canUseRssFeedLink(): bool + { + if (RssFeedService::canGenerateFeed() && isset($this->slug)) { + if ($this instanceof MarkdownPost) { + return true; + } + + if (str_starts_with($this->getCurrentPagePath(), 'post')) { + return true; + } + + if ($this->getCurrentPagePath() === 'index') { + return true; + } + } + + return false; + } + + public function hasTwitterTitleInConfig(): bool + { + return str_contains(json_encode(config('hyde.meta', [])), 'twitter:title'); + } + + public function hasOpenGraphTitleInConfig(): bool + { + return str_contains(json_encode(config('hyde.meta', [])), 'og:title'); + } +} diff --git a/packages/framework/src/Concerns/HasTableOfContents.php b/packages/framework/src/Concerns/HasTableOfContents.php new file mode 100644 index 00000000000..e38ffbe8260 --- /dev/null +++ b/packages/framework/src/Concerns/HasTableOfContents.php @@ -0,0 +1,22 @@ +tableOfContents = (new GeneratesTableOfContents($this->body))->execute(); + } + } +} diff --git a/packages/framework/src/Concerns/InteractsWithDirectories.php b/packages/framework/src/Concerns/InteractsWithDirectories.php new file mode 100644 index 00000000000..3e6c5b41297 --- /dev/null +++ b/packages/framework/src/Concerns/InteractsWithDirectories.php @@ -0,0 +1,33 @@ +needsDirectory($directory); + } + } +} diff --git a/packages/framework/src/Concerns/Internal/AssetManager.php b/packages/framework/src/Concerns/Internal/AssetManager.php new file mode 100644 index 00000000000..2e521a7d2e9 --- /dev/null +++ b/packages/framework/src/Concerns/Internal/AssetManager.php @@ -0,0 +1,39 @@ +stylePath(); + } + + /** + * Return the Hyde scripts. + */ + public static function scripts(): string + { + return static::assetManager()->scriptPath(); + } +} diff --git a/packages/framework/src/Concerns/Internal/BuildActionRunner.php b/packages/framework/src/Concerns/Internal/BuildActionRunner.php new file mode 100644 index 00000000000..c4c9a359b41 --- /dev/null +++ b/packages/framework/src/Concerns/Internal/BuildActionRunner.php @@ -0,0 +1,54 @@ +line('No '.$name.' found. Skipping...'); + $this->newLine(); + + return false; + } + + $this->comment(($verb ?? 'Creating')." $name..."); + + return true; + } + + /** @internal */ + protected function runBuildAction(string $model): void + { + $collection = CollectionService::getSourceFileListForModel($model); + $modelName = $this->getModelPluralName($model); + if ($this->canRunBuildAction($collection, $modelName)) { + $this->withProgressBar( + $collection, + function ($basename) use ($model) { + new StaticPageBuilder( + DiscoveryService::getParserInstanceForModel( + $model, + $basename + )->get(), + true + ); + } + ); + $this->newLine(2); + } + } +} diff --git a/packages/framework/src/Concerns/Internal/FileHelpers.php b/packages/framework/src/Concerns/Internal/FileHelpers.php new file mode 100644 index 00000000000..83b7f29d73c --- /dev/null +++ b/packages/framework/src/Concerns/Internal/FileHelpers.php @@ -0,0 +1,171 @@ + 0) { + $route .= str_repeat('../', $nestCount); + } + $route .= static::pageLink($destination); + + return str_replace('//', '/', $route); + } + + /** + * Gets a relative link to the given image stored in the _site/media folder. + */ + public static function image(string $name, string $current = ''): string + { + if (str_starts_with($name, 'http')) { + return $name; + } + + return static::relativeLink('media/'.basename($name), $current); + } + + /** + * Return a qualified URI path, if SITE_URL is set in .env, else return false. + * + * @param string|null $path optional relative path suffix. Omit to return base url. + * @return string|false + */ + public static function uriPath(?string $path = ''): string|false + { + if (config('hyde.site_url', false)) { + return rtrim(config('hyde.site_url'), '/').'/'.(trim($path, '/') ?? ''); + } + + return false; + } + + /** + * Wrapper for the copy function, but allows choosing if files may be overwritten. + * + * @param string $from The source file path. + * @param string $to The destination file path. + * @param bool $force If true, existing files will be overwritten. + * @return bool|int Returns true|false on copy() success|failure, or an error code on failure + */ + public static function copy(string $from, string $to, bool $force = false): bool|int + { + if (! file_exists($from)) { + return 404; + } + + if (file_exists($to) && ! $force) { + return 409; + } + + return copy($from, $to); + } +} diff --git a/packages/framework/src/Concerns/Internal/FluentPathHelpers.php b/packages/framework/src/Concerns/Internal/FluentPathHelpers.php new file mode 100644 index 00000000000..20ec0f3e04c --- /dev/null +++ b/packages/framework/src/Concerns/Internal/FluentPathHelpers.php @@ -0,0 +1,80 @@ +needsDirectory(Hyde::getSiteOutputPath('media')); + + $collection = CollectionService::getMediaAssetFiles(); + if ($this->canRunBuildAction($collection, 'Media Assets', 'Transferring')) { + $this->withProgressBar( + $collection, + function ($filepath) { + copy($filepath, Hyde::getSiteOutputPath('media/'.basename($filepath))); + } + ); + $this->newLine(2); + } + } +} diff --git a/packages/framework/src/Concerns/Markdown/HasConfigurableMarkdownFeatures.php b/packages/framework/src/Concerns/Markdown/HasConfigurableMarkdownFeatures.php new file mode 100644 index 00000000000..2f5ca27be00 --- /dev/null +++ b/packages/framework/src/Concerns/Markdown/HasConfigurableMarkdownFeatures.php @@ -0,0 +1,78 @@ +features)) { + $this->features[] = $feature; + } + + return $this; + } + + public function removeFeature(string $feature): self + { + if (in_array($feature, $this->features)) { + $this->features = array_diff($this->features, [$feature]); + } + + return $this; + } + + public function withTableOfContents(): self + { + $this->addFeature('table-of-contents'); + + return $this; + } + + public function withPermalinks(): self + { + $this->addFeature('permalinks'); + + return $this; + } + + public function hasFeature(string $feature): bool + { + return in_array($feature, $this->features); + } + + public function canEnablePermalinks(): bool + { + if ($this->hasFeature('permalinks')) { + return true; + } + + if ($this->isDocumentationPage() && Markdown::hasTableOfContents()) { + return true; + } + + return false; + } + + public function isDocumentationPage(): bool + { + return isset($this->sourceModel) && $this->sourceModel === DocumentationPage::class; + } + + public function canEnableTorchlight(): bool + { + return $this->hasFeature('torchlight') || + Features::hasTorchlight(); + } +} diff --git a/packages/framework/src/Concerns/Markdown/HasMarkdownFeatures.php b/packages/framework/src/Concerns/Markdown/HasMarkdownFeatures.php new file mode 100644 index 00000000000..889cf29ff3b --- /dev/null +++ b/packages/framework/src/Concerns/Markdown/HasMarkdownFeatures.php @@ -0,0 +1,16 @@ +isDocumentationPage() + && config('torchlight.attribution.enabled', true) + && str_contains($this->html, 'Syntax highlighted by torchlight.dev'); + } + + protected function injectTorchlightAttribution(): string + { + return '
'.$this->converter->convert(config( + 'torchlight.attribution.markdown', + 'Syntax highlighted by torchlight.dev' + )); + } +} diff --git a/packages/framework/src/Concerns/RegistersDefaultDirectories.php b/packages/framework/src/Concerns/RegistersDefaultDirectories.php new file mode 100644 index 00000000000..0507726365e --- /dev/null +++ b/packages/framework/src/Concerns/RegistersDefaultDirectories.php @@ -0,0 +1,22 @@ + $location) { + /** @var AbstractPage $class */ + $class::$sourceDirectory = $location; + } + } +} diff --git a/packages/framework/src/Concerns/ValidatesExistence.php b/packages/framework/src/Concerns/ValidatesExistence.php new file mode 100644 index 00000000000..148392eeb20 --- /dev/null +++ b/packages/framework/src/Concerns/ValidatesExistence.php @@ -0,0 +1,30 @@ +slug; + } + + public static function all(): Collection + { + $collection = new Collection(); + + foreach (CollectionService::getSourceFileListForModel(static::class) as $filepath) { + $collection->push((new static::$parserClass(basename($filepath, static::$fileExtension)))->get()); + } + + return $collection; + } +} diff --git a/packages/framework/src/Contracts/AbstractPageParser.php b/packages/framework/src/Contracts/AbstractPageParser.php new file mode 100644 index 00000000000..8530ff6740f --- /dev/null +++ b/packages/framework/src/Contracts/AbstractPageParser.php @@ -0,0 +1,43 @@ +slug = $slug; + $this->validateExistence($this->pageModel, $slug); + $this->execute(); + } +} diff --git a/packages/framework/src/Contracts/ActionContract.php b/packages/framework/src/Contracts/ActionContract.php new file mode 100644 index 00000000000..64e97fcc0d0 --- /dev/null +++ b/packages/framework/src/Contracts/ActionContract.php @@ -0,0 +1,14 @@ + + * + * @see \Tests\Unit\PageModelGetHelperTest + */ + public static function all(): Collection; +} diff --git a/packages/framework/src/Contracts/PageParserContract.php b/packages/framework/src/Contracts/PageParserContract.php new file mode 100644 index 00000000000..4e2b7bc2103 --- /dev/null +++ b/packages/framework/src/Contracts/PageParserContract.php @@ -0,0 +1,20 @@ +message = $path ? "File already exists: {$path}" : $this->message; + } +} diff --git a/packages/framework/src/Exceptions/FileNotFoundException.php b/packages/framework/src/Exceptions/FileNotFoundException.php new file mode 100644 index 00000000000..df8241ae6fe --- /dev/null +++ b/packages/framework/src/Exceptions/FileNotFoundException.php @@ -0,0 +1,16 @@ +message = $path ? "File {$path} not found." : $this->message; + } +} diff --git a/packages/framework/src/Exceptions/UnsupportedPageTypeException.php b/packages/framework/src/Exceptions/UnsupportedPageTypeException.php new file mode 100644 index 00000000000..a2ecb496ce3 --- /dev/null +++ b/packages/framework/src/Exceptions/UnsupportedPageTypeException.php @@ -0,0 +1,16 @@ +message = $page ? "The page type is not supported: {$page}" : $this->message; + } +} diff --git a/packages/framework/src/Helpers/Author.php b/packages/framework/src/Helpers/Author.php new file mode 100644 index 00000000000..e4a647a1a78 --- /dev/null +++ b/packages/framework/src/Helpers/Author.php @@ -0,0 +1,31 @@ + $name, + 'website'=> $website, + ]); + } + + public static function all(): Collection + { + return new Collection(config('authors', [])); + } + + public static function get(string $username): AuthorModel + { + return static::all()->firstWhere('username', $username) + ?? static::create($username); + } +} diff --git a/packages/framework/src/Helpers/Features.php b/packages/framework/src/Helpers/Features.php new file mode 100644 index 00000000000..36140a60dfb --- /dev/null +++ b/packages/framework/src/Helpers/Features.php @@ -0,0 +1,119 @@ +'; + } + + public static function property(string $property, string $content): string + { + $property = static::formatOpenGraphProperty($property); + + return ''; + } + + public static function render(array $overridesGlobalMeta = []): string + { + return implode("\n", + static::filterUnique( + array_merge( + static::getGlobalMeta(), + $overridesGlobalMeta + ) + ) + ); + } + + protected static function filterUnique(array $meta): array + { + $array = []; + $existing = []; + + foreach (array_reverse($meta) as $metaItem) { + $substring = substr($metaItem, 6, strpos($metaItem, ' content="') - 6); + + if (! in_array($substring, $existing)) { + $array[] = $metaItem; + $existing[] = $substring; + } + } + + return array_reverse($array); + } + + public static function getGlobalMeta(): array + { + return config('hyde.meta', []); + } + + protected static function formatOpenGraphProperty(string $property): string + { + return str_starts_with($property, 'og:') ? $property : 'og:'.$property; + } +} diff --git a/packages/framework/src/Hyde.php b/packages/framework/src/Hyde.php new file mode 100644 index 00000000000..1ca79b7c895 --- /dev/null +++ b/packages/framework/src/Hyde.php @@ -0,0 +1,70 @@ + + * @copyright 2022 Caen De Silva + * @license MIT License + * + * @link https://hydephp.com/ + */ +class Hyde +{ + use FileHelpers; + use AssetManager; + use FluentPathHelpers; + use HydeHelperFacade; + + protected static string $basePath; + + public static function version(): string + { + return InstalledVersions::getPrettyVersion('hyde/framework') ?: 'unreleased'; + } + + public static function getBasePath(): string + { + if (! isset(static::$basePath)) { + static::$basePath = getcwd(); + } + + return static::$basePath; + } + + public static function setBasePath(string $path): void + { + static::$basePath = $path; + } + + public static function titleFromSlug(string $slug): string + { + return Str::title(str_replace('-', ' ', ($slug))); + } + + /** + * @deprecated v0.34.x Use MarkdownPost::getLatestPosts() instead. + */ + public static function getLatestPosts(): Collection + { + $collection = new Collection(); + + foreach (CollectionService::getMarkdownPostList() as $filepath) { + $collection->push((new MarkdownPostParser(basename($filepath, '.md')))->get()); + } + + return $collection->sortByDesc('matter.date'); + } +} diff --git a/packages/framework/src/HydeServiceProvider.php b/packages/framework/src/HydeServiceProvider.php new file mode 100644 index 00000000000..a8a5d112a4f --- /dev/null +++ b/packages/framework/src/HydeServiceProvider.php @@ -0,0 +1,133 @@ +app->bind( + 'hyde.version', + function () { + return InstalledVersions::getPrettyVersion('hyde/hyde') ?: 'unreleased'; + } + ); + + /** + * @deprecated + */ + $this->app->bind( + 'framework.version', + function () { + return InstalledVersions::getPrettyVersion('hyde/framework') ?: 'unreleased'; + } + ); + + $this->app->singleton(AssetServiceContract::class, AssetService::class); + + $this->registerDefaultDirectories([ + BladePage::class => '_pages', + MarkdownPage::class => '_pages', + MarkdownPost::class => '_posts', + DocumentationPage::class => '_docs', + ]); + + $this->discoverBladeViewsIn('_pages'); + + $this->storeCompiledSiteIn(config( + 'hyde.site_output_path', Hyde::path('_site') + )); + + $this->commands([ + Commands\HydePublishHomepageCommand::class, + Commands\HydeUpdateConfigsCommand::class, + Commands\HydePublishViewsCommand::class, + Commands\HydeRebuildStaticSiteCommand::class, + Commands\HydeBuildStaticSiteCommand::class, + Commands\HydeBuildSitemapCommand::class, + Commands\HydeBuildRssFeedCommand::class, + Commands\HydeBuildSearchCommand::class, + Commands\HydeMakePostCommand::class, + Commands\HydeMakePageCommand::class, + Commands\HydeValidateCommand::class, + Commands\HydeInstallCommand::class, + Commands\HydeDebugCommand::class, + Commands\HydeServeCommand::class, + ]); + } + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot(): void + { + if (config('hyde.create_default_directories', true)) { + (new CreatesDefaultDirectories)->__invoke(); + } + + $this->loadViewsFrom(__DIR__.'/../resources/views', 'hyde'); + + $this->publishes([ + __DIR__.'/../config' => config_path(), + ], 'configs'); + + $this->publishes([ + __DIR__.'/../resources/views/layouts' => resource_path('views/vendor/hyde/layouts'), + ], 'hyde-layouts'); + + $this->publishes([ + __DIR__.'/../resources/views/components' => resource_path('views/vendor/hyde/components'), + ], 'hyde-components'); + + $this->publishes([ + __DIR__.'/../_pages/404.blade.php' => resource_path('views/pages/404.blade.php'), + ], 'hyde-page-404'); + } + + /** + * If you are loading Blade views from a different directory, + * you need to add the path to the view.php config. This is + * here done automatically when registering this provider. + */ + protected function discoverBladeViewsIn(string $directory): void + { + config(['view.paths' => array_merge( + config('view.paths', []), + [base_path($directory)] + )]); + } + + /** + * The absolute path to the directory when the compiled site is stored. + */ + protected function storeCompiledSiteIn(string $directory): void + { + StaticPageBuilder::$outputPath = $directory; + } +} diff --git a/packages/framework/src/Models/Author.php b/packages/framework/src/Models/Author.php new file mode 100644 index 00000000000..85c150ed124 --- /dev/null +++ b/packages/framework/src/Models/Author.php @@ -0,0 +1,66 @@ +username = $username; + if (isset($data['name'])) { + $this->name = $data['name']; + } + if (isset($data['website'])) { + $this->website = $data['website']; + } + } + + /** + * Get the author's preferred name. + * + * @see \Tests\Unit\AuthorGetNameTest + * + * @return string + */ + public function getName(): string + { + return $this->name ?? $this->username; + } +} diff --git a/packages/framework/src/Models/BladePage.php b/packages/framework/src/Models/BladePage.php new file mode 100644 index 00000000000..bb022d98fe7 --- /dev/null +++ b/packages/framework/src/Models/BladePage.php @@ -0,0 +1,56 @@ +view = $view; + $this->slug = $view; + } + + public static string $sourceDirectory = '_pages'; + public static string $fileExtension = '.blade.php'; + public static string $parserClass = self::class; + + /** + * Since this model also acts as a Blade View compiler, + * we implement the get method for compatability. + */ + public function get(): BladePage + { + return $this; + } + + public function getCurrentPagePath(): string + { + return $this->view; + } +} diff --git a/packages/framework/src/Models/DateString.php b/packages/framework/src/Models/DateString.php new file mode 100644 index 00000000000..c4bc0d2ee08 --- /dev/null +++ b/packages/framework/src/Models/DateString.php @@ -0,0 +1,49 @@ +string = $string; + + try { + $this->dateTimeObject = new DateTime($this->string); + } catch (\Exception $e) { + throw new CouldNotParseDateStringException($e->getMessage()); + } + + $this->datetime = $this->dateTimeObject->format('c'); + $this->sentence = $this->dateTimeObject->format('l M jS, Y, \a\t g:ia'); + $this->short = $this->dateTimeObject->format('M jS, Y'); + } +} diff --git a/packages/framework/src/Models/DocumentationPage.php b/packages/framework/src/Models/DocumentationPage.php new file mode 100644 index 00000000000..abb0a408711 --- /dev/null +++ b/packages/framework/src/Models/DocumentationPage.php @@ -0,0 +1,37 @@ +constructTableOfContents(); + } + + public function getCurrentPagePath(): string + { + return trim(Hyde::docsDirectory().'/'.$this->slug, '/'); + } + + /** @internal */ + public function getOnlineSourcePath(): string|false + { + if (config('docs.source_file_location_base', null) === null) { + return false; + } + + return trim(config('docs.source_file_location_base'), '/').'/'.$this->slug.'.md'; + } +} diff --git a/packages/framework/src/Models/DocumentationSidebar.php b/packages/framework/src/Models/DocumentationSidebar.php new file mode 100644 index 00000000000..77a6c497561 --- /dev/null +++ b/packages/framework/src/Models/DocumentationSidebar.php @@ -0,0 +1,36 @@ +push($item); + + return $this; + } + + public function sortItems(): self + { + return $this->sortBy('priority') + ->values(); // Reset the keys to consecutively numbered indexes: + } + + public function getCollection(): self + { + return $this; + } +} diff --git a/packages/framework/src/Models/DocumentationSidebarItem.php b/packages/framework/src/Models/DocumentationSidebarItem.php new file mode 100644 index 00000000000..53b4fb11309 --- /dev/null +++ b/packages/framework/src/Models/DocumentationSidebarItem.php @@ -0,0 +1,70 @@ +label = $label; + $this->destination = $destination; + $this->priority = $priority ?? $this->findPriorityInConfig($destination); + $this->category = $this->normalizeCategoryKey($category); + $this->hidden = $hidden; + } + + protected function findPriorityInConfig(string $slug): int + { + $orderIndexArray = config('docs.sidebar_order', []); + + if (! in_array($slug, $orderIndexArray)) { + return 500; + } + + return array_search($slug, $orderIndexArray) + 250; + + // Adding 250 makes so that pages with a front matter priority that is lower + // can be shown first. It's lower than the fallback of 500 so that they + // still come first. This is all to make it easier to mix priorities. + } + + public function isHidden(): bool + { + return $this->hidden; + } + + public static function parseFromFile(string $documentationPageSlug): static + { + $matter = YamlFrontMatter::markdownCompatibleParse( + file_get_contents(Hyde::getDocumentationPagePath('/'.$documentationPageSlug.'.md')) + )->matter(); + + return new static( + $matter['label'] ?? Hyde::titleFromSlug($documentationPageSlug), + $documentationPageSlug, + $matter['priority'] ?? null, + $matter['category'] ?? null, + $matter['hidden'] ?? false + ); + } + + protected function normalizeCategoryKey(?string $category): ?string + { + return empty($category) ? null : Str::slug($category); + } +} diff --git a/packages/framework/src/Models/Image.php b/packages/framework/src/Models/Image.php new file mode 100644 index 00000000000..e8835adefa2 --- /dev/null +++ b/packages/framework/src/Models/Image.php @@ -0,0 +1,193 @@ + $value) { + $this->{$key} = $value; + } + } + + public function getSource(): ?string + { + return $this->uri ?? $this->path ?? null; + } + + public function getLink(?string $currentPage = ''): string + { + return Hyde::image($this->getSource() ?? '', $currentPage); + } + + public function getContentLength(): int + { + return (new FindsContentLengthForImageObject($this))->execute(); + } + + public function getImageAuthorAttributionString(): ?string + { + if (isset($this->author)) { + if (isset($this->credit)) { + return ''; + } else { + return ''.e($this->author).''; + } + } + + return null; + } + + public function getCopyrightString(): ?string + { + if (isset($this->copyright)) { + return ''.e($this->copyright).''; + } + + return null; + } + + public function getLicenseString(): ?string + { + if (isset($this->license) && isset($this->licenseUrl)) { + return ''.e($this->license).''; + } + + if (isset($this->license)) { + return ''.e($this->license).''; + } + + return null; + } + + public function getFluentAttribution(): string + { + $attribution = []; + + $getImageAuthorAttributionString = $this->getImageAuthorAttributionString(); + if ($getImageAuthorAttributionString !== null) { + $attribution[] = 'Image by '.$getImageAuthorAttributionString; + } + + $getCopyrightString = $this->getCopyrightString(); + if ($getCopyrightString !== null) { + $attribution[] = $getCopyrightString; + } + + $getLicenseString = $this->getLicenseString(); + if ($getLicenseString !== null) { + $attribution[] = 'License '.$getLicenseString; + } + + return implode('. ', $attribution); + } + + /** + * Used in resources\views\components\post\image.blade.php to add meta tags with itemprop attributes. + * + * @return array + */ + public function getMetadataArray(): array + { + $metadata = []; + + if (isset($this->description)) { + $metadata['text'] = $this->description; + } + + if (isset($this->title)) { + $metadata['name'] = $this->title; + } + + $metadata['url'] = $this->getSource(); + $metadata['contentUrl'] = $this->getSource(); + + return $metadata; + } +} diff --git a/packages/framework/src/Models/MarkdownDocument.php b/packages/framework/src/Models/MarkdownDocument.php new file mode 100644 index 00000000000..501b33d887a --- /dev/null +++ b/packages/framework/src/Models/MarkdownDocument.php @@ -0,0 +1,40 @@ +matter = $matter; + $this->body = $body; + $this->title = $title; + $this->slug = $slug; + } +} diff --git a/packages/framework/src/Models/MarkdownPage.php b/packages/framework/src/Models/MarkdownPage.php new file mode 100644 index 00000000000..be5180ed126 --- /dev/null +++ b/packages/framework/src/Models/MarkdownPage.php @@ -0,0 +1,11 @@ +constructAuthor(); + $this->constructMetadata(); + $this->constructDateString(); + $this->constructFeaturedImage(); + + $this->category = $this->matter['category'] ?? null; + } + + public function getCurrentPagePath(): string + { + return 'posts/'.$this->slug; + } + + public function getCanonicalLink(): string + { + return Hyde::uriPath(Hyde::pageLink($this->getCurrentPagePath().'.html')); + } + + public function getPostDescription(): string + { + return $this->matter['description'] ?? substr($this->body, 0, 125).'...'; + } + + public static function getLatestPosts(): Collection + { + return static::all()->sortByDesc('matter.date'); + } +} diff --git a/packages/framework/src/Models/Parsers/DocumentationPageParser.php b/packages/framework/src/Models/Parsers/DocumentationPageParser.php new file mode 100644 index 00000000000..6b6c8ebedfa --- /dev/null +++ b/packages/framework/src/Models/Parsers/DocumentationPageParser.php @@ -0,0 +1,38 @@ +slug.md") + ))->get(); + + $this->title = $document->findTitleForDocument(); + + $this->body = $document->body; + } + + public function get(): DocumentationPage + { + return new DocumentationPage( + matter: [], + body: $this->body, + title: $this->title, + slug: $this->slug + ); + } +} diff --git a/packages/framework/src/Models/Parsers/MarkdownPageParser.php b/packages/framework/src/Models/Parsers/MarkdownPageParser.php new file mode 100644 index 00000000000..d4c11f0e2c8 --- /dev/null +++ b/packages/framework/src/Models/Parsers/MarkdownPageParser.php @@ -0,0 +1,41 @@ +slug.md") + ))->get(); + + $this->title = $document->findTitleForDocument(); + + $this->body = $document->body; + } + + public function get(): MarkdownPage + { + return new MarkdownPage( + matter: [], + body: $this->body, + title: $this->title, + slug: $this->slug + ); + } +} diff --git a/packages/framework/src/Models/Parsers/MarkdownPostParser.php b/packages/framework/src/Models/Parsers/MarkdownPostParser.php new file mode 100644 index 00000000000..5596f81bf3b --- /dev/null +++ b/packages/framework/src/Models/Parsers/MarkdownPostParser.php @@ -0,0 +1,43 @@ +slug.md") + ))->get(); + + $this->matter = array_merge($document->matter, [ + 'slug' => $this->slug, + ]); + + $this->title = $document->findTitleForDocument(); + + $this->body = $document->body; + } + + public function get(): MarkdownPost + { + return new MarkdownPost( + matter: $this->matter, + body: $this->body, + title: $this->title, + slug: $this->slug + ); + } +} diff --git a/packages/framework/src/Services/AssetService.php b/packages/framework/src/Services/AssetService.php new file mode 100644 index 00000000000..17ab463ebc7 --- /dev/null +++ b/packages/framework/src/Services/AssetService.php @@ -0,0 +1,41 @@ +version; + } + + public function stylePath(): string + { + return $this->constructCdnPath('hyde.css'); + } + + public function scriptPath(): string + { + return $this->constructCdnPath('hyde.js'); + } + + public function constructCdnPath(string $file): string + { + return 'https://cdn.jsdelivr.net/npm/hydefront@'.$this->version().'/dist/'.$file; + } + + public function hasMediaFile(string $file): bool + { + return file_exists(Hyde::path('_media').'/'.$file); + } +} diff --git a/packages/framework/src/Services/CollectionService.php b/packages/framework/src/Services/CollectionService.php new file mode 100644 index 00000000000..6b155de58b4 --- /dev/null +++ b/packages/framework/src/Services/CollectionService.php @@ -0,0 +1,127 @@ +createSidebar()->withoutIndex()->withoutHidden(); + } + + /** + * Shorthand to create a new Sidebar object using default methods. + */ + public static function get(): DocumentationSidebar + { + return static::create()->getSidebar()->sortItems()->getCollection(); + } + + /** + * Parse the _docs directory for sidebar items to create a new collection. + */ + public function createSidebar(): self + { + $this->sidebar = new DocumentationSidebar(); + + foreach ($this->getSidebarItems() as $slug) { + $this->sidebar->addItem( + $this->createSidebarItemFromSlug($slug) + ); + } + + return $this; + } + + /** + * Get the sidebar object created and managed by the service instance. + */ + public function getSidebar(): DocumentationSidebar + { + return $this->sidebar; + } + + /** + * Get the sorted sidebar created and managed by the service instance. + */ + public function getSortedSidebar(): DocumentationSidebar + { + return $this->getSidebar()->sortItems(); + } + + /** + * Add an item to the sidebar collection. + */ + public function addItem(DocumentationSidebarItem $item): self + { + $this->sidebar->addItem($item); + + return $this; + } + + /** + * Remove the index page from the sidebar collection. + */ + protected function withoutIndex(): self + { + $this->sidebar = $this->sidebar->reject(function (DocumentationSidebarItem $item) { + return $item->destination === 'index'; + }); + + return $this; + } + + /** + * Remove hidden files from the sidebar collection. + */ + protected function withoutHidden(): self + { + $this->sidebar = $this->sidebar->reject(function (DocumentationSidebarItem $item) { + return $item->isHidden(); + }); + + return $this; + } + + /** + * Get an array of source files to add to the sidebar. + */ + protected function getSidebarItems(): array + { + return CollectionService::getDocumentationPageList(); + } + + /** + * Generate a SidebarItem object from a source file referenced by its slug. + */ + protected function createSidebarItemFromSlug(string $slug): DocumentationSidebarItem + { + return DocumentationSidebarItem::parseFromFile($slug); + } +} diff --git a/packages/framework/src/Services/FileCacheService.php b/packages/framework/src/Services/FileCacheService.php new file mode 100644 index 00000000000..aa40d2e4395 --- /dev/null +++ b/packages/framework/src/Services/FileCacheService.php @@ -0,0 +1,67 @@ + static::unixsumFile($file), + ]; + } + + return $filecache; + } + + public static function getChecksums(): array + { + $cache = static::getFilecache(); + + $checksums = []; + + foreach ($cache as $file) { + $checksums[] = $file['unixsum']; + } + + return $checksums; + } + + public static function checksumMatchesAny(string $checksum): bool + { + return in_array($checksum, static::getChecksums()); + } + + /** + * A EOL agnostic wrapper for calculating MD5 checksums. + * + * @internal This function is not cryptographically secure. + * + * @see https://github.com/hydephp/framework/issues/85 + */ + public static function unixsum(string $string): string + { + $string = str_replace(["\r\n", "\r"], "\n", $string); + + return md5($string); + } + + /* Shorthand for @see static::unixsum() but loads a file */ + public static function unixsumFile(string $file): string + { + return static::unixsum(file_get_contents($file)); + } +} diff --git a/packages/framework/src/Services/HydeSmartDocs.php b/packages/framework/src/Services/HydeSmartDocs.php new file mode 100644 index 00000000000..c431a46260d --- /dev/null +++ b/packages/framework/src/Services/HydeSmartDocs.php @@ -0,0 +1,112 @@ +page = $page; + $this->html = $html; + } + + public function renderHeader(): string + { + return $this->header; + } + + public function renderBody(): string + { + return $this->body; + } + + public function renderFooter(): string + { + return $this->footer; + } + + /** @internal */ + public function process(): self + { + $this->tokenize(); + + $this->addDynamicHeaderContent(); + $this->addDynamicFooterContent(); + + return $this; + } + + protected function tokenize(): self + { + // The HTML content is expected to be two parts. To create semantic HTML, + // we need to split the content into header and body. We do this by + // extracting the first

tag and everything before it. + + // Split the HTML content by the first newline + $parts = explode("\n", $this->html, 2); + + $this->header = $parts[0]; + $this->body = $parts[1] ?? ''; + $this->footer = ''; + + return $this; + } + + protected function addDynamicHeaderContent(): self + { + // Hook to add dynamic content to the header. + // This is where we can add TOC, breadcrumbs, etc. + + if ($this->canRenderSourceLink('header')) { + $this->header .= $this->renderSourceLink(); + } + + return $this; + } + + protected function addDynamicFooterContent(): self + { + // Hook to add dynamic content to the footer. + // This is where we can add copyright, attributions, info, etc. + + if (config('torchlight.attribution.enabled', true) && $this->hasTorchlight()) { + $this->footer .= Str::markdown(config( + 'torchlight.attribution.markdown', + 'Syntax highlighted by torchlight.dev' + )); + } + + if ($this->canRenderSourceLink('footer')) { + $this->footer .= $this->renderSourceLink(); + } + + return $this; + } + + protected function renderSourceLink(): string + { + return sprintf('', + $this->page->getOnlineSourcePath(), + config('docs.edit_source_link_text', 'Edit page') + ); + } +} diff --git a/packages/framework/src/Services/Markdown/BladeDownProcessor.php b/packages/framework/src/Services/Markdown/BladeDownProcessor.php new file mode 100644 index 00000000000..46df45f5f2b --- /dev/null +++ b/packages/framework/src/Services/Markdown/BladeDownProcessor.php @@ -0,0 +1,78 @@ +run()->get(); + } + + public static function preprocess(string $markdown): string + { + return implode("\n", array_map(function ($line) { + return str_starts_with(strtolower($line), strtolower('[Blade]:')) + ? '' + : $line; + }, explode("\n", $markdown))); + } + + public static function process(string $html, ?array $pageData = []): string + { + return (new static($html, $pageData))->run()->get(); + } + + public function __construct(string $html, ?array $pageData = []) + { + $this->html = $html; + $this->pageData = $pageData; + } + + public function run(): self + { + $this->output = implode("\n", array_map(function ($line) { + return $this->lineStartsWithDirective($line) + ? $this->processLine($line) + : $line; + }, explode("\n", $this->html))); + + return $this; + } + + public function get(): string + { + return $this->output; + } + + protected function lineStartsWithDirective(string $line): bool + { + return str_starts_with(strtolower($line), '", + trim(str_replace(static::$patterns, '', $line)) + ); + + // Remove the original comment lines + unset($lines[$index]); + // Only unset the next line if it's empty + if (trim($lines[$index + 1]) === '') { + unset($lines[$index + 1]); + } + } + } + + return implode("\n", $lines); + } + + public static function process(string $html): string + { + $lines = explode("\n", $html); + + foreach ($lines as $index => $line) { + if (str_starts_with($line, ''; + + protected static function lineMatchesPattern(string $line): bool + { + foreach (static::$patterns as $pattern) { + if (str_starts_with($line, $pattern)) { + return true; + } + } + + return false; + } + + protected static function trimHydeDirective(string $line): string + { + return trim(str_replace('-->', '', str_replace( + ''; + + // Enable the extension in config + + $service = new MarkdownConverterService($markdown); + + $html = $service->parse(); + + $this->assertStringContainsString('Syntax highlighting by Torchlight.dev', $html); + } + + public function test_bladedown_is_not_enabled_by_default() + { + $service = new MarkdownConverterService('[Blade]: {{ "Hello World!" }}'); + $this->assertEquals("

[Blade]: {{ "Hello World!" }}

\n", $service->parse()); + } + + public function test_bladedown_can_be_enabled() + { + config(['markdown.enable_blade' => true]); + $service = new MarkdownConverterService('[Blade]: {{ "Hello World!" }}'); + $service->addFeature('bladedown')->parse(); + $this->assertEquals("Hello World!\n", $service->parse()); + } +} diff --git a/packages/framework/tests/Feature/MarkdownFileServiceTest.php b/packages/framework/tests/Feature/MarkdownFileServiceTest.php new file mode 100644 index 00000000000..cb9b80f0891 --- /dev/null +++ b/packages/framework/tests/Feature/MarkdownFileServiceTest.php @@ -0,0 +1,50 @@ +get(); + $this->assertInstanceOf(MarkdownDocument::class, $document); + + $this->assertEquals([ + 'title' => 'My New Post', + 'category' => 'blog', + 'author' => 'Mr. Hyde', + ], $document->matter); + + $this->assertEquals( + '# My New PostThis is a post stub used in the automated tests', + str_replace(["\n", "\r"], '', $document->body) + ); + } + + public function test_parsed_markdown_post_contains_valid_front_matter() + { + $post = (new MarkdownFileService(Hyde::path('_posts/test-post.md')))->get(); + $this->assertEquals('My New Post', $post->matter['title']); + $this->assertEquals('Mr. Hyde', $post->matter['author']); + $this->assertEquals('blog', $post->matter['category']); + } +} diff --git a/packages/framework/tests/Feature/MarkdownPageTest.php b/packages/framework/tests/Feature/MarkdownPageTest.php new file mode 100644 index 00000000000..e4537d78216 --- /dev/null +++ b/packages/framework/tests/Feature/MarkdownPageTest.php @@ -0,0 +1,96 @@ +assertIsArray($array); + $this->assertCount(1, $array); + $this->assertArrayHasKey('test-post', array_flip($array)); + } + + public function test_exception_is_thrown_for_missing_slug() + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('File _pages/invalid-file.md not found.'); + new MarkdownPageParser('invalid-file'); + } + + public function test_can_parse_documentation_page() + { + $parser = new MarkdownPageParser('test-post'); + $this->assertInstanceOf(MarkdownPageParser::class, $parser); + } + + public function test_title_was_inferred_from_heading() + { + $parser = new MarkdownPageParser('test-post'); + $this->assertIsString($parser->title); + $this->assertEquals('PHPUnit Test File', $parser->title); + } + + public function test_parser_contains_body_text() + { + $parser = new MarkdownPageParser('test-post'); + $this->assertIsString($parser->body); + $this->assertEquals("# PHPUnit Test File \n Hello World!", $parser->body); + } + + /** + * Test the Model. + */ + public function test_can_get_page_model_object(): MarkdownPage + { + $parser = new MarkdownPageParser('test-post'); + $object = $parser->get(); + $this->assertInstanceOf(MarkdownPage::class, $object); + + return $object; + } + + /** + * @depends test_can_get_page_model_object + */ + public function test_created_model_contains_expected_data(MarkdownPage $object) + { + $this->assertEquals('PHPUnit Test File', $object->title); + $this->assertEquals("# PHPUnit Test File \n Hello World!", $object->body); + $this->assertEquals('test-post', $object->slug); + } +} diff --git a/packages/framework/tests/Feature/MetadataHelperTest.php b/packages/framework/tests/Feature/MetadataHelperTest.php new file mode 100644 index 00000000000..e7e1e6842de --- /dev/null +++ b/packages/framework/tests/Feature/MetadataHelperTest.php @@ -0,0 +1,142 @@ + []]); + } + + // Test name method returns a valid HTML meta string + public function test_name_method_returns_a_valid_html_meta_string() + { + $this->assertEquals( + '', + Meta::name('foo', 'bar') + ); + } + + // Test property method returns a valid HTML meta string + public function test_property_method_returns_a_valid_html_meta_string() + { + $this->assertEquals( + '', + Meta::property('foo', 'bar') + ); + } + + // Test property method accepts property with og prefix + public function test_property_method_accepts_property_with_og_prefix() + { + $this->assertEquals( + '', + Meta::property('og:foo', 'bar') + ); + } + + // Test property method accepts property without og prefix + public function test_property_method_accepts_property_without_og_prefix() + { + $this->assertEquals( + '', + Meta::property('foo', 'bar') + ); + } + + // Test render method implodes an array of meta tags into a formatted string + public function test_render_method_implodes_an_array_of_meta_tags_into_a_formatted_string() + { + $this->assertEquals( + '' + ."\n".'', + + Meta::render([ + Meta::name('foo', 'bar'), + Meta::property('og:foo', 'bar'), + ]) + ); + } + + // Test render method returns an empty string if no meta tags are supplied + public function test_render_method_returns_an_empty_string_if_no_meta_tags_are_supplied() + { + $this->assertEquals( + '', + Meta::render([]) + ); + } + + // Test render method returns config defined tags if no meta tags are supplied + public function test_render_method_returns_config_defined_tags_if_no_meta_tags_are_supplied() + { + config(['hyde.meta' => [ + Meta::name('foo', 'bar'), + Meta::property('og:foo', 'bar'), + ]]); + + $this->assertEquals( + '' + ."\n".'', + + Meta::render([]) + ); + } + + // Test render method merges config defined tags with supplied meta tags + public function test_render_method_merges_config_defined_tags_with_supplied_meta_tags() + { + config(['hyde.meta' => [ + Meta::name('foo', 'bar'), + ]]); + + $this->assertEquals( + '' + ."\n".'', + + Meta::render([ + Meta::property('foo', 'bar'), + ]) + ); + } + + // Test render method returns unique meta tags + public function test_render_method_returns_unique_meta_tags() + { + config(['hyde.meta' => [ + Meta::name('foo', 'bar'), + ]]); + + $this->assertEquals( + '', + Meta::render([ + Meta::name('foo', 'bar'), + ]) + ); + } + + // Test render method gives precedence to supplied meta tags + public function test_render_method_gives_precedence_to_supplied_meta_tags() + { + config(['hyde.meta' => [ + Meta::name('foo', 'bar'), + ]]); + + $this->assertEquals( + '', + + Meta::render([ + Meta::name('foo', 'baz'), + ]) + ); + } +} diff --git a/packages/framework/tests/Feature/RebuildServiceTest.php b/packages/framework/tests/Feature/RebuildServiceTest.php new file mode 100644 index 00000000000..bdf0f160e0c --- /dev/null +++ b/packages/framework/tests/Feature/RebuildServiceTest.php @@ -0,0 +1,48 @@ +execute(); + $this->assertNotNull($service->model); + unlink(Hyde::path('_posts/test-post.md')); + unlink(Hyde::path('_site/posts/test-post.html')); + } + + public function test_execute_methods() + { + $this->runExecuteTest('_posts'); + $this->runExecuteTest('_pages'); + $this->runExecuteTest('_docs'); + $this->runExecuteTest('_pages', '.blade.php'); + + unlink(Hyde::path('_site/test-file.html')); + unlink(Hyde::path('_site/docs/test-file.html')); + unlink(Hyde::path('_site/posts/test-file.html')); + } + + private function runExecuteTest(string $prefix, string $suffix = '.md') + { + $path = $prefix.'/test-file'.$suffix; + createTestPost($path); + $service = new RebuildService($path); + $result = $service->execute(); + $this->assertInstanceOf(StaticPageBuilder::class, $result); + unlink(Hyde::path($path)); + } +} diff --git a/packages/framework/tests/Feature/Services/BladeDownProcessorTest.php b/packages/framework/tests/Feature/Services/BladeDownProcessorTest.php new file mode 100644 index 00000000000..6b8f7860ff1 --- /dev/null +++ b/packages/framework/tests/Feature/Services/BladeDownProcessorTest.php @@ -0,0 +1,73 @@ +assertEquals('Hello World!', BladeDownProcessor::render('[Blade]: {{ "Hello World!" }}')); + } + + // Test it renders Blade within multiline Markdown + public function test_it_renders_blade_within_multiline_markdown() + { + $this->assertEquals( + "Foo\nHello World!\nBar", + + BladeDownProcessor::render("Foo\n[Blade]: {{ 'Hello World!' }}\nBar") + ); + } + + // Test it renders Blade views + public function test_it_renders_blade_views() + { + file_put_contents(resource_path( + 'views/hello.blade.php' + ), 'Hello World!'); + + $this->assertEquals('Hello World!', BladeDownProcessor::render('[Blade]: @include("hello")')); + + unlink(resource_path('views/hello.blade.php')); + } + + // Test directive is case-insensitive + public function test_directive_is_case_insensitive() + { + $this->assertEquals('Hello World!', BladeDownProcessor::render('[blade]: {{ "Hello World!" }}')); + } + + // Test directive is ignored if it's not at the start of a line + public function test_directive_is_ignored_if_it_is_not_at_the_start_of_a_line() + { + $this->assertEquals('Example: [Blade]: {{ "Hello World!" }}', + BladeDownProcessor::render('Example: [Blade]: {{ "Hello World!" }}')); + } + + // Test it renders Blade echo syntax with variables + public function test_it_renders_blade_echo_syntax_with_variables() + { + $this->assertEquals('Hello World!', BladeDownProcessor::render('[Blade]: {{ $foo }}', ['foo' => 'Hello World!'])); + } + + // Test it renders Blade views with variables + public function test_it_renders_blade_views_with_variables() + { + file_put_contents(resource_path( + 'views/hello.blade.php' + ), 'Hello {{ $name }}!'); + + $this->assertEquals('Hello John!', BladeDownProcessor::render('[Blade]: @include("hello", ["name" => "John"])')); + + unlink(resource_path('views/hello.blade.php')); + } +} diff --git a/packages/framework/tests/Feature/Services/CollectionServiceTest.php b/packages/framework/tests/Feature/Services/CollectionServiceTest.php new file mode 100644 index 00000000000..6f4e660ff21 --- /dev/null +++ b/packages/framework/tests/Feature/Services/CollectionServiceTest.php @@ -0,0 +1,63 @@ +assertTrue(class_exists(CollectionService::class)); + } + + public function test_get_source_file_list_for_model_method() + { + $this->testListUnit(BladePage::class, '_pages/a8a7b7ce.blade.php'); + $this->testListUnit(MarkdownPage::class, '_pages/a8a7b7ce.md'); + $this->testListUnit(MarkdownPost::class, '_posts/a8a7b7ce.md'); + $this->testListUnit(DocumentationPage::class, '_docs/a8a7b7ce.md'); + + $this->assertFalse(CollectionService::getSourceFileListForModel('NonExistentModel')); + } + + public function test_get_media_asset_files() + { + $this->assertTrue(is_array(CollectionService::getMediaAssetFiles())); + } + + public function test_files_starting_with_underscore_are_ignored() + { + touch(Hyde::path('_posts/_foo.md')); + $this->assertNotContains('_foo', CollectionService::getMarkdownPostList()); + $this->assertNotContains('foo', CollectionService::getMarkdownPostList()); + unlink(Hyde::path('_posts/_foo.md')); + } + + private function testListUnit(string $model, string $path) + { + touch(Hyde::path($path)); + + $expected = str_replace(['.md', '.blade.php'], '', basename($path)); + + $this->assertContains($expected, CollectionService::getSourceFileListForModel($model)); + + unlink(Hyde::path($path)); + } + + public function tearDown(): void + { + // restoreDirectory(Hyde::path('_docs')); + + parent::tearDown(); + } +} diff --git a/packages/framework/tests/Feature/Services/DocumentationSidebarServiceTest.php b/packages/framework/tests/Feature/Services/DocumentationSidebarServiceTest.php new file mode 100644 index 00000000000..1ae0ce9ed27 --- /dev/null +++ b/packages/framework/tests/Feature/Services/DocumentationSidebarServiceTest.php @@ -0,0 +1,276 @@ +resetDocsDirectory(); + } + + protected function tearDown(): void + { + $this->resetDocsDirectory(); + + parent::tearDown(); + } + + public function test_sidebar_can_be_created() + { + $service = new DocumentationSidebarService(); + $sidebar = $service->createSidebar()->getSidebar(); + + $this->assertInstanceOf(DocumentationSidebar::class, $sidebar); + } + + public function test_sidebar_items_can_be_added() + { + $sidebar = DocumentationSidebarService::get()->addItem( + new DocumentationSidebarItem('test', 'test') + ); + + $this->assertCount(1, $sidebar); + $this->assertInstanceOf(DocumentationSidebarItem::class, $sidebar[0]); + } + + public function test_sidebar_items_are_added_automatically() + { + $this->createTestFiles(); + + $sidebar = DocumentationSidebarService::get(); + + $this->assertCount(5, $sidebar); + } + + public function test_index_page_is_removed_from_sidebar() + { + $this->createTestFiles(); + touch(Hyde::path('_docs/index.md')); + + $sidebar = DocumentationSidebarService::get(); + $this->assertCount(5, $sidebar); + } + + public function test_files_with_front_matter_hidden_set_to_true_are_removed_from_sidebar() + { + $this->createTestFiles(); + File::put(Hyde::path('_docs/test.md'), "---\nhidden: true\n---\n\n# Foo"); + + $sidebar = DocumentationSidebarService::get(); + $this->assertCount(5, $sidebar); + } + + public function test_sidebar_is_ordered_alphabetically_when_no_order_is_set_in_config() + { + Config::set('docs.sidebar_order', []); + touch(Hyde::path('_docs/alpha.md')); + touch(Hyde::path('_docs/bravo.md')); + touch(Hyde::path('_docs/charlie.md')); + + $sidebar = DocumentationSidebarService::get(); + + $this->assertEquals('alpha', $sidebar[0]->destination); + $this->assertEquals('bravo', $sidebar[1]->destination); + $this->assertEquals('charlie', $sidebar[2]->destination); + } + + public function test_sidebar_is_ordered_by_priority_when_priority_is_set_in_config() + { + Config::set('docs.sidebar_order', [ + 'charlie', + 'bravo', + 'alpha', + ]); + touch(Hyde::path('_docs/alpha.md')); + touch(Hyde::path('_docs/bravo.md')); + touch(Hyde::path('_docs/charlie.md')); + + $sidebar = DocumentationSidebarService::get(); + $this->assertEquals('charlie', $sidebar[0]->destination); + $this->assertEquals('bravo', $sidebar[1]->destination); + $this->assertEquals('alpha', $sidebar[2]->destination); + } + + public function test_sidebar_item_priority_can_be_set_in_front_matter() + { + file_put_contents( + Hyde::path('_docs/foo.md'), + (new ConvertsArrayToFrontMatter)->execute([ + 'priority' => 25, + ]) + ); + + $this->assertEquals(25, DocumentationSidebarItem::parseFromFile('foo')->priority); + $this->assertEquals(25, DocumentationSidebarService::get()->first()->priority); + } + + public function test_sidebar_item_priority_set_in_config_overrides_front_matter() + { + file_put_contents(Hyde::path('_docs/foo.md'), + (new ConvertsArrayToFrontMatter)->execute(['priority' => 25]) + ); + + Config::set('docs.sidebar_order', ['foo']); + + $this->assertEquals(25, DocumentationSidebarService::get()->first()->priority); + } + + public function test_sidebar_priorities_can_be_set_in_both_front_matter_and_config() + { + Config::set('docs.sidebar_order', [ + 'first', + 'third', + 'second', + ]); + touch(Hyde::path('_docs/first.md')); + touch(Hyde::path('_docs/second.md')); + file_put_contents(Hyde::path('_docs/third.md'), + (new ConvertsArrayToFrontMatter)->execute(['priority' => 300]) + ); + $sidebar = DocumentationSidebarService::get(); + $this->assertEquals('first', $sidebar[0]->destination); + $this->assertEquals('second', $sidebar[1]->destination); + $this->assertEquals('third', $sidebar[2]->destination); + } + + public function test_category_can_be_set_in_front_matter() + { + file_put_contents( + Hyde::path('_docs/foo.md'), + (new ConvertsArrayToFrontMatter)->execute([ + 'category' => 'bar', + ]) + ); + + $this->assertEquals('bar', DocumentationSidebarItem::parseFromFile('foo')->category); + $this->assertEquals('bar', DocumentationSidebarService::get()->first()->category); + } + + public function test_sidebar_categories_are_assembled_from_sidebar_items() + { + $service = (new DocumentationSidebarService)->createSidebar(); + $service->addItem(new DocumentationSidebarItem('foo', 'foo', category: 'foo')); + $service->addItem(new DocumentationSidebarItem('bar', 'bar', category: 'foo')); + $service->addItem(new DocumentationSidebarItem('cat', 'cat', category: 'cat')); + $service->addItem(new DocumentationSidebarItem('hat', 'hat')); + + $this->assertEquals(['foo', 'cat', 'other'], $service->getCategories()); + } + + public function test_has_categories_returns_false_if_no_categories_are_set() + { + $service = (new DocumentationSidebarService)->createSidebar(); + $service->addItem(new DocumentationSidebarItem('foo', 'foo')); + + $this->assertFalse($service->hasCategories()); + } + + public function test_has_categories_returns_true_if_at_least_one_category_is_set() + { + $service = (new DocumentationSidebarService)->createSidebar(); + $service->addItem(new DocumentationSidebarItem('foo', 'foo', category: 'foo')); + $service->addItem(new DocumentationSidebarItem('bar', 'bar')); + + $this->assertTrue($service->hasCategories()); + } + + public function test_get_items_in_category_returns_items_with_given_category() + { + $service = (new DocumentationSidebarService)->createSidebar(); + + $service->addItem($foo = new DocumentationSidebarItem('foo', 'foo', category: 'foo')); + $service->addItem(new DocumentationSidebarItem('bar', 'bar', category: 'foo')); + $service->addItem(new DocumentationSidebarItem('cat', 'cat', category: 'cat')); + $service->addItem(new DocumentationSidebarItem('hat', 'hat')); + + $this->assertCount(2, $service->getItemsInCategory('foo')); + $this->assertCount(1, $service->getItemsInCategory('cat')); + $this->assertCount(0, $service->getItemsInCategory('hat')); + + $this->assertEquals($foo, $service->getItemsInCategory('foo')->first()); + } + + public function test_items_with_no_category_gets_added_to_the_default_category_when_at_least_one_category_is_set() + { + $service = DocumentationSidebarService::create(); + + $service->addItem(new DocumentationSidebarItem('foo', 'foo', category: 'foo')); + $service->addItem(new DocumentationSidebarItem('bar', 'bar')); + + $categories = $service->getCategories(); + + $this->assertCount(2, $categories); + $this->assertCount(1, $service->getItemsInCategory('foo')); + $this->assertCount(1, $service->getItemsInCategory('other')); + $this->assertEquals('foo', $service->getItemsInCategory('foo')->first()->category); + $this->assertEquals('other', $service->getItemsInCategory('other')->first()->category); + } + + public function test_items_with_no_category_gets_added_to_the_default_category_when_no_categories_are_set() + { + $service = DocumentationSidebarService::create(); + $service->addItem(new DocumentationSidebarItem('foo', 'foo')); + $service->addItem(new DocumentationSidebarItem('bar', 'bar')); + + $categories = $service->getCategories(); + + $this->assertCount(0, $categories); + } + + public function test_category_names_are_case_insensitive() + { + $service = DocumentationSidebarService::create(); + $service->addItem(new DocumentationSidebarItem('foo', 'foo', category: 'Foo Bar')); + $service->addItem(new DocumentationSidebarItem('bar', 'bar', category: 'foo bar')); + $service->addItem(new DocumentationSidebarItem('cat', 'cat', category: 'Foo_bar')); + $categories = $service->getCategories(); + + $this->assertCount(1, $categories); + $this->assertCount(3, $service->getItemsInCategory('foo bar')); + } + + public function test_get_sorted_categories_returns_categories_sorted_by_priority() + { + $service = DocumentationSidebarService::create(); + $service->addItem(new DocumentationSidebarItem('third', 'foo', priority: 3, category: 'third')); + $service->addItem(new DocumentationSidebarItem('second', 'foo', priority: 2, category: 'second')); + $service->addItem(new DocumentationSidebarItem('first', 'foo', priority: 1, category: 'first')); + + $categories = $service->getCategories(); + + $this->assertCount(3, $categories); + $this->assertEquals('first', $categories[0]); + $this->assertEquals('second', $categories[1]); + $this->assertEquals('third', $categories[2]); + } + + protected function resetDocsDirectory(): void + { + File::deleteDirectory(Hyde::path('_docs')); + mkdir(Hyde::path('_docs')); + } + + protected function createTestFiles(int $count = 5): void + { + for ($i = 0; $i < $count; $i++) { + touch(Hyde::path('_docs/test-'.$i.'.md')); + } + } +} diff --git a/packages/framework/tests/Feature/Services/FileCacheServiceTest.php b/packages/framework/tests/Feature/Services/FileCacheServiceTest.php new file mode 100644 index 00000000000..d9f80c03c41 --- /dev/null +++ b/packages/framework/tests/Feature/Services/FileCacheServiceTest.php @@ -0,0 +1,55 @@ +getFilecache(); + + $this->assertIsArray($fileCache); + $this->assertArrayHasKey('/resources/views/layouts/app.blade.php', $fileCache); + $this->assertArrayHasKey('unixsum', $fileCache['/resources/views/layouts/app.blade.php']); + $this->assertEquals(32, strlen($fileCache['/resources/views/layouts/app.blade.php']['unixsum'])); + } + + // Test getChecksums() method returns array with just the checksums + public function testGetChecksums() + { + $fileCacheService = new FileCacheService(); + $checksums = $fileCacheService->getChecksums(); + + $this->assertIsArray($checksums); + $this->assertEquals(32, strlen($checksums[0])); + } + + // Test checksumMatchesAny() method returns true if a supplied checksum matches any of the checksums in array + public function testChecksumMatchesAny() + { + $fileCacheService = new FileCacheService(); + + $this->assertTrue($fileCacheService->checksumMatchesAny(FileCacheService::unixsumFile( + Hyde::vendorPath('resources/views/layouts/app.blade.php')) + )); + } + + // Test checksumMatchesAny() method returns false if a supplied checksum does not match any of the checksums in array + public function testChecksumMatchesAnyFalse() + { + $fileCacheService = new FileCacheService(); + + $this->assertFalse($fileCacheService->checksumMatchesAny(FileCacheService::unixsum( + 'foo' + ))); + } +} diff --git a/packages/framework/tests/Feature/Services/HasConfigurableMarkdownFeaturesTest.php b/packages/framework/tests/Feature/Services/HasConfigurableMarkdownFeaturesTest.php new file mode 100644 index 00000000000..5c1c1461356 --- /dev/null +++ b/packages/framework/tests/Feature/Services/HasConfigurableMarkdownFeaturesTest.php @@ -0,0 +1,122 @@ +assertIsArray($this->features); + } + + // Test that the features array is empty by default + public function test_has_features_array_empty() + { + $this->assertEmpty($this->features); + } + + // Test that features can be added to the array + public function test_has_features_array_add() + { + $this->addFeature('test'); + $this->assertContains('test', $this->features); + } + + // Test that features can be removed from the array + public function test_has_features_array_remove() + { + $this->addFeature('test'); + $this->removeFeature('test'); + $this->assertNotContains('test', $this->features); + } + + // Test that method chaining can be used to programmatically add features to the array + public function test_has_features_array_add_chain() + { + $this->addFeature('test')->addFeature('test2'); + $this->assertContains('test', $this->features); + $this->assertContains('test2', $this->features); + } + + // Test that method chaining can be used to programmatically remove features from the array + public function test_has_features_array_remove_chain() + { + $this->addFeature('test')->addFeature('test2')->removeFeature('test'); + $this->assertNotContains('test', $this->features); + $this->assertContains('test2', $this->features); + } + + // Test that method withTableOfContents method chain adds the table-of-contents feature + public function test_has_features_array_add_toc() + { + $this->withTableOfContents(); + $this->assertContains('table-of-contents', $this->features); + } + + // Test that method withPermalinks method chain adds the permalinks feature + public function test_has_features_array_add_permalinks() + { + $this->withPermalinks(); + $this->assertContains('permalinks', $this->features); + } + + // Test that hasFeature() returns true if the feature is in the array + public function test_has_features_array_has() + { + $this->addFeature('test'); + $this->assertTrue($this->hasFeature('test')); + } + + // Test that hasFeature() returns false if the feature is not in the array + public function test_has_features_array_has_not() + { + $this->assertFalse($this->hasFeature('test')); + } + + // Test that method canEnablePermalinks returns true if the permalinks feature is in the array + public function test_has_features_array_can_enable_permalinks() + { + $this->addFeature('permalinks'); + $this->assertTrue($this->canEnablePermalinks()); + } + + // Test that method canEnablePermalinks is automatically for DocumentationPages + public function test_has_features_array_can_enable_permalinks_auto() + { + Config::set('docs.table_of_contents.enabled', true); + $this->sourceModel = DocumentationPage::class; + + $this->assertTrue($this->canEnablePermalinks()); + } + + // Test that method canEnablePermalinks returns false if the permalinks feature is not in the array + public function test_has_features_array_can_enable_permalinks_not() + { + $this->assertFalse($this->canEnablePermalinks()); + } + + // Test that method canEnableTorchlight returns true if the torchlight feature is in the array + public function test_has_features_array_can_enable_torchlight() + { + $this->addFeature('torchlight'); + $this->assertTrue($this->canEnableTorchlight()); + } + + // Test that method canEnableTorchlight returns false if the torchlight feature is not in the array + public function test_has_features_array_can_enable_torchlight_not() + { + $this->assertFalse($this->canEnableTorchlight()); + } +} diff --git a/packages/framework/tests/Feature/Services/Markdown/CodeblockFilepathProcessorTest.php b/packages/framework/tests/Feature/Services/Markdown/CodeblockFilepathProcessorTest.php new file mode 100644 index 00000000000..4ece541d66d --- /dev/null +++ b/packages/framework/tests/Feature/Services/Markdown/CodeblockFilepathProcessorTest.php @@ -0,0 +1,182 @@ +\n```php\necho 'Hello World';\n```"; + + $this->assertEquals($expected, CodeblockFilepathProcessor::preprocess($markdown)); + } + + // Test preprocess method accepts multiple filepath formats + public function test_preprocess_accepts_multiple_filepath_formats() + { + $patterns = [ + '// filepath: ', + '// Filepath: ', + '# filepath: ', + '# Filepath: ', + '// filepath ', + '// Filepath ', + '# filepath ', + '# Filepath ', + ]; + + foreach ($patterns as $pattern) { + $markdown = "\n```php\n{$pattern}foo.php\necho 'Hello World';\n```"; + $expected = "\n\n```php\necho 'Hello World';\n```"; + + $this->assertEquals($expected, CodeblockFilepathProcessor::preprocess($markdown)); + } + } + + // Test preprocess method accepts multiple languages + public function test_preprocess_accepts_multiple_languages() + { + $languages = [ + 'php', + 'js', + 'html', + 'made-up', + 'foo', + ]; + + foreach ($languages as $language) { + $markdown = "\n```{$language}\n// filepath: foo.{$language}\nfoo\n```"; + $expected = "\n\n```{$language}\nfoo\n```"; + + $this->assertEquals($expected, CodeblockFilepathProcessor::preprocess($markdown)); + } + + $this->assertEquals($expected, CodeblockFilepathProcessor::preprocess($markdown)); + } + + // Test preprocess method accepts multiple input blocks + public function test_preprocess_accepts_multiple_input_blocks() + { + $markdown = <<<'MD' + + ```php + // filepath: foo.php + echo 'Hello World'; + ``` + + ```js + // filepath: bar.js + echo 'Hello World'; + ``` + MD; + + $expected = <<<'MD' + + + ```php + echo 'Hello World'; + ``` + + + ```js + echo 'Hello World'; + ``` + MD; + + $this->assertEqualsIgnoringLineReturnType($expected, CodeblockFilepathProcessor::preprocess($markdown)); + } + + // Test preprocess method accepts multi-line codeblocks + public function test_preprocess_accepts_multi_line_codeblocks() + { + $markdown = <<<'MD' + + ```php + // filepath: foo.php + echo 'Hello World'; + + echo 'Hello World'; + ``` + MD; + + $expected = <<<'MD' + + + ```php + echo 'Hello World'; + + echo 'Hello World'; + ``` + MD; + + $this->assertEqualsIgnoringLineReturnType($expected, CodeblockFilepathProcessor::preprocess($markdown)); + } + + // Test space after filepath is optional + public function test_space_after_filepath_is_optional() + { + $markdown = <<<'MD' + + ```php + // filepath: foo.php + + echo 'Hello World'; + ``` + MD; + + $expected = <<<'MD' + + ```php + // filepath: foo.php + echo 'Hello World'; + ``` + MD; + + $this->assertEqualsIgnoringLineReturnType(CodeblockFilepathProcessor::preprocess($expected), + CodeblockFilepathProcessor::preprocess($markdown)); + } + + // Test processor expands filepath directive in standard codeblock + public function test_processor_expands_filepath_directive_in_standard_codeblock() + { + $html = <<<'HTML' + +
+ HTML; + + $expected = <<<'HTML' +
Filepath: foo.html
+ HTML; + + $this->assertEqualsIgnoringLineReturnType($expected, CodeblockFilepathProcessor::process($html)); + } + + // Test processor expands filepath directive in Torchlight codeblock + public function test_processor_expands_filepath_directive_in_torchlight_codeblock() + { + $html = <<<'HTML' + +
1 
+ HTML; + + $expected = <<<'HTML' +
Filepath: foo.html
1 
+ HTML; + + $this->assertEqualsIgnoringLineReturnType($expected, CodeblockFilepathProcessor::process($html)); + } + + protected function assertEqualsIgnoringLineReturnType(string $expected, string $actual) + { + $this->assertEquals(str_replace("\r\n", "\n", $expected), + str_replace("\r\n", "\n", $actual)); + } +} diff --git a/packages/framework/tests/Feature/Services/Markdown/ShortcodeProcessorTest.php b/packages/framework/tests/Feature/Services/Markdown/ShortcodeProcessorTest.php new file mode 100644 index 00000000000..7651a0f5d57 --- /dev/null +++ b/packages/framework/tests/Feature/Services/Markdown/ShortcodeProcessorTest.php @@ -0,0 +1,68 @@ +shortcodes; + + $this->assertCount(4, $shortcodes); + $this->assertContainsOnlyInstancesOf(MarkdownShortcodeContract::class, $shortcodes); + } + + // Test discovered shortcodes are used to process input + public function test_discovered_shortcodes_are_used_to_process_input() + { + $processor = new ShortcodeProcessor('>info foo'); + + $this->assertEquals('
foo
', + $processor->run()); + } + + // Test string not matching shortcode is not modified + public function test_string_without_shortcode_is_not_modified() + { + $processor = new ShortcodeProcessor('foo'); + + $this->assertEquals('foo', $processor->run()); + } + + // Test the static process() shorthand method + public function test_process_static_shorthand() + { + $this->assertEquals('
foo
', + ShortcodeProcessor::process('>info foo')); + } + + // Test shortcodes can be added to the processor + public function test_shortcodes_can_be_added_to_processor() + { + $processor = new ShortcodeProcessor('foo'); + + $processor->addShortcode(new class implements MarkdownShortcodeContract + { + public static function signature(): string + { + return 'foo'; + } + + public static function resolve(string $input): string + { + return 'bar'; + } + }); + + $this->assertArrayHasKey('foo', $processor->shortcodes); + $this->assertEquals('bar', $processor->run()); + } +} diff --git a/packages/framework/tests/Feature/Services/RssFeedServiceTest.php b/packages/framework/tests/Feature/Services/RssFeedServiceTest.php new file mode 100644 index 00000000000..ba3f39ab8ef --- /dev/null +++ b/packages/framework/tests/Feature/Services/RssFeedServiceTest.php @@ -0,0 +1,156 @@ +assertInstanceOf('SimpleXMLElement', $service->feed); + } + + // Test XML root element is set to RSS 2.0 + public function test_xml_root_element_is_set_to_rss_2_0() + { + $service = new RssFeedService(); + $this->assertEquals('rss', $service->feed->getName()); + $this->assertEquals('2.0', $service->feed->attributes()->version); + } + + // Test XML element has channel element + public function test_xml_element_has_channel_element() + { + $service = new RssFeedService(); + $this->assertObjectHasAttribute('channel', $service->feed); + } + + // Test XML channel element has required elements + public function test_xml_channel_element_has_required_elements() + { + config(['hyde.name' => 'Test Blog']); + config(['hyde.site_url' => 'https://example.com']); + + $service = new RssFeedService(); + $this->assertObjectHasAttribute('title', $service->feed->channel); + $this->assertObjectHasAttribute('link', $service->feed->channel); + $this->assertObjectHasAttribute('description', $service->feed->channel); + + $this->assertEquals('Test Blog', $service->feed->channel->title); + $this->assertEquals('https://example.com', $service->feed->channel->link); + $this->assertEquals('Test Blog RSS Feed', $service->feed->channel->description); + } + + // Test XML channel element has additional elements + public function test_xml_channel_element_has_additional_elements() + { + config(['hyde.site_url' => 'https://example.com']); + + $service = new RssFeedService(); + $this->assertObjectHasAttribute('link', $service->feed->channel); + $this->assertEquals('https://example.com', $service->feed->channel->link); + $this->assertEquals('https://example.com/feed.xml', + $service->feed->channel->children('atom', true)->link->attributes()->href); + + $this->assertObjectHasAttribute('language', $service->feed->channel); + $this->assertObjectHasAttribute('generator', $service->feed->channel); + $this->assertObjectHasAttribute('lastBuildDate', $service->feed->channel); + } + + // Test XML channel data can be customized + public function test_xml_channel_data_can_be_customized() + { + config(['hyde.name' => 'Foo']); + config(['hyde.site_url' => 'https://blog.foo.com/bar']); + config(['hyde.rss_description' => 'Foo is a web log about stuff']); + + $service = new RssFeedService(); + $this->assertEquals('Foo', $service->feed->channel->title); + $this->assertEquals('https://blog.foo.com/bar', $service->feed->channel->link); + $this->assertEquals('Foo is a web log about stuff', $service->feed->channel->description); + } + + // Test Markdown blog posts are added to RSS feed through autodiscovery + public function test_markdown_blog_posts_are_added_to_rss_feed_through_autodiscovery() + { + file_put_contents(Hyde::path('_posts/rss.md'), <<<'MD' + --- + title: RSS + author: Hyde + date: "2022-05-19 10:15:30" + description: RSS description + category: test + image: _media/rss-test.jpg + --- + + # RSS Post + + Foo bar + MD + ); + + config(['hyde.site_url' => 'https://example.com']); + + file_put_contents(Hyde::path('_media/rss-test.jpg'), 'statData'); // 8 bytes to test stat gets file length + + $service = new RssFeedService(); + $service->generate(); + $this->assertCount(1, $service->feed->channel->item); + + $item = $service->feed->channel->item[0]; + $this->assertEquals('RSS', $item->title); + $this->assertEquals('RSS description', $item->description); + $this->assertEquals('https://example.com/posts/rss.html', $item->link); + $this->assertEquals(date(DATE_RSS, strtotime('2022-05-19T10:15:30+00:00')), $item->pubDate); + $this->assertEquals('Hyde', $item->children('dc', true)->creator); + $this->assertEquals('test', $item->category); + + $this->assertObjectHasAttribute('enclosure', $item); + $this->assertEquals('https://example.com/media/rss-test.jpg', $item->enclosure->attributes()->url); + $this->assertEquals('image/jpeg', $item->enclosure->attributes()->type); + $this->assertEquals('8', $item->enclosure->attributes()->length); + + unlink(Hyde::path('_posts/rss.md')); + unlink(Hyde::path('_media/rss-test.jpg')); + } + + // Test getXML method returns XML string + public function test_get_xml_method_returns_xml_string() + { + $service = new RssFeedService(); + $this->assertStringStartsWith('', ($service->getXML())); + } + + // Test generateFeed helper returns XML string + public function test_generate_feed_helper_returns_xml_string() + { + $this->assertStringStartsWith('', (RssFeedService::generateFeed())); + } + + public function test_can_generate_sitemap_helper_returns_true_if_hyde_has_base_url() + { + config(['hyde.site_url' => 'foo']); + $this->assertTrue(RssFeedService::canGenerateFeed()); + } + + public function test_can_generate_sitemap_helper_returns_false_if_hyde_does_not_have_base_url() + { + config(['hyde.site_url' => '']); + $this->assertFalse(RssFeedService::canGenerateFeed()); + } + + public function test_can_generate_sitemap_helper_returns_false_if_sitemaps_are_disabled_in_config() + { + config(['hyde.site_url' => 'foo']); + config(['hyde.generate_rss_feed' => false]); + $this->assertFalse(RssFeedService::canGenerateFeed()); + } +} diff --git a/packages/framework/tests/Feature/Services/SitemapServiceTest.php b/packages/framework/tests/Feature/Services/SitemapServiceTest.php new file mode 100644 index 00000000000..7d96fd61218 --- /dev/null +++ b/packages/framework/tests/Feature/Services/SitemapServiceTest.php @@ -0,0 +1,144 @@ +assertInstanceOf('SimpleXMLElement', $service->xmlElement); + } + + public function test_generate_adds_default_pages_to_xml() + { + $service = new SitemapService(); + $service->generate(); + + // Test runner has an index and 404 page, so we are using that as a baseline + $this->assertCount(2, $service->xmlElement->url); + } + + public function test_generate_adds_markdown_pages_to_xml() + { + touch(Hyde::path('_pages/foo.md')); + + $service = new SitemapService(); + $service->generate(); + + $this->assertCount(3, $service->xmlElement->url); + + unlink(Hyde::path('_pages/foo.md')); + } + + public function test_generate_adds_markdown_posts_to_xml() + { + touch(Hyde::path('_posts/foo.md')); + + $service = new SitemapService(); + $service->generate(); + + $this->assertCount(3, $service->xmlElement->url); + + unlink(Hyde::path('_posts/foo.md')); + } + + public function test_generate_adds_documentation_pages_to_xml() + { + touch(Hyde::path('_docs/foo.md')); + + $service = new SitemapService(); + $service->generate(); + + $this->assertCount(3, $service->xmlElement->url); + + unlink(Hyde::path('_docs/foo.md')); + } + + public function test_get_xml_returns_xml_string() + { + $service = new SitemapService(); + $service->generate(); + $xml = $service->getXML(); + + $this->assertIsString($xml); + $this->assertStringStartsWith('', $xml); + } + + public function test_generate_sitemap_shorthand_method_returns_xml_string() + { + $xml = SitemapService::generateSitemap(); + + $this->assertIsString($xml); + $this->assertStringStartsWith('', $xml); + } + + public function test_can_generate_sitemap_helper_returns_true_if_hyde_has_base_url() + { + config(['hyde.site_url' => 'foo']); + $this->assertTrue(SitemapService::canGenerateSitemap()); + } + + public function test_can_generate_sitemap_helper_returns_false_if_hyde_does_not_have_base_url() + { + config(['hyde.site_url' => '']); + $this->assertFalse(SitemapService::canGenerateSitemap()); + } + + public function test_can_generate_sitemap_helper_returns_false_if_sitemaps_are_disabled_in_config() + { + config(['hyde.site_url' => 'foo']); + config(['hyde.generate_sitemap' => false]); + $this->assertFalse(SitemapService::canGenerateSitemap()); + } + + public function test_url_item_is_generated_correctly() + { + config(['hyde.pretty_urls' => false]); + config(['hyde.site_url' => 'https://example.com']); + touch(Hyde::path('_pages/0-test.blade.php')); + + $service = new SitemapService(); + $service->generate(); + + $url = $service->xmlElement->url[0]; + $this->assertEquals('https://example.com/0-test.html', $url->loc); + $this->assertEquals('daily', $url->changefreq); + $this->assertEquals(date('c'), $url->lastmod); + + unlink(Hyde::path('_pages/0-test.blade.php')); + } + + public function test_url_item_is_generated_with_pretty_ur_ls_if_enabled() + { + config(['hyde.pretty_urls' => true]); + config(['hyde.site_url' => 'https://example.com']); + touch(Hyde::path('_pages/0-test.blade.php')); + + $service = new SitemapService(); + $service->generate(); + + $url = $service->xmlElement->url[0]; + $this->assertEquals('https://example.com/0-test', $url->loc); + + unlink(Hyde::path('_pages/0-test.blade.php')); + } +} diff --git a/packages/framework/tests/Feature/Services/StarterFileServiceTest.php b/packages/framework/tests/Feature/Services/StarterFileServiceTest.php new file mode 100644 index 00000000000..f3cd436c2ea --- /dev/null +++ b/packages/framework/tests/Feature/Services/StarterFileServiceTest.php @@ -0,0 +1,32 @@ +assertFileExists(Hyde::path($file)); + } + } +} diff --git a/packages/framework/tests/Feature/SourceDirectoriesCanBeChangedTest.php b/packages/framework/tests/Feature/SourceDirectoriesCanBeChangedTest.php new file mode 100644 index 00000000000..137616a7f15 --- /dev/null +++ b/packages/framework/tests/Feature/SourceDirectoriesCanBeChangedTest.php @@ -0,0 +1,66 @@ +assertEquals('_pages', BladePage::$sourceDirectory); + $this->assertEquals('_pages', MarkdownPage::$sourceDirectory); + $this->assertEquals('_posts', MarkdownPost::$sourceDirectory); + $this->assertEquals('_docs', DocumentationPage::$sourceDirectory); + } + + public function test_source_directories_can_be_changed_programmatically() + { + BladePage::$sourceDirectory = '.source/pages'; + MarkdownPage::$sourceDirectory = '.source/pages'; + MarkdownPost::$sourceDirectory = '.source/posts'; + DocumentationPage::$sourceDirectory = '.source/docs'; + + $this->assertEquals('.source/pages', BladePage::$sourceDirectory); + $this->assertEquals('.source/pages', MarkdownPage::$sourceDirectory); + $this->assertEquals('.source/posts', MarkdownPost::$sourceDirectory); + $this->assertEquals('.source/docs', DocumentationPage::$sourceDirectory); + } + + public function test_build_service_recognizes_changed_directory() + { + MarkdownPost::$sourceDirectory = '_source/posts'; + + $this->assertEquals( + '_source/posts', + DiscoveryService::getFilePathForModelClassFiles(MarkdownPost::class) + ); + } + + public function test_autodiscovery_discovers_posts_in_changed_directory() + { + // Using a subdirectory in a directory we know exists, to make cleanup easier. + mkdir(Hyde::path('_posts/test')); + touch(Hyde::path('_posts/test/test.md')); + + MarkdownPost::$sourceDirectory = '_posts/test'; + + $this->assertEquals( + ['test'], + CollectionService::getSourceFileListForModel(MarkdownPost::class) + ); + + unlink(Hyde::path('_posts/test/test.md')); + rmdir(Hyde::path('_posts/test')); + } +} diff --git a/packages/framework/tests/Feature/StaticPageBuilderTest.php b/packages/framework/tests/Feature/StaticPageBuilderTest.php new file mode 100644 index 00000000000..76f022e6e26 --- /dev/null +++ b/packages/framework/tests/Feature/StaticPageBuilderTest.php @@ -0,0 +1,116 @@ +__invoke(); + } + + protected function tearDown(): void + { + // Restore the file environment and any backed up files + restoreDirectory(Hyde::path('_site')); + + parent::tearDown(); + } + + protected function validateBasicHtml(string $html) + { + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + } + + public function test_can_build_blade_page() + { + file_put_contents(BladePage::$sourceDirectory.'/foo.blade.php', 'bar'); + + $page = new BladePage('foo'); + + new StaticPageBuilder($page, true); + + $this->assertFileExists(Hyde::path('_site/foo.html')); + $this->assertStringEqualsFile(Hyde::path('_site/foo.html'), 'bar'); + + unlink(BladePage::$sourceDirectory.'/foo.blade.php'); + } + + public function test_can_build_markdown_post() + { + $page = new MarkdownPost([ + 'title' => 'foo', + 'author' => 'bar', + ], '# Body', 'Title', 'foo'); + + new StaticPageBuilder($page, true); + + $this->assertFileExists(Hyde::path('_site/posts/foo.html')); + $this->validateBasicHtml(file_get_contents(Hyde::path('_site/posts/foo.html'))); + } + + public function test_can_build_markdown_page() + { + $page = new MarkdownPage([], '# Body', 'Title', 'foo'); + + new StaticPageBuilder($page, true); + + $this->assertFileExists(Hyde::path('_site/foo.html')); + $this->validateBasicHtml(file_get_contents(Hyde::path('_site/foo.html'))); + } + + public function test_can_build_documentation_page() + { + $page = new DocumentationPage([], '# Body', 'Title', 'foo'); + + new StaticPageBuilder($page, true); + + $this->assertFileExists(Hyde::path('_site/'.Hyde::docsDirectory().'/foo.html')); + $this->validateBasicHtml(file_get_contents(Hyde::path('_site/'.Hyde::docsDirectory().'/foo.html'))); + } + + public function test_creates_custom_documentation_directory() + { + $page = new DocumentationPage([], '# Body', 'Title', 'foo'); + + Config::set('docs.output_directory', 'docs/foo'); + + new StaticPageBuilder($page, true); + + $this->assertFileExists(Hyde::path('_site/docs/foo/foo.html')); + $this->validateBasicHtml(file_get_contents(Hyde::path('_site/docs/foo/foo.html'))); + } +} diff --git a/packages/framework/tests/Pest.php b/packages/framework/tests/Pest.php new file mode 100644 index 00000000000..92fa2827631 --- /dev/null +++ b/packages/framework/tests/Pest.php @@ -0,0 +1,106 @@ +in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ + +function backup(string $filepath) +{ + if (file_exists($filepath)) { + copy($filepath, $filepath.'.bak'); + } +} + +function restore(string $filepath) +{ + if (file_exists($filepath.'.bak')) { + copy($filepath.'.bak', $filepath); + unlink($filepath.'.bak'); + } +} + +function unlinkIfExists(string $filepath) +{ + if (file_exists($filepath)) { + unlink($filepath); + } +} + +function backupDirectory(string $directory) +{ + if (file_exists($directory)) { + File::copyDirectory($directory, $directory.'-bak', true); + } +} + +function restoreDirectory(string $directory) +{ + if (file_exists($directory.'-bak')) { + File::moveDirectory($directory.'-bak', $directory, true); + File::deleteDirectory($directory.'-bak'); + } +} + +function deleteDirectory(string $directory) +{ + if (file_exists($directory)) { + File::deleteDirectory($directory); + } +} + +function createTestPost(?string $path = null): string +{ + $path = Hyde::path($path ?? '_posts/test-post.md'); + file_put_contents($path, '--- +title: My New Post +category: blog +author: Mr. Hyde +--- + +# My New Post + +This is a post stub used in the automated tests +'); + + return $path; +} + +uses()->group('validators')->in('validators'); diff --git a/packages/framework/tests/ResetsApplication.php b/packages/framework/tests/ResetsApplication.php new file mode 100644 index 00000000000..760c42e7e2a --- /dev/null +++ b/packages/framework/tests/ResetsApplication.php @@ -0,0 +1,23 @@ +__invoke(); + Hyde::copy(Hyde::vendorPath('resources/views/homepages/welcome.blade.php'), Hyde::path('_pages/index.blade.php')); + Hyde::copy(Hyde::vendorPath('resources/views/pages/404.blade.php'), Hyde::path('_pages/404.blade.php')); + } +} diff --git a/packages/framework/tests/TestCase.php b/packages/framework/tests/TestCase.php new file mode 100644 index 00000000000..1567f7b9600 --- /dev/null +++ b/packages/framework/tests/TestCase.php @@ -0,0 +1,39 @@ +resetApplication(); + + self::$booted = true; + } + } + + /** + * Clean up the testing environment before the next test. + * + * @return void + */ + protected function tearDown(): void + { + parent::tearDown(); + } +} diff --git a/packages/framework/tests/Unit/AuthorGetNameTest.php b/packages/framework/tests/Unit/AuthorGetNameTest.php new file mode 100644 index 00000000000..150c03b759a --- /dev/null +++ b/packages/framework/tests/Unit/AuthorGetNameTest.php @@ -0,0 +1,31 @@ +name = 'John Doe'; + + $this->assertEquals('John Doe', $author->getName()); + } + + // Test get name helper returns username if name is not set + public function test_get_name_helper_returns_username_if_name_is_not_set() + { + $author = new Author('username'); + + $this->assertEquals('username', $author->getName()); + } +} diff --git a/packages/framework/tests/Unit/BlogPostFrontMatterIsOptionalTest.php b/packages/framework/tests/Unit/BlogPostFrontMatterIsOptionalTest.php new file mode 100644 index 00000000000..9702fa7525a --- /dev/null +++ b/packages/framework/tests/Unit/BlogPostFrontMatterIsOptionalTest.php @@ -0,0 +1,44 @@ +assertFileExists(Hyde::path('_site/posts/test-post.html')); + + unlink(Hyde::path('_posts/test-post.md')); + unlink(Hyde::path('_site/posts/test-post.html')); + } + + // Test blog post feed can be rendered when having post without front matter + public function testBlogPostFeedCanBeRenderedWhenPostHasNoFrontMatter() + { + file_put_contents(Hyde::path('_posts/test-post.md'), '# My New Post'); + + // Create a temporary page to test the feed + file_put_contents(Hyde::path('_pages/feed-test.blade.php'), + '@foreach(Hyde::getLatestPosts() as $post) + @include(\'hyde::components.article-excerpt\') + @endforeach' + ); + + Artisan::call('rebuild _pages/feed-test.blade.php'); + + $this->assertFileExists(Hyde::path('_site/feed-test.html')); + + unlink(Hyde::path('_posts/test-post.md')); + unlink(Hyde::path('_pages/feed-test.blade.php')); + unlink(Hyde::path('_site/feed-test.html')); + } +} diff --git a/packages/framework/tests/Unit/CreatesDefaultDirectoriesTest.php b/packages/framework/tests/Unit/CreatesDefaultDirectoriesTest.php new file mode 100644 index 00000000000..dfe257cb5cf --- /dev/null +++ b/packages/framework/tests/Unit/CreatesDefaultDirectoriesTest.php @@ -0,0 +1,29 @@ +assertTrue(is_dir(Hyde::path($directory))); + } + } +} diff --git a/packages/framework/tests/Unit/DateStringTest.php b/packages/framework/tests/Unit/DateStringTest.php new file mode 100644 index 00000000000..891b6819f4c --- /dev/null +++ b/packages/framework/tests/Unit/DateStringTest.php @@ -0,0 +1,56 @@ +assertEquals('2020-01-01', $dateString->string); + } + + // Test it can parse date string into datetime object + public function test_it_can_parse_date_string_into_datetime_object() + { + $dateString = new DateString('2020-01-01 UTC'); + $this->assertInstanceOf(DateTime::class, $dateString->dateTimeObject); + } + + // Test it can format date string into a machine-readable string + public function test_it_can_format_date_string_into_machine_readable_string() + { + $dateString = new DateString('2020-01-01 UTC'); + $this->assertEquals('2020-01-01T00:00:00+00:00', $dateString->datetime); + } + + // Test it can format date string into a human-readable string + public function test_it_can_format_date_string_into_human_readable_string() + { + $dateString = new DateString('2020-01-01 UTC'); + $this->assertEquals('Wednesday Jan 1st, 2020, at 12:00am', $dateString->sentence); + } + + // Test it can format date string into a short human-readable string + public function test_it_can_format_date_string_into_short_human_readable_string() + { + $dateString = new DateString('2020-01-01 UTC'); + $this->assertEquals('Jan 1st, 2020', $dateString->short); + } + + // Test it handles invalid date strings + public function test_it_handles_invalid_date_strings() + { + $this->expectException(CouldNotParseDateStringException::class); + new DateString('foo bar'); + } +} diff --git a/packages/framework/tests/Unit/DiscoveryServiceCanFindModelFromCustomSourceFilePathTest.php b/packages/framework/tests/Unit/DiscoveryServiceCanFindModelFromCustomSourceFilePathTest.php new file mode 100644 index 00000000000..81ab4624e52 --- /dev/null +++ b/packages/framework/tests/Unit/DiscoveryServiceCanFindModelFromCustomSourceFilePathTest.php @@ -0,0 +1,62 @@ +assertEquals( + BladePage::class, + DiscoveryService::findModelFromFilePath('.source/pages/test.blade.php') + ); + } + + public function test_method_can_find_markdown_pages() + { + $this->assertEquals( + MarkdownPage::class, + DiscoveryService::findModelFromFilePath('.source/pages/test.md') + ); + } + + public function test_method_can_find_markdown_posts() + { + $this->assertEquals( + MarkdownPost::class, + DiscoveryService::findModelFromFilePath('.source/posts/test.md') + ); + } + + public function test_method_can_find_documentation_pages() + { + $this->assertEquals( + DocumentationPage::class, + DiscoveryService::findModelFromFilePath('.source/docs/test.md') + ); + } +} diff --git a/packages/framework/tests/Unit/DocumentationPageParserTest.php b/packages/framework/tests/Unit/DocumentationPageParserTest.php new file mode 100644 index 00000000000..18b416c652d --- /dev/null +++ b/packages/framework/tests/Unit/DocumentationPageParserTest.php @@ -0,0 +1,42 @@ +get(); + $this->assertInstanceOf(DocumentationPage::class, $page); + } +} diff --git a/packages/framework/tests/Unit/EnsureCommandsFollowNamingConventionTest.php b/packages/framework/tests/Unit/EnsureCommandsFollowNamingConventionTest.php new file mode 100644 index 00000000000..2517ad7f6d2 --- /dev/null +++ b/packages/framework/tests/Unit/EnsureCommandsFollowNamingConventionTest.php @@ -0,0 +1,23 @@ +markTestSkipped('No commands found.'); + } + + foreach ($files as $filepath) { + $filename = basename($filepath, '.php'); + $this->assertStringStartsWith('Hyde', $filename); + $this->assertStringEndsWith('Command', $filename); + } + } +} diff --git a/packages/framework/tests/Unit/FileCacheServiceUnixsumMethodTest.php b/packages/framework/tests/Unit/FileCacheServiceUnixsumMethodTest.php new file mode 100644 index 00000000000..0068178279f --- /dev/null +++ b/packages/framework/tests/Unit/FileCacheServiceUnixsumMethodTest.php @@ -0,0 +1,80 @@ +assertIsString(Service::unixsum('foo')); + } + + public function test_method_returns_string_with_length_of_32() + { + $this->assertEquals(32, strlen(Service::unixsum('foo'))); + } + + public function test_method_returns_string_matching_expected_format() + { + $this->assertMatchesRegularExpression('/^[a-f0-9]{32}$/', Service::unixsum('foo')); + } + + public function test_method_returns_same_value_for_same_string_using_normal_method() + { + $this->assertEquals(md5('foo'), Service::unixsum('foo')); + } + + public function test_method_returns_different_value_for_different_string() + { + $this->assertNotEquals(Service::unixsum('foo'), Service::unixsum('bar')); + } + + public function test_function_is_case_sensitive() + { + $this->assertNotEquals(Service::unixsum('foo'), Service::unixsum('FOO')); + } + + public function test_function_is_space_sensitive() + { + $this->assertNotEquals(Service::unixsum(' foo '), Service::unixsum('foo')); + } + + public function test_method_returns_same_value_regardless_of_end_of_line_sequence() + { + $this->assertEquals(Service::unixsum('foo'), Service::unixsum('foo')); + $this->assertEquals(Service::unixsum("foo\n"), Service::unixsum("foo\n")); + $this->assertEquals(Service::unixsum("foo\n"), Service::unixsum("foo\r")); + $this->assertEquals(Service::unixsum("foo\n"), Service::unixsum("foo\r\n")); + } + + public function test_method_returns_same_value_for_string_with_mixed_end_of_line_sequences() + { + $this->assertEquals(Service::unixsum("foo\nbar\r\nbaz\r\n"), + Service::unixsum("foo\nbar\nbaz\n")); + } + + public function test_method_returns_same_value_when_loaded_from_file() + { + $string = "foo\nbar\r\nbaz\r\n"; + $file = tempnam(sys_get_temp_dir(), 'foo'); + file_put_contents($file, $string); + + $this->assertEquals(Service::unixsum($string), Service::unixsum(file_get_contents($file))); + + unlink($file); + } + + public function test_method_returns_same_value_when_loaded_from_file_using_shorthand() + { + $string = "foo\nbar\r\nbaz\r\n"; + $file = tempnam(sys_get_temp_dir(), 'foo'); + file_put_contents($file, $string); + + $this->assertEquals(Service::unixsum($string), Service::unixsumFile($file)); + + unlink($file); + } +} diff --git a/packages/framework/tests/Unit/FileHelperPageLinkPrettyUrlTest.php b/packages/framework/tests/Unit/FileHelperPageLinkPrettyUrlTest.php new file mode 100644 index 00000000000..d4c1a6bb959 --- /dev/null +++ b/packages/framework/tests/Unit/FileHelperPageLinkPrettyUrlTest.php @@ -0,0 +1,95 @@ + false]); + + $this->assertEquals('foo/bar.html', Hyde::pageLink('foo/bar.html')); + } + + public function test_helper_returns_pretty_url_if_pretty_urls_is_true() + { + config(['hyde.pretty_urls' => true]); + + $this->assertEquals('foo/bar', Hyde::pageLink('foo/bar.html')); + } + + public function test_non_pretty_urls_is_default_value_when_config_is_not_set() + { + config(['hyde.pretty_urls' => null]); + + $this->assertEquals('foo/bar.html', Hyde::pageLink('foo/bar.html')); + } + + public function test_helper_respects_absolute_urls() + { + config(['hyde.pretty_urls' => false]); + $this->assertEquals('/foo/bar.html', Hyde::pageLink('/foo/bar.html')); + } + + public function test_helper_respects_pretty_absolute_urls() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('/foo/bar', Hyde::pageLink('/foo/bar.html')); + } + + public function test_helper_respects_relative_urls() + { + config(['hyde.pretty_urls' => false]); + $this->assertEquals('../foo/bar.html', Hyde::pageLink('../foo/bar.html')); + } + + public function test_helper_respects_pretty_relative_urls() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('../foo/bar', Hyde::pageLink('../foo/bar.html')); + } + + public function test_non_html_links_are_not_modified() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('/foo/bar.jpg', Hyde::pageLink('/foo/bar.jpg')); + } + + public function test_helper_respects_absolute_urls_with_pretty_urls_enabled() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('/foo/bar.jpg', Hyde::pageLink('/foo/bar.jpg')); + } + + public function test_helper_rewrites_index_when_using_pretty_urls() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('/', Hyde::pageLink('index.html')); + } + + public function test_helper_does_not_rewrite_index_when_not_using_pretty_urls() + { + config(['hyde.pretty_urls' => false]); + $this->assertEquals('index.html', Hyde::pageLink('index.html')); + } + + public function test_helper_rewrites_documentation_page_index_when_using_pretty_urls() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('docs/', Hyde::pageLink('docs/index.html')); + } + + public function test_helper_does_not_rewrite_documentation_page_index_when_not_using_pretty_urls() + { + config(['hyde.pretty_urls' => false]); + $this->assertEquals('docs/index.html', Hyde::pageLink('docs/index.html')); + } +} diff --git a/packages/framework/tests/Unit/FileHelperRelativeLinkTest.php b/packages/framework/tests/Unit/FileHelperRelativeLinkTest.php new file mode 100644 index 00000000000..07b4a7e9f1d --- /dev/null +++ b/packages/framework/tests/Unit/FileHelperRelativeLinkTest.php @@ -0,0 +1,111 @@ +assertEquals('foo/bar.html', Hyde::relativeLink('foo/bar.html')); + } + + // Test helper injects the proper number of `../` + public function test_helper_injects_proper_number_of_doubles_slash() + { + $this->assertEquals('../foo.html', Hyde::relativeLink('foo.html', 'foo/bar.html')); + } + + // Test helper injects the proper number of `../` for deeply nested $current paths + public function test_helper_injects_proper_number_of_doubles_slash_for_deeply_nested_paths() + { + $this->assertEquals('../../../foo.html', Hyde::relativeLink('foo.html', 'foo/bar/baz/qux.html')); + } + + // Test helper handles destination without file extension + public function test_helper_handles_destination_without_file_extension() + { + $this->assertEquals('../foo', Hyde::relativeLink('foo', 'foo/bar.html')); + } + + // Test helper handles $current without file extension + public function test_helper_handles_current_without_file_extension() + { + $this->assertEquals('../foo.html', Hyde::relativeLink('foo.html', 'foo/bar')); + } + + // Test helper handles case without any file extensions + public function test_helper_handles_case_without_any_file_extensions() + { + $this->assertEquals('../foo', Hyde::relativeLink('foo', 'foo/bar')); + } + + // Test helper handles case with mixed file extensions + public function test_helper_handles_case_with_mixed_file_extensions() + { + $this->assertEquals('../foo.md', Hyde::relativeLink('foo.md', 'foo/bar.md')); + $this->assertEquals('../foo.txt', Hyde::relativeLink('foo.txt', 'foo/bar.txt')); + } + + // Test helper handles different file extensions + public function test_helper_handles_different_file_extensions() + { + $this->assertEquals('../foo.png', Hyde::relativeLink('foo.png', 'foo/bar')); + $this->assertEquals('../foo.css', Hyde::relativeLink('foo.css', 'foo/bar')); + $this->assertEquals('../foo.js', Hyde::relativeLink('foo.js', 'foo/bar')); + } + + // Test helper returns pretty URL if enabled and destination is a HTML file + public function test_helper_returns_pretty_url_if_enabled_and_destination_is_a_html_file() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('../foo', Hyde::relativeLink('foo.html', 'foo/bar.html')); + } + + // Test helper method does not require current path to be HTML to use pretty URLs + public function test_helper_method_does_not_require_current_path_to_be_html_to_use_pretty_urls() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('../foo', Hyde::relativeLink('foo.html', 'foo/bar')); + } + + // Test helper returns does not return pretty URL if when enabled but and destination is not a HTML file + public function test_helper_returns_does_not_return_pretty_url_if_when_enabled_but_and_destination_is_not_a_html_file() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('../foo.png', Hyde::relativeLink('foo.png', 'foo/bar.html')); + } + + public function test_helper_rewrites_index_when_using_pretty_urls() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('/', Hyde::relativeLink('index.html', 'foo.html')); + $this->assertEquals('../', Hyde::relativeLink('index.html', 'foo/bar.html')); + $this->assertEquals('../../', Hyde::relativeLink('index.html', 'foo/bar/baz.html')); + } + + public function test_helper_does_not_rewrite_index_when_not_using_pretty_urls() + { + config(['hyde.pretty_urls' => false]); + $this->assertEquals('index.html', Hyde::relativeLink('index.html', 'foo.html')); + $this->assertEquals('../index.html', Hyde::relativeLink('index.html', 'foo/bar.html')); + $this->assertEquals('../../index.html', Hyde::relativeLink('index.html', 'foo/bar/baz.html')); + } + + public function test_helper_rewrites_documentation_page_index_when_using_pretty_urls() + { + config(['hyde.pretty_urls' => true]); + $this->assertEquals('docs/', Hyde::relativeLink('docs/index.html', 'foo.html')); + $this->assertEquals('docs/', Hyde::relativeLink('docs/index.html', 'docs.html')); + $this->assertEquals('../docs/', Hyde::relativeLink('docs/index.html', 'foo/bar.html')); + $this->assertEquals('../docs/', Hyde::relativeLink('docs/index.html', 'docs/foo.html')); + } +} diff --git a/packages/framework/tests/Unit/FluentPathHelpersTest.php b/packages/framework/tests/Unit/FluentPathHelpersTest.php new file mode 100644 index 00000000000..e6b22d33523 --- /dev/null +++ b/packages/framework/tests/Unit/FluentPathHelpersTest.php @@ -0,0 +1,178 @@ +assertEquals( + Hyde::path('_posts'), + Hyde::getModelSourcePath(MarkdownPost::class) + ); + + $this->assertEquals( + Hyde::path('_pages'), + Hyde::getModelSourcePath(MarkdownPage::class) + ); + + $this->assertEquals( + Hyde::path('_docs'), + Hyde::getModelSourcePath(DocumentationPage::class) + ); + + $this->assertEquals( + Hyde::path('_pages'), + Hyde::getModelSourcePath(BladePage::class) + ); + } + + public function test_get_model_source_path_method_returns_path_to_file_for_model_classes() + { + $this->assertEquals( + Hyde::path('_posts'.DIRECTORY_SEPARATOR.'foo.md'), + Hyde::getModelSourcePath(MarkdownPost::class, 'foo.md') + ); + + $this->assertEquals( + Hyde::path('_pages'.DIRECTORY_SEPARATOR.'foo.md'), + Hyde::getModelSourcePath(MarkdownPage::class, 'foo.md') + ); + + $this->assertEquals( + Hyde::path('_docs'.DIRECTORY_SEPARATOR.'foo.md'), + Hyde::getModelSourcePath(DocumentationPage::class, 'foo.md') + ); + + $this->assertEquals( + Hyde::path('_pages'.DIRECTORY_SEPARATOR.'foo.md'), + Hyde::getModelSourcePath(BladePage::class, 'foo.md') + ); + } + + public function test_helper_for_blade_pages() + { + $this->assertEquals( + Hyde::path('_pages'), + Hyde::getBladePagePath() + ); + } + + public function test_helper_for_markdown_pages() + { + $this->assertEquals( + Hyde::path('_pages'), + Hyde::getMarkdownPagePath() + ); + } + + public function test_helper_for_markdown_posts() + { + $this->assertEquals( + Hyde::path('_posts'), + Hyde::getMarkdownPostPath() + ); + } + + public function test_helper_for_documentation_pages() + { + $this->assertEquals( + Hyde::path('_docs'), + Hyde::getDocumentationPagePath() + ); + } + + public function test_helper_for_site_output_path() + { + $this->assertEquals( + Hyde::path('_site'), + Hyde::getSiteOutputPath() + ); + } + + public function test_helper_for_site_output_path_returns_path_to_file_within_the_directory() + { + $this->assertEquals( + Hyde::path('_site'.DIRECTORY_SEPARATOR.'foo.html'), + Hyde::getSiteOutputPath('foo.html') + ); + } + + public function test_get_site_output_path_returns_absolute_path() + { + $this->assertEquals( + Hyde::path('_site'), + Hyde::getSiteOutputPath() + ); + } + + public function test_site_output_path_helper_ignores_trailing_slashes() + { + $this->assertEquals( + Hyde::path('_site'.DIRECTORY_SEPARATOR.'foo.html'), + Hyde::getSiteOutputPath('/foo.html/') + ); + } + + public function test_path_to_relative_helper_decodes_hyde_path_into_relative() + { + $s = DIRECTORY_SEPARATOR; + $this->assertEquals('foo', Hyde::pathToRelative(Hyde::path('foo'))); + $this->assertEquals('foo', Hyde::pathToRelative(Hyde::path('/foo/'))); + $this->assertEquals('foo.md', Hyde::pathToRelative(Hyde::path('foo.md'))); + $this->assertEquals("foo{$s}bar", Hyde::pathToRelative(Hyde::path("foo{$s}bar"))); + $this->assertEquals("foo{$s}bar.md", Hyde::pathToRelative(Hyde::path("foo{$s}bar.md"))); + } + + public function test_path_to_relative_helper_does_not_modify_already_relative_paths() + { + $this->assertEquals('foo', Hyde::pathToRelative('foo')); + $this->assertEquals('foo/', Hyde::pathToRelative('foo/')); + $this->assertEquals('../foo', Hyde::pathToRelative('../foo')); + $this->assertEquals('../foo/', Hyde::pathToRelative('../foo/')); + $this->assertEquals('foo.md', Hyde::pathToRelative('foo.md')); + $this->assertEquals('foo/bar', Hyde::pathToRelative('foo/bar')); + $this->assertEquals('foo/bar.md', Hyde::pathToRelative('foo/bar.md')); + } + + public function test_path_to_relative_helper_does_not_modify_non_project_paths() + { + $testStrings = [ + 'C:\Documents\Newsletters\Summer2018.pdf', + '\Program Files\Custom Utilities\StringFinder.exe', + '2018\January.xlsx', + '..\Publications\TravelBrochure.pdf', + 'C:\Projects\library\library.sln', + 'C:Projects\library\library.sln', + '/home/seth/Pictures/penguin.jpg', + '~/Pictures/penguin.jpg', + ]; + + foreach ($testStrings as $testString) { + $this->assertEquals( + $this->systemPath(($testString)), + Hyde::pathToRelative( + $this->systemPath($testString) + ) + ); + } + } + + protected function systemPath(string $path): string + { + return str_replace('/', DIRECTORY_SEPARATOR, $path); + } +} diff --git a/packages/framework/tests/Unit/GetLatestMarkdownPostsTest.php b/packages/framework/tests/Unit/GetLatestMarkdownPostsTest.php new file mode 100644 index 00000000000..ec397f8f06e --- /dev/null +++ b/packages/framework/tests/Unit/GetLatestMarkdownPostsTest.php @@ -0,0 +1,31 @@ +assertCount(2, $collection); + $this->assertInstanceOf(Collection::class, $collection); + $this->assertContainsOnlyInstancesOf(MarkdownPost::class, $collection); + + $this->assertEquals('new', $collection->first()->slug); + $this->assertEquals('old', $collection->last()->slug); + + unlink(Hyde::path('_posts/new.md')); + unlink(Hyde::path('_posts/old.md')); + } +} diff --git a/packages/framework/tests/Unit/HasAuthorTest.php b/packages/framework/tests/Unit/HasAuthorTest.php new file mode 100644 index 00000000000..c822a0917ab --- /dev/null +++ b/packages/framework/tests/Unit/HasAuthorTest.php @@ -0,0 +1,48 @@ +matter = [ + 'author' => 'John Doe', + ]; + + $this->constructAuthor(); + $this->assertInstanceOf(Author::class, $this->author); + $this->assertEquals('John Doe', $this->author->username); + $this->assertNull($this->author->name); + $this->assertNull($this->author->website); + } + + // Test it can create a new author instance from user array + public function test_it_can_create_a_new_author_instance_from_user_array() + { + $this->matter['author'] = [ + 'username' => 'john_doe', + 'name' => 'John Doe', + 'website' => 'https://example.com', + ]; + $this->constructAuthor(); + $this->assertInstanceOf(Author::class, $this->author); + $this->assertEquals('john_doe', $this->author->username); + $this->assertEquals('John Doe', $this->author->name); + $this->assertEquals('https://example.com', $this->author->website); + } +} diff --git a/packages/framework/tests/Unit/HasDynamicTitleTest.php b/packages/framework/tests/Unit/HasDynamicTitleTest.php new file mode 100644 index 00000000000..efdd5424cc9 --- /dev/null +++ b/packages/framework/tests/Unit/HasDynamicTitleTest.php @@ -0,0 +1,41 @@ + 'My Title', + ], body: ''); + + $this->assertEquals('My Title', $document->findTitleForDocument()); + } + + // Test it can find title from H1 tag. + public function testFindTitleFromH1Tag() + { + $document = new MarkdownDocument([], body: '# My Title'); + + $this->assertEquals('My Title', $document->findTitleForDocument()); + } + + // Test it can find title from slug. + public function testFindTitleFromSlug() + { + $document = new MarkdownDocument([], body: '', slug: 'my-title'); + $this->assertEquals('My Title', $document->findTitleForDocument()); + } +} diff --git a/packages/framework/tests/Unit/HasFeaturedImageTest.php b/packages/framework/tests/Unit/HasFeaturedImageTest.php new file mode 100644 index 00000000000..875a4ee6920 --- /dev/null +++ b/packages/framework/tests/Unit/HasFeaturedImageTest.php @@ -0,0 +1,71 @@ +matter = [ + 'image' => 'https://example.com/image.jpg', + ]; + + $this->constructFeaturedImage(); + $this->assertInstanceOf(Image::class, $this->image); + $this->assertEquals('https://example.com/image.jpg', $this->image->uri); + } + + // Test it can create a new Image instance from an array + public function test_it_can_create_a_new_image_instance_from_an_array() + { + $this->matter = [ + 'image' => [ + 'uri' => 'https://example.com/image.jpg', + ], + ]; + + $this->constructFeaturedImage(); + $this->assertInstanceOf(Image::class, $this->image); + $this->assertEquals('https://example.com/image.jpg', $this->image->uri); + } + + // Test constructBaseImage() sets the source to the image's uri when supplied path is an uri + public function test_construct_base_image_sets_the_source_to_the_image_uri_when_supplied_path_is_an_uri() + { + $image = $this->constructBaseImage('https://example.com/image.jpg'); + $this->assertEquals('https://example.com/image.jpg', $image->getSource()); + } + + // Test constructBaseImage() sets the source to the image's path when supplied path is a local path + public function test_construct_base_image_sets_the_source_to_the_image_path_when_supplied_path_is_a_local_path() + { + $image = $this->constructBaseImage('/path/to/image.jpg'); + $this->assertEquals('/path/to/image.jpg', $image->getSource()); + } + + // Test constructBaseImage() returns an Image instance created from a string + public function test_construct_base_image_returns_an_image_instance_created_from_a_string() + { + $this->assertInstanceOf(Image::class, $this->constructBaseImage('')); + } + + // Test constructFullImage() returns an Image instance created from an array + public function test_construct_full_image_returns_an_image_instance_created_from_an_array() + { + $this->assertInstanceOf(Image::class, $this->constructFullImage([])); + } +} diff --git a/packages/framework/tests/Unit/HasMarkdownFeaturesTest.php b/packages/framework/tests/Unit/HasMarkdownFeaturesTest.php new file mode 100644 index 00000000000..28b636220e1 --- /dev/null +++ b/packages/framework/tests/Unit/HasMarkdownFeaturesTest.php @@ -0,0 +1,26 @@ +assertIsBool(static::hasTableOfContents()); + + Config::set('docs.table_of_contents.enabled', true); + $this->assertTrue(static::hasTableOfContents()); + + Config::set('docs.table_of_contents.enabled', false); + $this->assertFalse(static::hasTableOfContents()); + } +} diff --git a/packages/framework/tests/Unit/HasPageMetadataRssFeedLinkTest.php b/packages/framework/tests/Unit/HasPageMetadataRssFeedLinkTest.php new file mode 100644 index 00000000000..569ae12f102 --- /dev/null +++ b/packages/framework/tests/Unit/HasPageMetadataRssFeedLinkTest.php @@ -0,0 +1,107 @@ + 'foo']); + } + + public function test_can_use_rss_feed_link_adds_meta_link_for_markdown_posts() + { + $page = new MarkdownPost([], ''); + + $this->assertStringContainsString( + '', + $page->renderPageMetadata() + ); + } + + public function test_can_use_rss_feed_link_adds_meta_link_for_post_related_pages() + { + $page = new MarkdownPage([], '', slug: 'posts'); + + $this->assertStringContainsString( + '', + $page->renderPageMetadata() + ); + } + + public function test_can_use_rss_feed_link_adds_meta_link_for_markdown_index_page() + { + $page = new MarkdownPage([], '', slug: 'index'); + + $this->assertStringContainsString( + '', + $page->renderPageMetadata() + ); + } + + public function test_can_use_rss_feed_link_adds_meta_link_for_blade_index_page() + { + $page = new BladePage('index'); + + $this->assertStringContainsString( + '', + $page->renderPageMetadata() + ); + } + + public function test_can_use_rss_feed_link_does_not_add_meta_link_for_documentation_index_page() + { + $page = new DocumentationPage([], '', slug: 'index'); + + $this->assertStringNotContainsString( + '', + $page->renderPageMetadata() + ); + } + + public function test_can_use_rss_feed_uses_configured_site_url() + { + config(['hyde.site_url' => 'https://example.org']); + $page = new MarkdownPost([], ''); + + $this->assertStringContainsString( + '', + $page->renderPageMetadata() + ); + } + + public function test_can_use_rss_feed_uses_configured_rss_file_name() + { + config(['hyde.rss_filename' => 'posts.rss']); + $page = new MarkdownPost([], ''); + + $this->assertStringContainsString( + '', + $page->renderPageMetadata() + ); + } + + // Test link is not added if site url is not set + public function test_link_is_not_added_if_site_url_is_not_set() + { + config(['hyde.site_url' => '']); + $page = new MarkdownPost([], ''); + + $this->assertStringNotContainsString( + 'renderPageMetadata() + ); + } +} diff --git a/packages/framework/tests/Unit/HasTableOfContentsTest.php b/packages/framework/tests/Unit/HasTableOfContentsTest.php new file mode 100644 index 00000000000..e7c855ad7dd --- /dev/null +++ b/packages/framework/tests/Unit/HasTableOfContentsTest.php @@ -0,0 +1,33 @@ +assertClassHasAttribute('tableOfContents', static::class); + } + + public function testConstructorCreatesTableOfContentsString() + { + $this->body = '## Title'; + $this->constructTableOfContents(); + $this->assertIsString($this->tableOfContents); + $this->assertEquals('', str_replace("\n", '', $this->tableOfContents)); + } +} diff --git a/packages/framework/tests/Unit/HydeBasePathCanBeChangedTest.php b/packages/framework/tests/Unit/HydeBasePathCanBeChangedTest.php new file mode 100644 index 00000000000..c3611b07d3b --- /dev/null +++ b/packages/framework/tests/Unit/HydeBasePathCanBeChangedTest.php @@ -0,0 +1,41 @@ +basePath)) { + $this->basePath = Hyde::getBasePath(); + } + } + + protected function tearDown(): void + { + Hyde::setBasePath($this->basePath); + + parent::tearDown(); + } + + public function test_hyde_base_path_can_be_changed() + { + Hyde::setBasePath('/foo/bar'); + $this->assertEquals('/foo/bar', Hyde::getBasePath()); + $this->assertEquals('/foo/bar', Hyde::path()); + } +} diff --git a/packages/framework/tests/Unit/HydeGetBasePathHasFallbackTest.php b/packages/framework/tests/Unit/HydeGetBasePathHasFallbackTest.php new file mode 100644 index 00000000000..ac4e03ac175 --- /dev/null +++ b/packages/framework/tests/Unit/HydeGetBasePathHasFallbackTest.php @@ -0,0 +1,23 @@ +assertEquals(getcwd(), $mock::getBasePath()); + } +} diff --git a/packages/framework/tests/Unit/HydeGetLatestPostsHelperTest.php b/packages/framework/tests/Unit/HydeGetLatestPostsHelperTest.php new file mode 100644 index 00000000000..cddd519c65a --- /dev/null +++ b/packages/framework/tests/Unit/HydeGetLatestPostsHelperTest.php @@ -0,0 +1,31 @@ +assertInstanceOf(Collection::class, Hyde::getLatestPosts()); + } + + public function test_get_latest_posts_helper_returns_collection_with_posts() + { + file_put_contents(MarkdownPost::$sourceDirectory.'/foo.md', ''); + + $collection = Hyde::getLatestPosts(); + $this->assertTrue($collection->isNotEmpty()); + $this->assertInstanceOf(MarkdownPost::class, $collection->first()); + + unlink(MarkdownPost::$sourceDirectory.'/foo.md'); + } +} diff --git a/packages/framework/tests/Unit/HydePathHelperTest.php b/packages/framework/tests/Unit/HydePathHelperTest.php new file mode 100644 index 00000000000..86760756ec5 --- /dev/null +++ b/packages/framework/tests/Unit/HydePathHelperTest.php @@ -0,0 +1,47 @@ +toBeTrue(); +}); + +test('string is returned', function () { + expect(Hyde::path())->toBeString(); +}); + +test( + 'returned directory contains content expected to be in the project directory', + function () { + expect( + file_exists(Hyde::path().DIRECTORY_SEPARATOR.'hyde') && + file_exists(Hyde::path().DIRECTORY_SEPARATOR.'_pages') && + file_exists(Hyde::path().DIRECTORY_SEPARATOR.'_posts') && + file_exists(Hyde::path().DIRECTORY_SEPARATOR.'_site') + )->toBeTrue(); + } +); + +test('method returns qualified file path when supplied with argument', function () { + expect(Hyde::path('file.php'))->toEqual(Hyde::path().DIRECTORY_SEPARATOR.'file.php'); +}); + +test('method strips trailing directory separators from argument', function () { + expect(Hyde::path('\\/file.php/'))->toEqual(Hyde::path().DIRECTORY_SEPARATOR.'file.php'); +}); + +test('method returns expected value for nested path arguments', function () { + expect(Hyde::path('directory/file.php')) + ->toEqual(Hyde::path().DIRECTORY_SEPARATOR.'directory/file.php'); +}); + +test('method returns expected value regardless of trailing directory separators in argument', function () { + expect(Hyde::path('directory/file.php/')) + ->toEqual(Hyde::path().DIRECTORY_SEPARATOR.'directory/file.php'); + expect(Hyde::path('/directory/file.php/')) + ->toEqual(Hyde::path().DIRECTORY_SEPARATOR.'directory/file.php'); + expect(Hyde::path('\\/directory/file.php/')) + ->toEqual(Hyde::path().DIRECTORY_SEPARATOR.'directory/file.php'); +}); diff --git a/packages/framework/tests/Unit/HydeSafeCopyHelperTest.php b/packages/framework/tests/Unit/HydeSafeCopyHelperTest.php new file mode 100644 index 00000000000..b1b0cbd6af9 --- /dev/null +++ b/packages/framework/tests/Unit/HydeSafeCopyHelperTest.php @@ -0,0 +1,101 @@ +assertTrue(method_exists(Hyde::class, 'copy')); + } + + public function test_copy_method_returns404_if_source_file_does_not_exist() + { + $this->assertEquals( + 404, + Hyde::copy(static::testDir('/does/not/exist.txt'), static::testDir('/test.txt')) + ); + } + + public function test_copy_method_returns409_if_destination_file_exists_and_force_flag_is_not_set() + { + file_put_contents(static::testDir('/test.txt'), 'test'); + + $this->assertEquals( + 409, + Hyde::copy(static::testDir('/test.txt'), static::testDir('/test.txt')) + ); + + unlink(static::testDir('/test.txt')); + } + + public function test_copy_method_returns_true_if_destination_file_exists_and_force_flag_is_set() + { + file_put_contents(static::testDir('/foo.txt'), 'foo'); + + $this->assertTrue( + Hyde::copy(static::testDir('/foo.txt'), static::testDir('/bar.txt'), true) + ); + + unlink(static::testDir('/foo.txt')); + unlink(static::testDir('/bar.txt')); + } + + public function test_file_with_no_conflicts_is_copied() + { + file_put_contents(static::testDir('/foo.txt'), 'foo'); + + $this->assertTrue( + Hyde::copy(static::testDir('/foo.txt'), static::testDir('/bar.txt')) + ); + + $this->assertFileExists(static::testDir('/bar.txt')); + } + + public function test_file_with_conflicts_is_not_copied_when_force_flag_is_not_set() + { + file_put_contents(static::testDir('/foo.txt'), 'foo'); + file_put_contents(static::testDir('/bar.txt'), 'bar'); + + $this->assertEquals( + 409, + Hyde::copy(static::testDir('/foo.txt'), static::testDir('/bar.txt')) + ); + + $this->assertStringEqualsFile(static::testDir('/bar.txt'), 'bar'); + } + + public function test_file_with_conflicts_is_copied_when_force_flag_is_set() + { + file_put_contents(static::testDir('/foo.txt'), 'foo'); + file_put_contents(static::testDir('/bar.txt'), 'bar'); + + $this->assertTrue( + Hyde::copy(static::testDir('/foo.txt'), static::testDir('/bar.txt'), true) + ); + + $this->assertStringEqualsFile(static::testDir('/bar.txt'), 'foo'); + } + + public function tearDown(): void + { + deleteDirectory(static::testDir()); + + parent::tearDown(); + } +} diff --git a/packages/framework/tests/Unit/HydeServiceProviderTest.php b/packages/framework/tests/Unit/HydeServiceProviderTest.php new file mode 100644 index 00000000000..ebadcd522e1 --- /dev/null +++ b/packages/framework/tests/Unit/HydeServiceProviderTest.php @@ -0,0 +1,39 @@ +provider = new HydeServiceProvider(app()); + + parent::setUp(); + } + + public function test_provider_is_constructed() + { + $this->assertInstanceOf(HydeServiceProvider::class, $this->provider); + } + + public function test_provider_has_register_method() + { + $this->assertTrue(method_exists($this->provider, 'register')); + } + + public function test_provider_has_boot_method() + { + $this->assertTrue(method_exists($this->provider, 'boot')); + } + + public function test_provider_registers_hyde_versions_into_app_container() + { + $this->assertIsString(app('hyde.version')); + $this->assertIsString(app('framework.version')); + } +} diff --git a/packages/framework/tests/Unit/HydeUriPathHelperTest.php b/packages/framework/tests/Unit/HydeUriPathHelperTest.php new file mode 100644 index 00000000000..a39bc9c6939 --- /dev/null +++ b/packages/framework/tests/Unit/HydeUriPathHelperTest.php @@ -0,0 +1,19 @@ +assertFalse(Hyde::uriPath()); + } + + public function test_helper_returns_expected_string_when_site_url_is_set() + { + \Illuminate\Support\Facades\Config::set('hyde.site_url', 'https://example.com'); + $this->assertEquals('https://example.com/foo/bar.html', Hyde::uriPath('foo/bar.html')); + } +} diff --git a/packages/framework/tests/Unit/HydeVendorPathHelperTest.php b/packages/framework/tests/Unit/HydeVendorPathHelperTest.php new file mode 100644 index 00000000000..08dafa98ac6 --- /dev/null +++ b/packages/framework/tests/Unit/HydeVendorPathHelperTest.php @@ -0,0 +1,54 @@ +assertTrue(method_exists('Hyde', 'vendorPath')); + } + + public function test_method_returns_string() + { + $this->assertIsString(Hyde::vendorPath()); + } + + public function test_method_returns_string_containing_vendor_path() + { + $this->assertStringContainsString('vendor', Hyde::vendorPath()); + } + + public function test_method_returns_path_to_the_vendor_directory() + { + $this->assertDirectoryExists(Hyde::vendorPath()); + $this->assertFileExists(Hyde::vendorPath().'/composer.json'); + $this->assertStringContainsString('"name": "hyde/framework",', file_get_contents(Hyde::vendorPath().'/composer.json')); + } + + public function test_method_returns_qualified_file_path_when_supplied_with_argument() + { + $this->assertEquals(Hyde::vendorPath('file.php'), Hyde::vendorPath().'/file.php'); + } + + public function test_method_returns_expected_value_regardless_of_trailing_directory_separators_in_argument() + { + $this->assertEquals(Hyde::vendorPath('\\/file.php/'), Hyde::vendorPath().'/file.php'); + + $this->assertEquals(Hyde::vendorPath('directory/file.php'), Hyde::vendorPath().'/directory/file.php'); + $this->assertEquals(Hyde::vendorPath('directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + $this->assertEquals(Hyde::vendorPath('/directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + $this->assertEquals(Hyde::vendorPath('\\/directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + + $this->assertEquals(Hyde::vendorPath('\\/directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + $this->assertEquals(Hyde::vendorPath('/directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + $this->assertEquals(Hyde::vendorPath('\\/directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + + $this->assertEquals(Hyde::vendorPath('\\/directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + $this->assertEquals(Hyde::vendorPath('/directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + $this->assertEquals(Hyde::vendorPath('\\/directory/file.php/'), Hyde::vendorPath().'/directory/file.php'); + } +} diff --git a/packages/framework/tests/Unit/InteractsWithDirectoriesConcernTest.php b/packages/framework/tests/Unit/InteractsWithDirectoriesConcernTest.php new file mode 100644 index 00000000000..25182009d5d --- /dev/null +++ b/packages/framework/tests/Unit/InteractsWithDirectoriesConcernTest.php @@ -0,0 +1,64 @@ +needsDirectory(Hyde::path('foo')); + $this->assertDirectoryExists(Hyde::path('foo')); + } + + public function test_needs_directory_creates_the_directory_recursively() + { + $this->needsDirectory(Hyde::path('foo/bar/baz')); + $this->assertDirectoryExists(Hyde::path('foo/bar/baz')); + } + + public function test_needs_directory_handles_existing_directory() + { + $this->needsDirectory(Hyde::path('foo')); + $this->needsDirectory(Hyde::path('foo')); + $this->assertDirectoryExists(Hyde::path('foo')); + } + + public function test_needs_directories_creates_single_directory() + { + $this->needsDirectories([Hyde::path('foo')]); + $this->assertDirectoryExists(Hyde::path('foo')); + } + + public function test_needs_directories_creates_multiple_directories() + { + $this->needsDirectories([Hyde::path('foo'), Hyde::path('bar')]); + $this->assertDirectoryExists(Hyde::path('foo')); + $this->assertDirectoryExists(Hyde::path('bar')); + } +} diff --git a/packages/framework/tests/Unit/MarkdownConverterTest.php b/packages/framework/tests/Unit/MarkdownConverterTest.php new file mode 100644 index 00000000000..22fcb6cce46 --- /dev/null +++ b/packages/framework/tests/Unit/MarkdownConverterTest.php @@ -0,0 +1,24 @@ +assertIsString($html); + $this->assertEquals("

Hello World!

\n", $html); + } +} diff --git a/packages/framework/tests/Unit/MarkdownPostHelpersTest.php b/packages/framework/tests/Unit/MarkdownPostHelpersTest.php new file mode 100644 index 00000000000..f8de62b12af --- /dev/null +++ b/packages/framework/tests/Unit/MarkdownPostHelpersTest.php @@ -0,0 +1,44 @@ +assertEquals('posts/foo-bar', $post->getCurrentPagePath()); + } + + public function test_get_canonical_link_returns_canonical_uri_path_for_post_slug() + { + config(['hyde.site_url' => 'https://example.com']); + $post = new MarkdownPost([], '', '', 'foo-bar'); + $this->assertEquals('https://example.com/posts/foo-bar.html', $post->getCanonicalLink()); + } + + public function test_get_canonical_link_returns_pretty_url_when_enabled() + { + config(['hyde.site_url' => 'https://example.com', 'hyde.pretty_urls' => true]); + $post = new MarkdownPost([], '', '', 'foo-bar'); + $this->assertEquals('https://example.com/posts/foo-bar', $post->getCanonicalLink()); + } + + public function test_get_post_description_returns_post_description_when_set_in_front_matter() + { + $post = new MarkdownPost(['description' => 'This is a post description'], '', '', 'foo-bar'); + $this->assertEquals('This is a post description', $post->getPostDescription()); + } + + public function test_get_post_description_returns_truncated_post_body_when_no_description_set_in_front_matter() + { + $post = new MarkdownPost([], 'This is a post body', '', 'foo-bar'); + $this->assertEquals('This is a post body...', $post->getPostDescription()); + } +} diff --git a/packages/framework/tests/Unit/MarkdownPostParserTest.php b/packages/framework/tests/Unit/MarkdownPostParserTest.php new file mode 100644 index 00000000000..122e20ec4d4 --- /dev/null +++ b/packages/framework/tests/Unit/MarkdownPostParserTest.php @@ -0,0 +1,49 @@ +get(); + $this->assertInstanceOf(MarkdownPost::class, $post); + $this->assertCount(4, ($post->matter)); + $this->assertIsArray($post->matter); + $this->assertIsString($post->body); + $this->assertIsString($post->slug); + $this->assertTrue(strlen($post->body) > 32); + $this->assertTrue(strlen($post->slug) > 8); + } + + public function test_parsed_markdown_post_contains_valid_front_matter() + { + $post = (new MarkdownPostParser('test-post'))->get(); + $this->assertEquals('My New Post', $post->matter['title']); + $this->assertEquals('Mr. Hyde', $post->matter['author']); + $this->assertEquals('blog', $post->matter['category']); + $this->assertEquals('test-post', $post->matter['slug']); + } +} diff --git a/packages/framework/tests/Unit/PageModelGetHelperTest.php b/packages/framework/tests/Unit/PageModelGetHelperTest.php new file mode 100644 index 00000000000..1cf090ebc4e --- /dev/null +++ b/packages/framework/tests/Unit/PageModelGetHelperTest.php @@ -0,0 +1,61 @@ +assertCount(2, $collection); + $this->assertInstanceOf(Collection::class, $collection); + $this->assertContainsOnlyInstancesOf(BladePage::class, $collection); + } + + public function test_markdown_page_get_helper_returns_markdown_page_collection() + { + touch(Hyde::path('_pages/test-page.md')); + + $collection = MarkdownPage::all(); + $this->assertCount(1, $collection); + $this->assertInstanceOf(Collection::class, $collection); + $this->assertContainsOnlyInstancesOf(MarkdownPage::class, $collection); + + unlink(Hyde::path('_pages/test-page.md')); + } + + public function test_markdown_post_get_helper_returns_markdown_post_collection() + { + touch(Hyde::path('_posts/test-post.md')); + + $collection = MarkdownPost::all(); + $this->assertCount(1, $collection); + $this->assertInstanceOf(Collection::class, $collection); + $this->assertContainsOnlyInstancesOf(MarkdownPost::class, $collection); + + unlink(Hyde::path('_posts/test-post.md')); + } + + public function test_documentation_page_get_helper_returns_documentation_page_collection() + { + touch(Hyde::path('_docs/test-page.md')); + + $collection = DocumentationPage::all(); + $this->assertCount(1, $collection); + $this->assertInstanceOf(Collection::class, $collection); + $this->assertContainsOnlyInstancesOf(DocumentationPage::class, $collection); + + unlink(Hyde::path('_docs/test-page.md')); + } +} diff --git a/packages/framework/tests/Unit/SourceFilesInCustomDirectoriesCanBeCompiledTest.php b/packages/framework/tests/Unit/SourceFilesInCustomDirectoriesCanBeCompiledTest.php new file mode 100644 index 00000000000..77c0b7da8cd --- /dev/null +++ b/packages/framework/tests/Unit/SourceFilesInCustomDirectoriesCanBeCompiledTest.php @@ -0,0 +1,115 @@ +get(), + true + ); + + $this->assertFileExists(Hyde::path('_site/posts/test.html')); + unlink(Hyde::path('_site/posts/test.html')); + } + + public function test_markdown_pages_in_changed_directory_can_be_compiled() + { + mkdir(Hyde::path('testSourceDir/pages')); + touch(Hyde::path('testSourceDir/pages/test.md')); + + MarkdownPage::$sourceDirectory = 'testSourceDir/pages'; + + // Uses the same logic as the BuildActionRunner for an accurate test. + new StaticPageBuilder( + DiscoveryService::getParserInstanceForModel( + MarkdownPage::class, + 'test' + )->get(), + true + ); + + $this->assertFileExists(Hyde::path('_site/test.html')); + unlink(Hyde::path('_site/test.html')); + } + + public function test_documentation_pages_in_changed_directory_can_be_compiled() + { + mkdir(Hyde::path('testSourceDir/documentation')); + touch(Hyde::path('testSourceDir/documentation/test.md')); + + DocumentationPage::$sourceDirectory = 'testSourceDir/documentation'; + + // Uses the same logic as the BuildActionRunner for an accurate test. + new StaticPageBuilder( + DiscoveryService::getParserInstanceForModel( + DocumentationPage::class, + 'test' + )->get(), + true + ); + + $this->assertFileExists(Hyde::path('_site/docs/test.html')); + unlink(Hyde::path('_site/docs/test.html')); + } + + public function test_blade_pages_in_changed_directory_can_be_compiled() + { + mkdir(Hyde::path('testSourceDir/blade')); + touch(Hyde::path('testSourceDir/blade/test.blade.php')); + + BladePage::$sourceDirectory = 'testSourceDir/blade'; + Config::set('view.paths', ['testSourceDir/blade']); + + // Uses the same logic as the BuildActionRunner for an accurate test. + new StaticPageBuilder( + DiscoveryService::getParserInstanceForModel( + BladePage::class, + 'test' + )->get(), + true + ); + + $this->assertFileExists(Hyde::path('_site/test.html')); + unlink(Hyde::path('_site/test.html')); + } +} diff --git a/packages/framework/tests/Unit/TestBuildStaticSiteCommandFlagToEnablePrettyUrlsTest.php b/packages/framework/tests/Unit/TestBuildStaticSiteCommandFlagToEnablePrettyUrlsTest.php new file mode 100644 index 00000000000..14f625d1315 --- /dev/null +++ b/packages/framework/tests/Unit/TestBuildStaticSiteCommandFlagToEnablePrettyUrlsTest.php @@ -0,0 +1,27 @@ + false]); + + $this->artisan('build --pretty-urls') + ->expectsOutput('Generating site with pretty URLs') + ->assertExitCode(0); + + $this->assertTrue(config('hyde.pretty_urls', false)); + } + + public function test_config_change_is_not_persisted() + { + $this->assertFalse(config('hyde.pretty_urls', false)); + } +} diff --git a/packages/framework/tests/Unit/ValidatesExistenceTest.php b/packages/framework/tests/Unit/ValidatesExistenceTest.php new file mode 100644 index 00000000000..563f0822963 --- /dev/null +++ b/packages/framework/tests/Unit/ValidatesExistenceTest.php @@ -0,0 +1,38 @@ +validateExistence(BladePage::class, 'index'); + + $this->assertTrue(true); + } + + public function test_validate_existence_throws_file_not_found_exception_if_file_does_not_exist() + { + $this->expectException(FileNotFoundException::class); + + $class = new class + { + use ValidatesExistence; + }; + + $class->validateExistence(BladePage::class, 'not-found'); + } +} diff --git a/packages/framework/tests/Unit/Views/ArticleExcerptViewTest.php b/packages/framework/tests/Unit/Views/ArticleExcerptViewTest.php new file mode 100644 index 00000000000..fac33c0785b --- /dev/null +++ b/packages/framework/tests/Unit/Views/ArticleExcerptViewTest.php @@ -0,0 +1,75 @@ + $post]); + } + + public function test_component_can_be_rendered() + { + $view = $this->renderTestView(new MarkdownPost([], '')); + $this->assertStringContainsString('http://schema.org/Article', $view); + } + + public function test_component_renders_post_data() + { + $view = $this->renderTestView(new MarkdownPost([ + 'title' => 'Test Post', + 'date' => '2022-01-01 12:00:00', + 'author' => 'John Doe', + 'description' => 'This is a test post.', + ], '# Foo Bar', 'Foo Bar', 'foo-bar')); + + $this->assertStringContainsString('Test Post', $view); + $this->assertStringContainsString('Jan 1st, 2022', $view); + $this->assertStringContainsString('John Doe', $view); + $this->assertStringContainsString('This is a test post.', $view); + $this->assertStringContainsString('Read post', $view); + } + + public function test_component_renders_post_with_author_object() + { + $view = $this->renderTestView(new MarkdownPost([ + 'author' => [ + 'name' => 'John Doe', + 'url' => '#', + ], + ], '')); + + $this->assertStringContainsString('John Doe', $view); + } + + public function test_there_is_no_comma_after_date_string_when_there_is_no_author() + { + $view = $this->renderTestView(new MarkdownPost([ + 'date' => '2022-01-01', + ], '')); + + $this->assertStringContainsString('Jan 1st, 2022', $view); + $this->assertStringNotContainsString('Jan 1st, 2022,', $view); + } + + public function test_there_is_a_comma_after_date_string_when_there_is_a_author() + { + $view = $this->renderTestView(new MarkdownPost([ + 'date' => '2022-01-01', + 'author' => 'John Doe', + ], '')); + + $this->assertStringContainsString('Jan 1st, 2022,', $view); + } +}