🆙 Add cms i using 🆙

This commit is contained in:
Remco
2025-11-25 22:42:56 +01:00
parent 94704e0925
commit d44196149e
35591 changed files with 3601123 additions and 0 deletions
@@ -0,0 +1,40 @@
<?php
$finder = Symfony\Component\Finder\Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->name('*.php')
->notName('*.blade.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return (new PhpCsFixer\Config())
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'not_operator_with_successor_space' => true,
'trailing_comma_in_multiline' => true,
'phpdoc_scalar' => true,
'unary_operator_spaces' => true,
'binary_operator_spaces' => true,
'blank_line_before_statement' => [
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
],
'phpdoc_single_line_var_spacing' => true,
'phpdoc_var_without_name' => true,
'class_attributes_separation' => [
'elements' => [
'method' => 'one',
],
],
'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline',
'keep_multiple_spaces_after_comma' => true,
],
'single_trait_insert_per_statement' => true,
])
->setFinder($finder);
@@ -0,0 +1,157 @@
# Changelog
All notable changes to `shiki-php` will be documented in this file.
## 2.3.1 - 2025-02-18
### What's Changed
* fix: restore blade grammar file by @heywhy in https://github.com/spatie/shiki-php/pull/31
**Full Changelog**: https://github.com/spatie/shiki-php/compare/2.3.0...2.3.1
## 2.3.0 - 2025-02-10
### What's Changed
* feat: upgrade shiki to v2 by @heywhy in https://github.com/spatie/shiki-php/pull/29
**Full Changelog**: https://github.com/spatie/shiki-php/compare/2.2.2...2.3.0
## 2.2.2 - 2024-11-06
### What's Changed
* fix: support string and object for `token.htmlStyle` by @Barbapapazes in https://github.com/spatie/shiki-php/pull/28
**Full Changelog**: https://github.com/spatie/shiki-php/compare/2.2.1...2.2.2
## 2.2.1 - 2024-11-05
### What's Changed
* fix: create shiki with dual themes by @Barbapapazes in https://github.com/spatie/shiki-php/pull/27
**Full Changelog**: https://github.com/spatie/shiki-php/compare/2.2.0...2.2.1
## 2.2.0 - 2024-11-05
### What's Changed
* feat: support (at least) dual themes by @Barbapapazes in https://github.com/spatie/shiki-php/pull/26
### New Contributors
* @Barbapapazes made their first contribution in https://github.com/spatie/shiki-php/pull/26
**Full Changelog**: https://github.com/spatie/shiki-php/compare/2.1.0...2.2.0
## 2.1.0 - 2024-09-29
### What's Changed
* Allow running in vanilla PHP projects by @timacdonald in https://github.com/spatie/shiki-php/pull/25
* Re-add `getAvailableLanguages`, `getAvailableThemes`, `languageIsAvailable` and `themeIsAvailable` by @s3ththompson in https://github.com/spatie/shiki-php/pull/23
### New Contributors
* @timacdonald made their first contribution in https://github.com/spatie/shiki-php/pull/25
* @s3ththompson made their first contribution in https://github.com/spatie/shiki-php/pull/23
**Full Changelog**: https://github.com/spatie/shiki-php/compare/2.0.0...2.1.0
## 2.0.0 - 2024-02-19
### Upgrading
- The `getAvailableLanguages`, `getAvailableThemes`, `languageIsAvailable` and `themeIsAvailable` methods have been removed. These are no longer necessary as Shiki 1.0 lazy loads these when necessary.
#### What's Changed
* feat: upgrade support to shiki v1 by @heywhy in https://github.com/spatie/shiki-php/pull/21
* Close code block by @mxsgx in https://github.com/spatie/shiki-php/pull/15
* Update README.md by @joshbruce in https://github.com/spatie/shiki-php/pull/17
* Fix badges by @erikn69 in https://github.com/spatie/shiki-php/pull/18
* feat: support tj/n by @axelrindle in https://github.com/spatie/shiki-php/pull/20
#### New Contributors
* @mxsgx made their first contribution in https://github.com/spatie/shiki-php/pull/15
* @joshbruce made their first contribution in https://github.com/spatie/shiki-php/pull/17
* @erikn69 made their first contribution in https://github.com/spatie/shiki-php/pull/18
* @axelrindle made their first contribution in https://github.com/spatie/shiki-php/pull/20
* @heywhy made their first contribution in https://github.com/spatie/shiki-php/pull/21
**Full Changelog**: https://github.com/spatie/shiki-php/compare/1.3.0...2.0.0
## 1.3.0 - 2022-06-01
### What's Changed
- Allow php7.4 by @jonassiewertsen in https://github.com/spatie/shiki-php/pull/14
### New Contributors
- @jonassiewertsen made their first contribution in https://github.com/spatie/shiki-php/pull/14
**Full Changelog**: https://github.com/spatie/shiki-php/compare/1.2.0...1.3.0
## 1.2.0 - 2022-02-24
## What's Changed
- Add information to README.md about using NVM by @Drewdan in https://github.com/spatie/shiki-php/pull/10
- Update antlers.tmLanguage.json by @JohnathonKoster in https://github.com/spatie/shiki-php/pull/13
- Allow for user customizable renderer scripts by @mallardduck in https://github.com/spatie/shiki-php/pull/9
## New Contributors
- @Drewdan made their first contribution in https://github.com/spatie/shiki-php/pull/10
- @JohnathonKoster made their first contribution in https://github.com/spatie/shiki-php/pull/13
- @mallardduck made their first contribution in https://github.com/spatie/shiki-php/pull/9
**Full Changelog**: https://github.com/spatie/shiki-php/compare/1.1.7...1.2.0
## 1.1.7 - 2021-07-22
- fix Node path
## 1.1.6 - 2021-07-22
- add common locations to look for node
## 1.1.5 - 2021-07-12
- Update Antlers syntax highlighting
## 1.1.4 - 2021-07-12
- Fix compatibility on lower versions of Node
## 1.1.3 - 2021-07-11
- Fix an issue where embedded languages weren't being highlighted correctly
## 1.1.2 - 2021-07-11
- Fix an issue where focus was applied to the Shiki div when not needed
## 1.1.1 - 2021-07-10
- Fix an issue when passing lines to be highlighted
## 1.1.0 - 2021-07-10
- You can now mark lines as `highlighted`, `added`, `deleted` or `focus`
## 1.0.2 - 2021-07-09
- significantly improve speed
## 1.0.1 - 2021-07-09
- use default executable name
## 1.0.0 - 2021-07-09
- initial release
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) spatie <info@spatie.be>
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.
@@ -0,0 +1,243 @@
# Code highlighting with Shiki in PHP
[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/shiki-php.svg?style=flat-square)](https://packagist.org/packages/spatie/shiki-php)
[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/spatie/shiki-php/run-tests.yml?branch=main&label=Tests)](https://github.com/spatie/shiki-php/actions?query=workflow%3ATests+branch%3Amaster)
[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/spatie/shiki-php/php-cs-fixer.yml?branch=main&label=Code%20Style)](https://github.com/spatie/shiki-php/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amaster)
[![Total Downloads](https://img.shields.io/packagist/dt/spatie/shiki-php.svg?style=flat-square)](https://packagist.org/packages/spatie/shiki-php)
[Shiki](https://github.com/shikijs/shiki) is a beautiful syntax highlighter powered by the same language engine that many code editors use. This package allows you to use Shiki from PHP.
```php
\Spatie\ShikiPhp\Shiki::highlight(
code: '<?php echo "Hello World"; ?>',
language: 'php',
theme: 'github-light',
);
```
This package also ships with the following extra languages, on top of the [100+ that Shiki supports](https://github.com/shikijs/shiki/tree/master/docs/languages.md) out of the box:
- Antlers
- Blade
## Usage in Laravel and league/commonmark
Laravel users can easily use Shiki via our [spatie/laravel-markdown](https://github.com/spatie/laravel-markdown) package.
If you need a league/commonmark extension to highlight code, head over to [spatie/commonmark-shiki-highlighter](https://github.com/spatie/commonmark-shiki-highlighter).
## Support us
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/shiki-php.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/shiki-php)
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
## Installation
You can install the package via composer:
```bash
composer require spatie/shiki-php
```
In your project, you must have the JavaScript package [`shiki`](https://github.com/shikijs/shiki) installed, otherwise the `<pre>` element will not be present in the output.
You can install it via npm
```bash
npm install shiki
```
... or Yarn.
```bash
yarn add shiki
```
Make sure you have installed Node 10 or higher.
## Usage
Here's an example where we are going to highlight some PHP code.
```php
use Spatie\ShikiPhp\Shiki;
Shiki::highlight(
code: '<?php echo "Hello World"; ?>',
language: 'php',
theme: 'github-light',
);
```
The output is this chunk of HTML which will render beautifully in the browser:
```php
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">&lt;?</span><span style="color: #D8DEE9FF">php </span><span style="color: #81A1C1">echo</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Hello World</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">?&gt;</span></span></code></pre>
```
## Marking lines as highlighted, added, deleted or focus
Shiki-php allows you to mark certain lines as `highlighted`, `added`, `deleted` and `focus`. To do this, you can pass in the necessary lines to the `highlight` method:
```php
use Spatie\ShikiPhp\Shiki;
// Highlighting lines 1 and 4,5,6
Shiki::highlight(
code: $code,
language: 'php',
highlightLines: [1, '4-6'],
);
// Marking line 1 as added
Shiki::highlight(
code: $code,
language: 'php',
addLines: [1],
);
// Marking line 1 as deleted
Shiki::highlight(
code: $code,
language: 'php',
deleteLines: [1],
);
// Marking line 1 as focus
Shiki::highlight(
code: $code,
language: 'php',
focusLines: [1],
);
```
You can then target these classes in your own CSS to color these lines how you want.
## PHP 7.4 support
Shiki has a nice and easy syntax in combination with at least PHP 8.
It does support PHP 7.4, but does loose a little bit of it's nice syntax if using it with PHP7.4, as you need to follow the order of the variables.
```php
// As reference
highlight(
string $code,
?string $language = 'php',
?string $theme = 'nord',
?array $highlightLines = [],
?array $addLines = [],
?array $deleteLines = [],
?array $focusLines = []
)
// Instead of PHP 8 syntax
Shiki::highlight(
code: $code,
language: 'php',
deleteLines: [1],
);
// You need to follow PHP 7.4 syntax
Shiki::highlight(
$code,
'php',
null,
null,
[1],
);
```
## Determining available languages
To get an array with [all languages that Shiki supports](https://github.com/shikijs/shiki/blob/master/docs/languages.md), call `getAvailableLanguages`
```php
$shiki = new \Spatie\ShikiPhp\Shiki();
$shiki->getAvailableLanguages(); // returns an array
$shiki->languageIsAvailable('php'); // returns true
$shiki->languageIsAvailable('non-existing-language'); // returns false
```
## Determining available themes
To get an array with [all themes that Shiki supports](https://github.com/shikijs/shiki/blob/master/docs/themes.md), call `getAvailableThemes`
```php
$shiki = new \Spatie\ShikiPhp\Shiki();
$shiki->getAvailableThemes(); // returns an array
$shiki->themeIsAvailable('github-light'); // returns true
$shiki->themeIsAvailable('non-existing-theme'); // returns false
```
### Using a custom theme
Shiki [supports](https://github.com/shikijs/shiki/blob/master/docs/themes.md) any [VSCode themes](https://code.visualstudio.com/docs/getstarted/themes).
You can load a theme simply by passing an absolute path as the theme parameter.
```php
use Spatie\ShikiPhp\Shiki;
Shiki::highlight(
code: '<?php echo "Hello World"; ?>',
language: 'php',
theme: __DIR__ . '/your-path-to/themes/some-theme.json',
);
```
## Using Node Version Manager
Under the hood, this package runs a node command to render the markdown. If you use NVM during development,
then the package will be unlikely to find your version of node as it looks for the node executable in
`/usr/local/bin` and `/opt/homebrew/bin` - if this is the case, then you should create a symlink between
the node distributable in your NVM folder, to that of the `usr/local/bin`. Such a command might look like this:
```bash
sudo ln -s /home/some-user/.nvm/versions/node/v17.3.1/bin/node /usr/local/bin/node
```
Creating this symlink will allow the package to find your NPM executable. Please note, if you change
your NPM version you will have to update your symlinks accordingly.
## Testing
You can run all the tests with this command:
```bash
composer test
```
## Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
## Contributing
Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
## Security Vulnerabilities
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
## Credits
- [Rias Van der Veken](https://github.com/riasvdv)
- [Freek Van der Herten](https://github.com/freekmurze)
- [All Contributors](../../contributors)
The Blade syntax highlighting source is taken from [this repo](https://github.com/onecentlin/laravel-blade-snippets-vscode/blob/master/syntaxes/blade.tmLanguage.json).
The Antlers syntax highlighting source is taken from [this repo](https://github.com/Stillat/vscode-antlers-language-server/blob/main/client/syntaxes/antlers.json).
## Alternatives
If you don't want to install and handle Shiki yourself, take a look at [Torchlight](https://torchlight.dev), which can highlight your code with minimal setup.
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
@@ -0,0 +1,166 @@
const FontStyle = {
NotSet: -1,
None: 0,
Italic: 1,
Bold: 2,
Underline: 4,
};
const FONT_STYLE_TO_CSS = {
[FontStyle.Italic]: "font-style: italic",
[FontStyle.Bold]: "font-weight: bold",
[FontStyle.Underline]: "text-decoration: underline",
};
const renderToHtml = function (lines, options = {}) {
const theme = options.theme;
const themes = options.themes;
const highlightedLines = makeHighlightSet(options.highlightLines);
const addLines = makeHighlightSet(options.addLines);
const deleteLines = makeHighlightSet(options.deleteLines);
const focusLines = makeHighlightSet(options.focusLines);
let className = "shiki";
if (highlightedLines.size) {
className += " highlighted";
}
if (addLines.size) {
className += " added";
}
if (deleteLines.size) {
className += " deleted";
}
if (focusLines.size) {
className += " focus";
}
let html = "";
if (theme) {
html += `<pre class="${className}" style="background-color: ${theme.theme.bg}">`;
} else if (themes) {
const backgroundStyles = Object.entries(themes).map(
([theme, theme$]) => {
if (theme === "light") {
return `background-color:${theme$.theme.bg};`;
}
return `--shiki-${theme}-bg:${theme$.theme.bg};`;
}
);
const foregroundStyles = Object.entries(themes).map(
([theme, theme$]) => {
if (theme === "light") {
return `color:${theme$.theme.fg};`;
}
return `--shiki-${theme}:${theme$.theme.fg};`;
}
);
const classes = `${className} shiki-themes ${Object.values(themes)
.map((theme) => theme.theme.name)
.join(" ")}`;
html += `<pre class="${classes}" style="${backgroundStyles.join(
""
)}${foregroundStyles.join("")}">`;
}
if (options.langId) {
html += `<div class="language-id">${options.langId}</div>`;
}
html += `<code>`;
lines.forEach((l, index) => {
const lineNumber = index + 1;
let lineClass = "line";
if (highlightedLines.has(lineNumber)) {
lineClass += " highlight";
}
if (addLines.has(lineNumber)) {
lineClass += " add";
}
if (deleteLines.has(lineNumber)) {
lineClass += " del";
}
if (focusLines.has(lineNumber)) {
lineClass += " focus";
}
html += `<span class="${lineClass.trim()}">`;
l.forEach((token) => {
const cssDeclarations = [];
if (theme) {
cssDeclarations.push(`color:${token.color || theme.theme.fg}`);
} else if (themes) {
/**
* The `htmlStyle` property can be a `string` or an `object`. The `string` representation is deprecated.
* @see https://github.com/search?q=repo%3Ashikijs%2Fshiki+htmlStyle&type=code
*/
if (typeof token.htmlStyle === "string") {
cssDeclarations.push(token.htmlStyle);
} else if (typeof token.htmlStyle === "object") {
for (const [key, value] of Object.entries(
token.htmlStyle
)) {
cssDeclarations.push(`${key}:${value}`);
}
}
}
if (token.fontStyle > FontStyle.None) {
cssDeclarations.push(FONT_STYLE_TO_CSS[token.fontStyle]);
}
html += `<span style="${cssDeclarations.join(";")}">${escapeHtml(
token.content
)}</span>`;
});
html += `</span>\n`;
});
html = html.replace(/\n*$/, ""); // Get rid of final new lines
html += `</code></pre>`;
return html;
};
const makeHighlightSet = function (highlightLines) {
const lines = new Set();
if (!highlightLines) {
return lines;
}
for (let lineSpec of highlightLines) {
if (lineSpec.toString().includes("-")) {
const [begin, end] = lineSpec
.split("-")
.map((lineNo) => Number(lineNo));
for (let line = begin; line <= end; line++) {
lines.add(line);
}
} else if (lineSpec.toString().trim()) {
lines.add(Number(lineSpec));
}
}
return lines;
};
const htmlEscapes = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#39;",
};
function escapeHtml(html) {
return html.replace(/[&<>"']/g, (chr) => htmlEscapes[chr]);
}
exports.renderToHtml = renderToHtml;
@@ -0,0 +1,146 @@
const fs = require("fs");
const path = require("path");
const renderer = require("./renderer");
const args = JSON.parse(process.argv.slice(2));
const customLanguages = {
antlers: {
scopeName: "text.html.statamic",
embeddedLangs: ["html"],
},
};
async function main(args) {
const shiki = await import("shiki");
const highlighter = await shiki.createHighlighter({});
for (const [lang, spec] of Object.entries(customLanguages)) {
for (const embedded of spec.embeddedLangs) {
await highlighter.loadLanguage(embedded);
}
await highlighter.loadLanguage({
...spec,
...loadLanguage(lang),
name: lang,
});
}
const language = args[1] || "php";
/**
* If only one theme is provided, the variable `theme` will be a string. The variable `themes` will be null.
*
* If multiple themes are provided, the variable `themes` will be an array and the variable `theme` will be null.
*/
let theme = args[2] || "nord";
let themes = null;
if (typeof args[2] === "object") {
theme = null;
themes = args[2];
}
if (theme) {
if (fs.existsSync(theme)) {
theme = loadLocalTheme(theme);
} else {
await highlighter.loadTheme(theme);
}
} else if (themes) {
for (const theme of Object.values(themes)) {
if (fs.existsSync(theme)) {
themes[theme] = loadLocalTheme(theme);
} else {
await highlighter.loadTheme(theme);
}
}
}
if (!customLanguages[language]) await highlighter.loadLanguage(language);
if (args[0] === "languages") {
process.stdout.write(
JSON.stringify([
...Object.keys(shiki.bundledLanguagesBase),
...Object.keys(customLanguages),
])
);
return;
}
if (args[0] === "aliases") {
process.stdout.write(
JSON.stringify([
...Object.keys(shiki.bundledLanguages),
...Object.keys(customLanguages),
])
);
return;
}
if (args[0] === "themes") {
process.stdout.write(JSON.stringify(Object.keys(shiki.bundledThemes)));
return;
}
const codeToTokensOptions = {
lang: language,
};
if (theme) {
codeToTokensOptions.theme = theme;
} else if (themes) {
codeToTokensOptions.themes = themes;
}
const result = highlighter.codeToTokens(args[0], codeToTokensOptions);
const options = args[3] || {};
const renderToHtmlOptions = {
highlightLines: options.highlightLines,
addLines: options.addLines,
deleteLines: options.deleteLines,
focusLines: options.focusLines,
};
if (theme) {
renderToHtmlOptions.theme = highlighter.setTheme(theme);
} else if (themes) {
const themes$ = {};
for (const [theme, theme$] of Object.entries(themes)) {
themes$[theme] = highlighter.setTheme(theme$);
}
renderToHtmlOptions.themes = themes$;
}
const rendered = renderer.renderToHtml(result.tokens, renderToHtmlOptions);
process.stdout.write(rendered);
}
main(args);
function loadLanguage(language) {
const path = getLanguagePath(language);
const content = fs.readFileSync(path);
return JSON.parse(content);
}
function getLanguagePath(language) {
const url = path.join(
__dirname,
"..",
"languages",
`${language}.tmLanguage.json`
);
return path.normalize(url);
}
function loadLocalTheme(theme) {
return JSON.parse(fs.readFileSync(theme, "utf-8"));
}
@@ -0,0 +1,57 @@
{
"name": "spatie/shiki-php",
"description": "Highlight code using Shiki in PHP",
"keywords": [
"spatie",
"shiki"
],
"homepage": "https://github.com/spatie/shiki-php",
"license": "MIT",
"authors": [
{
"name": "Rias Van der Veken",
"email": "rias@spatie.be",
"role": "Developer"
},
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"role": "Developer"
}
],
"require": {
"php": "^8.0",
"ext-json": "*",
"symfony/process": "^5.4|^6.4|^7.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^v3.0",
"pestphp/pest": "^1.8",
"phpunit/phpunit": "^9.5",
"spatie/pest-plugin-snapshots": "^1.1",
"spatie/ray": "^1.10"
},
"autoload": {
"psr-4": {
"Spatie\\ShikiPhp\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Spatie\\ShikiPhp\\Tests\\": "tests"
}
},
"scripts": {
"test": "vendor/bin/pest",
"test-coverage": "XDEBUG_MODE=coverage vendor/bin/pest --coverage-html coverage",
"format": "vendor/bin/php-cs-fixer fix --allow-risky=yes"
},
"config": {
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
@@ -0,0 +1,520 @@
{
"name": "Antlers (Statamic Syntax)",
"fileTypes": ["antlers.html", "html", "htm", "xhtml"],
"scopeName": "text.html.statamic",
"injections": {
"text.html.statamic - (meta.embedded | meta.tag), L:((text.html.statamic meta.tag) - (meta.embedded.block.php | meta.embedded.line.php)), L:(source.js - (meta.embedded.block.php | meta.embedded.line.php)), L:(source.css - (meta.embedded.block.php | meta.embedded.line.php))": {
"patterns": [
{
"include": "#php-tag"
}
]
},
"text.html.statamic": {
"patterns": [
{
"include": "#statamic-comments"
},
{
"include": "#antlers-tags"
}
]
},
"text.html.statamic - (meta.embedded | meta.tag | comment.block.statamic), L:(text.html.statamic meta.tag - (comment.block.statamic | meta.embedded.statamic))": {
"patterns": [
{
"comment": "This is set to use XHTML standards, but you can change that by changing .strict to .basic for HTML standards",
"include": "text.html.basic"
}
]
}
},
"patterns": [
{
"include": "#php-tag"
},
{
"include": "#statamic-comments"
},
{
"include": "#frontMatter"
},
{
"include": "#antlers-tags"
}
],
"repository": {
"php-tag": {
"patterns": [
{
"begin": "<\\?(?i:php|=)?(?![^?]*\\?>)",
"beginCaptures": {
"0": {
"name": "punctuation.section.embedded.begin.statamic"
}
},
"end": "(\\?)>",
"endCaptures": {
"0": {
"name": "punctuation.section.embedded.end.statamic"
},
"1": {
"name": "source.php"
}
},
"name": "meta.embedded.block.statamic",
"contentName": "source.php",
"patterns": [
{
"include": "source.php"
}
]
},
{
"begin": "(?<!@){{([\\$\\?]\\s?)",
"beginCaptures": {
"0": {
"name": "punctuation.section.embedded.begin.statamic"
}
},
"end": "(\\s?)[\\$\\?]}}",
"endCaptures": {
"0": {
"name": "punctuation.section.embedded.end.statamic"
},
"1": {
"name": "source.php"
}
},
"name": "meta.embedded.line.statamic",
"contentName": "source.php",
"patterns": [
{
"include": "source.php"
}
]
},
{
"begin": "<\\?(?i:php|=)?",
"beginCaptures": {
"0": {
"name": "punctuation.section.embedded.begin.statamic"
}
},
"end": "(\\?)>",
"endCaptures": {
"0": {
"name": "punctuation.section.embedded.end.statamic"
},
"1": {
"name": "source.php"
}
},
"name": "meta.embedded.line.statamic",
"contentName": "source.php",
"patterns": [
{
"include": "source.php"
}
]
}
]
},
"frontMatter": {
"begin": "\\A-{3}\\s*$",
"contentName": "meta.embedded.block.frontmatter",
"patterns": [
{
"include": "source.yaml"
}
],
"end": "(^|\\G)-{3}|\\.{3}\\s*$"
},
"statamic-comments": {
"begin": "{{#",
"end": "#}}",
"name": "comment.block.statamic"
},
"antlers-tags": {
"begin": "(?<!@){{(?!\\$)(\\s?)",
"end": "(\\s?)}}",
"name": "meta.embedded.block.statamic",
"patterns": [
{
"include": "#statamic-comments"
},
{
"include": "#php-tag"
},
{
"include": "#antlers-conditionals"
},
{
"include": "#antlers-expression"
}
]
},
"antlers-conditionals": {
"match": "(?<!:)(/?else|/?elseif|/?if|/?unless|endif|endunless|unlesselse)",
"name": "keyword.control.statamic"
},
"string-double-quoted": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.statamic"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.statamic"
}
},
"name": "string.quoted.double.statamic",
"patterns": [
{
"begin": "(?<!@){(\\s?)",
"end": "(\\s?)}",
"patterns": [
{
"include": "#antlers-expression"
}
]
}
]
},
"string-single-quoted": {
"begin": "'",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.statamic"
}
},
"end": "'",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.statamic"
}
},
"name": "string.quoted.single.statamic",
"patterns": [
{
"match": "\\\\[\\\\']",
"name": "constant.character.escape.statamic"
},
{
"begin": "(?<!@){(\\s?)",
"end": "(\\s?)}",
"patterns": [
{
"include": "#antlers-expression"
}
]
}
]
},
"antlers-strings": {
"patterns": [
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
}
]
},
"antlers-expression": {
"patterns": [
{
"match": ";",
"name": "punctuation.terminator.expression.statamic"
},
{
"include": "#antlers-strings"
},
{
"include": "#antlers-numbers"
},
{
"match": "=>",
"name": "keyword.operator.key.statamic"
},
{
"match": "->",
"name": "keyword.operator.class.statamic"
},
{
"include": "#statamic-explicit-tags"
},
{
"include": "#statamic-core-tags"
},
{
"match": "===|==|!==|!=|<>",
"name": "keyword.operator.comparison.statamic"
},
{
"match": "\\&=?",
"name": "keyword.operator.string.statamic"
},
{
"match": "=|\\+=|\\-=|\\*\\*?=|/=|%=|\\|=|\\^=|<<=|>>=",
"name": "keyword.operator.assignment.statamic"
},
{
"match": "(?i)(!|\\?\\?|\\?=|\\?|&&|&|\\|\\|)|\\b(and|or|xor)\\b",
"name": "keyword.operator.logical.statamic"
},
{
"match": "(?i)\\b(bwa|bwo|bxor|bnot|bsl|bsr)\\b",
"name": "keyword.operator.bitwise.statamic"
},
{
"match": "<=>|<=|>=|<|>",
"name": "keyword.operator.comparison.statamic"
},
{
"match": "\\-|\\+|\\*\\*?|/|%",
"name": "keyword.operator.arithmetic.statamic"
},
{
"begin": "(arr|list|switch)\\s*(\\()",
"beginCaptures": {
"1": {
"name": "support.function.construct.statamic"
},
"2": {
"name": "punctuation.definition.array.begin.bracket.round.statamic"
}
},
"end": "\\)|(?=\\?>)",
"endCaptures": {
"0": {
"name": "punctuation.definition.array.end.bracket.round.statamic"
}
},
"name": "meta.array.statamic",
"patterns": [
{
"include": "#antlers-expression"
}
]
},
{
"begin": "(?<!@){(\\s?)",
"end": "(\\s?)}",
"patterns": [
{
"include": "#antlers-expression"
}
]
},
{
"include": "#antlers-tag-parameter-variable"
},
{
"include": "#antlers-language-operators"
},
{
"include": "#antlers-variable"
},
{
"include": "#antlers-modifier-pipe"
},
{
"include": "#antlers-variable-modifier-name"
},
{
"include": "#antlers-variable-modifiers"
},
{
"include": "#antlers-constants"
}
]
},
"antlers-tag-parameter-variable": {
"match": "(:?([\\S:]+?)(=)('|\")([^\\4]*?)(\\4))",
"captures": {
"1": {
"patterns": [
{
"match": ":([\\S:]+?)(=)('|\")(.*?)(\\3)",
"captures": {
"1": {
"name": "entity.other.attribute-name"
},
"2": {
"name": "keyword.operator.assignment.statamic"
},
"3": {
"name": "punctuation.definition.string.begin.statamic"
},
"4": {
"patterns": [
{
"include": "#antlers-expression"
}
]
},
"5": {
"name": "punctuation.definition.string.end.statamic"
}
}
},
{
"match": "([\\S]+?)(=)(('|\")(.*?)(\\4))",
"captures": {
"1": {
"name": "entity.other.attribute-name"
},
"2": {
"name": "keyword.operator.assignment.statamic"
},
"3": {
"patterns": [
{
"include": "#string-single-quoted"
},
{
"include": "#string-double-quoted"
}
]
}
}
}
]
}
}
},
"antlers-constants": {
"match": "(\\G|\\s|\\b)(true|TRUE|false|FALSE|yes|YES|no|NO|null|as)\\s",
"captures": {
"2": {
"name": "constant.language.statamic"
}
}
},
"antlers-variable-modifier-name": {
"match": "(\\s)?(\\|)(\\s)?(\\w+((^:([a-zA-Z0-9-_/-@]+)){1,2})?|((-|\\+|\\*|/|\\^|\\%):(\\d*)?\\.?(\\d+)))+",
"captures": {
"2": {
"name": "keyword.operator.statamic"
},
"4": {
"name": "support.function.statamic"
}
}
},
"antlers-variable-modifiers": {
"match": "(\\s)?(\\|)(\\s)?(\\w+((:([a-zA-Z0-9-_/-@]+)){1,2})?|((-|\\+|\\*|/|\\^|\\%):(\\d*)?\\.?(\\d+)))+",
"captures": {
"2": {
"name": "keyword.operator.statamic"
},
"4": {
"name": "support.function.statamic",
"patterns": [
{
"include": "#antlers-expression"
}
]
}
}
},
"statamic-core-tags": {
"match": "\\G(/?\\w+)+",
"captures": {
"1": {
"name": "variable.statamic",
"patterns": [
{
"include": "#core-tag-names"
}
]
}
}
},
"statamic-explicit-tags": {
"match": "\\G(/?%\\w+)+",
"captures": {
"1": {
"name": "entity.name.tag.statamic"
}
}
},
"statamic-core-closing-tags": {
"match": "\\/(\\w+)+",
"captures": {
"1": {
"name": "variable.statamic",
"patterns": [
{
"include": "#core-tag-names"
}
]
}
}
},
"antlers-language-operators": {
"match": "(\\w+)[\\s]",
"captures": {
"1": {
"name": "variable.statamic",
"patterns": [
{
"include": "#antlers-numbers"
},
{
"include": "#language-operators"
}
]
}
}
},
"antlers-variable": {
"match": "(/?\\w+)(:)?(\\w+)?",
"captures": {
"1": {
"name": "variable.statamic",
"patterns": []
},
"2": {
"name": "keyword.operator.statamic"
},
"3": {
"name": "variable.statamic",
"patterns": [
{
"include": "$self"
}
]
}
}
},
"antlers-numbers": {
"match": "0|[1-9](?:_?[0-9]+)*",
"name": "constant.numeric.statamic"
},
"antlers-modifier-pipe": {
"match": "(\\|)",
"name": "keyword.operator.other.statamic"
},
"core-tag-names": {
"patterns": [
{
"match": "(?i)\\b(taxonomy|collection|asset|form|assets|cache|can|dd|ddd|dump|get_content|get_error|get_errors|get_files|glide|in|increment|installed|is|iterate|foreach|link|locales|markdown|member|mix|nav|not_found|404|obfuscate|parent|partial|path|query|range|loop|redirect|relate|rotate|route|scope|section|session|set|structure|svg|theme|trans|trans_choice|user|users|widont|yields|yield|slot|once|noparse|view)\\b",
"name": "entity.name.tag.statamic"
}
]
},
"language-operators": {
"patterns": [
{
"match": "(?i)\\b(pluck|take|skip|arr|orderby|groupby|merge|where|switch|bwa|bwo|bxor|bnot|bsl|bsr|if|elseif|else|void)(\\b)",
"name": "support.function.array.statamic"
}
]
}
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,539 @@
{
"name": "shiki-php",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "shiki-php",
"license": "MIT",
"dependencies": {
"shiki": "^2.2.0"
}
},
"node_modules/@shikijs/core": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.2.0.tgz",
"integrity": "sha512-U+vpKdsQDWuX3fPTCkSc8XPX9dCaS+r+qEP1XhnU30yxRFo2OxHJmY2H5rO1q+v0zB5R2vobsxEFt5uPf31CGQ==",
"license": "MIT",
"dependencies": {
"@shikijs/engine-javascript": "2.2.0",
"@shikijs/engine-oniguruma": "2.2.0",
"@shikijs/types": "2.2.0",
"@shikijs/vscode-textmate": "^10.0.1",
"@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.4"
}
},
"node_modules/@shikijs/engine-javascript": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.2.0.tgz",
"integrity": "sha512-96SpZ4V3UVMtpSPR5QpmU395CNrQiRPszXK62m8gKR2HMA0653ruce7omS5eX6EyAyFSYHvBWtTuspiIsHpu4A==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "2.2.0",
"@shikijs/vscode-textmate": "^10.0.1",
"oniguruma-to-es": "^2.3.0"
}
},
"node_modules/@shikijs/engine-oniguruma": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.2.0.tgz",
"integrity": "sha512-wowCKwkvPFFMXFkiKK/a2vs5uTCc0W9+O9Xcu/oqFP6VoDFe14T8u/D+Rl4dCJJSOyeynP9mxNPJ82T5JHTNCw==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "2.2.0",
"@shikijs/vscode-textmate": "^10.0.1"
}
},
"node_modules/@shikijs/langs": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.2.0.tgz",
"integrity": "sha512-RSWLH3bnoyG6O1kZ2msh5jOkKKp8eENwyT30n62vUtXfp5cxkF/bpWPpO+p4+GAPhL2foBWR2kOerwkKG0HXlQ==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "2.2.0"
}
},
"node_modules/@shikijs/themes": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.2.0.tgz",
"integrity": "sha512-8Us9ZF2mV9kuh+4ySJ9MzrUDIpc2RIkRfKBZclkliW1z9a0PlGU2U7fCkItZZHpR5e4/ft5BzuO+GDqombC6Aw==",
"license": "MIT",
"dependencies": {
"@shikijs/types": "2.2.0"
}
},
"node_modules/@shikijs/types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.2.0.tgz",
"integrity": "sha512-wkZZKs80NtW5Jp/7ONI1j7EdXSatX2BKMS7I01wliDa09gJKHkZyVqlEMRka/mjT5Qk9WgAyitoCKgGgbsP/9g==",
"license": "MIT",
"dependencies": {
"@shikijs/vscode-textmate": "^10.0.1",
"@types/hast": "^3.0.4"
}
},
"node_modules/@shikijs/vscode-textmate": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.1.tgz",
"integrity": "sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==",
"license": "MIT"
},
"node_modules/@types/hast": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
"license": "MIT",
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/@types/mdast": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
"integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
"license": "MIT",
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/@types/unist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"license": "MIT"
},
"node_modules/@ungap/structured-clone": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"license": "ISC"
},
"node_modules/ccount": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
"integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-entities-html4": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
"integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-entities-legacy": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
"integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/comma-separated-tokens": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
"integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/devlop": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
"license": "MIT",
"dependencies": {
"dequal": "^2.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/emoji-regex-xs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
"integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
"license": "MIT"
},
"node_modules/hast-util-to-html": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.4.tgz",
"integrity": "sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
"@types/unist": "^3.0.0",
"ccount": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"hast-util-whitespace": "^3.0.0",
"html-void-elements": "^3.0.0",
"mdast-util-to-hast": "^13.0.0",
"property-information": "^6.0.0",
"space-separated-tokens": "^2.0.0",
"stringify-entities": "^4.0.0",
"zwitch": "^2.0.4"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-whitespace": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
"integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/html-void-elements": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
"integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/mdast-util-to-hast": {
"version": "13.2.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
"integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
"@types/mdast": "^4.0.0",
"@ungap/structured-clone": "^1.0.0",
"devlop": "^1.0.0",
"micromark-util-sanitize-uri": "^2.0.0",
"trim-lines": "^3.0.0",
"unist-util-position": "^5.0.0",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-util-character": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
"integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
],
"license": "MIT",
"dependencies": {
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
}
},
"node_modules/micromark-util-encode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
"integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
],
"license": "MIT"
},
"node_modules/micromark-util-sanitize-uri": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
"integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
],
"license": "MIT",
"dependencies": {
"micromark-util-character": "^2.0.0",
"micromark-util-encode": "^2.0.0",
"micromark-util-symbol": "^2.0.0"
}
},
"node_modules/micromark-util-symbol": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
"integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
],
"license": "MIT"
},
"node_modules/micromark-util-types": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz",
"integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==",
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
],
"license": "MIT"
},
"node_modules/oniguruma-to-es": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz",
"integrity": "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==",
"license": "MIT",
"dependencies": {
"emoji-regex-xs": "^1.0.0",
"regex": "^5.1.1",
"regex-recursion": "^5.1.1"
}
},
"node_modules/property-information": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
"integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/regex": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/regex/-/regex-5.1.1.tgz",
"integrity": "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==",
"license": "MIT",
"dependencies": {
"regex-utilities": "^2.3.0"
}
},
"node_modules/regex-recursion": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-5.1.1.tgz",
"integrity": "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==",
"license": "MIT",
"dependencies": {
"regex": "^5.1.1",
"regex-utilities": "^2.3.0"
}
},
"node_modules/regex-utilities": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz",
"integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
"license": "MIT"
},
"node_modules/shiki": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-2.2.0.tgz",
"integrity": "sha512-3uoZBmc+zpd2JOEeTvKP/vK5UVDDe8YiigkT9flq+MV5Z1MKFiUXfbLIvHfqcJ+V90StDiP1ckN97z1WlhC6cQ==",
"license": "MIT",
"dependencies": {
"@shikijs/core": "2.2.0",
"@shikijs/engine-javascript": "2.2.0",
"@shikijs/engine-oniguruma": "2.2.0",
"@shikijs/langs": "2.2.0",
"@shikijs/themes": "2.2.0",
"@shikijs/types": "2.2.0",
"@shikijs/vscode-textmate": "^10.0.1",
"@types/hast": "^3.0.4"
}
},
"node_modules/space-separated-tokens": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
"integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/stringify-entities": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
"integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
"license": "MIT",
"dependencies": {
"character-entities-html4": "^2.0.0",
"character-entities-legacy": "^3.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
"integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/unist-util-is": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
"integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-position": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
"integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-stringify-position": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
"integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-visit": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
"integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
"unist-util-is": "^6.0.0",
"unist-util-visit-parents": "^6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-visit-parents": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
"integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
"unist-util-is": "^6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/vfile": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
"integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
"vfile-message": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/vfile-message": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
"integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
"unist-util-stringify-position": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
}
}
}
@@ -0,0 +1,12 @@
{
"name": "shiki-php",
"main": "index.js",
"scripts": {
"build": "rollup -c"
},
"author": "Spatie",
"license": "MIT",
"dependencies": {
"shiki": "^2.2.0"
}
}
@@ -0,0 +1,129 @@
<?php
namespace Spatie\ShikiPhp;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;
class Shiki
{
/**
* @var string|array<string, string> Can be a single theme or an array with a light and a dark theme.
*/
protected mixed $defaultTheme;
private static ?string $customWorkingDirPath = null;
public static function setCustomWorkingDirPath(?string $path)
{
static::$customWorkingDirPath = $path;
}
/**
* @param string|array<string, string>|null $theme Can be a single theme or an array with a light and a dark theme.
*/
public static function highlight(
string $code,
?string $language = null,
mixed $theme = null,
?array $highlightLines = null,
?array $addLines = null,
?array $deleteLines = null,
?array $focusLines = null
): string {
$language = $language ?? 'php';
$theme = $theme ?? 'nord';
return (new static())->highlightCode($code, $language, $theme, [
'highlightLines' => $highlightLines ?? [],
'addLines' => $addLines ?? [],
'deleteLines' => $deleteLines ?? [],
'focusLines' => $focusLines ?? [],
]);
}
public function getAvailableLanguages(): array
{
$shikiResult = $this->callShiki('languages');
$languages = json_decode($shikiResult, true);
sort($languages);
return $languages;
}
/**
* @param string|array<string, string> $defaultTheme Can be a single theme or an array with a light and a dark theme.
*/
public function __construct(mixed $defaultTheme = 'nord')
{
$this->defaultTheme = $defaultTheme;
}
public function getAvailableThemes(): array
{
$shikiResult = $this->callShiki('themes');
return json_decode($shikiResult, true);
}
public function languageIsAvailable(string $language): bool
{
$shikiResult = $this->callShiki('aliases');
$aliases = json_decode($shikiResult, true);
return in_array($language, $aliases);
}
public function themeIsAvailable(string $theme): bool
{
return in_array($theme, $this->getAvailableThemes());
}
public function highlightCode(string $code, string $language, mixed $theme = null, ?array $options = []): string
{
$theme = $theme ?? $this->defaultTheme;
return $this->callShiki($code, $language, $theme, $options);
}
public function getWorkingDirPath(): string
{
if (static::$customWorkingDirPath !== null && ($path = realpath(static::$customWorkingDirPath)) !== false) {
return $path;
}
return realpath(dirname(__DIR__) . '/bin');
}
protected function callShiki(...$arguments): string
{
$home = getenv("HOME");
$command = [
(new ExecutableFinder())->find('node', 'node', [
'/usr/local/bin',
'/opt/homebrew/bin',
$home . '/n/bin', // support https://github.com/tj/n
]),
'shiki.js',
json_encode(array_values($arguments)),
];
$process = new Process(
$command,
$this->getWorkingDirPath(),
null,
);
$process->run();
if (! $process->isSuccessful()) {
throw new ProcessFailedException($process);
}
return $process->getOutput();
}
}