diff --git a/bin/shiki.js b/bin/shiki.js index abc88e6..8f382ed 100644 --- a/bin/shiki.js +++ b/bin/shiki.js @@ -1,84 +1,83 @@ -const shiki = require('shiki'); const fs = require('fs'); const path = require('path'); const renderer = require('./renderer'); +const args = JSON.parse(process.argv.slice(2)); -const arguments = JSON.parse(process.argv.slice(2)); - -const customLanguages = [ - { - id: 'antlers', +const customLanguages = { + antlers: { scopeName: 'text.html.statamic', - path: getLanguagePath('antlers'), embeddedLangs: ['html'], }, - { - id: 'blade', + blade: { scopeName: 'text.html.php.blade', - path: getLanguagePath('blade'), embeddedLangs: ['html', 'php'], }, -]; +}; -if (arguments[0] === 'themes') { - process.stdout.write(JSON.stringify(shiki.BUNDLED_THEMES)); - return; -} +async function main(args) { + const shiki = await import('shiki'); + const highlighter = await shiki.getHighlighter({}); -let allLanguages = shiki.BUNDLED_LANGUAGES; -allLanguages.push(...customLanguages); + for (const [lang, spec] of Object.entries(customLanguages)) { + for (const embedded of spec.embeddedLangs) { + await highlighter.loadLanguage(embedded); + } -if (arguments[0] === 'languages') { - process.stdout.write(JSON.stringify(allLanguages)); - return; -} + await highlighter.loadLanguage({ ...spec, ...loadLanguage(lang), name: lang }); + } + + const language = args[1] || 'php'; + let theme = args[2] || 'nord'; -const language = arguments[1] || 'php'; -let theme = arguments[2] || 'nord'; + if (fs.existsSync(theme)) { + theme = JSON.parse(fs.readFileSync(theme, 'utf-8')); + } else { + await highlighter.loadTheme(theme); + } -const languagesToLoad = allLanguages.filter(lang => lang.id === language || (lang.aliases && lang.aliases.includes(language))); + if (!customLanguages[language]) await highlighter.loadLanguage(language); -(function loadEmbeddedLangsRecursively() { - languagesToLoad.forEach(function (language) { - const embeddedLangs = language.embeddedLangs || []; - embeddedLangs.forEach(function (languageKey) { - if (languagesToLoad.find(lang => lang.id === languageKey || (lang.aliases && lang.aliases.includes(languageKey)))) { - return; - } + if (args[0] === 'languages') { + process.stdout.write(JSON.stringify(highlighter.getLoadedLanguages())); + return; + } - languagesToLoad.push(allLanguages.find(lang => lang.id === languageKey || (lang.aliases && lang.aliases.includes(languageKey)))); - loadEmbeddedLangsRecursively(); - }); + if (args[0] === 'themes') { + process.stdout.write(JSON.stringify(highlighter.getLoadedThemes())); + return; + } + + const { theme: theme$ } = highlighter.setTheme(theme) + + const result = highlighter.codeToTokens(args[0], { + theme: theme$, + lang: language, }); -})(); -if (fs.existsSync(theme)) { - theme = JSON.parse(fs.readFileSync(theme, 'utf-8')); -} + const options = args[3] || {}; -shiki.getHighlighter({ - theme, - langs: languagesToLoad, -}).then((highlighter) => { - const tokens = highlighter.codeToThemedTokens(arguments[0], language); - const theme = highlighter.getTheme(); - const options = arguments[3] || {}; - - process.stdout.write(renderer.renderToHtml(tokens, { - fg: theme.fg, - bg: theme.bg, + const rendered = renderer.renderToHtml(result.tokens, { + fg: theme$.fg, + bg: theme$.bg, highlightLines: options.highlightLines, addLines: options.addLines, deleteLines: options.deleteLines, focusLines: options.focusLines, - })); -}); + }); + + process.stdout.write(rendered); +} -function getLanguagePath(language) -{ - const pathToShikiDistDirectory = path.dirname(require.resolve('shiki')); - const pathToShikiLanguages = path.resolve(`${pathToShikiDistDirectory}/../languages`); - const relativeDirectory = path.relative(pathToShikiLanguages, `${__dirname}/../languages`); +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 `${relativeDirectory}/${language}.tmLanguage.json` + return path.normalize(url); } diff --git a/package-lock.json b/package-lock.json index 638c2ff..8bcd4e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,121 +1,27 @@ { "name": "shiki-php", - "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "shiki-php", "license": "MIT", "dependencies": { - "shiki": "^0.9.5" + "shiki": "^1.1.3" } }, - "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dependencies": { - "lru-cache": "^5.1.1" - } + "node_modules/@shikijs/core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.1.3.tgz", + "integrity": "sha512-1QWSvWcPbvZXsDxB1F7ejW+Kuxp3z/JHs944hp/f8BYOlFd5gplzseFIkE/GTu/qytFef3zNME4qw1oHbQ0j2A==" }, "node_modules/shiki": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.5.tgz", - "integrity": "sha512-XFn+rl3wIowDjzdr5DlHoHgQphXefgUTs2bNp/bZu4WF9gTrTLnKwio3f28VjiFG6Jpip7yQn/p4mMj6OrjrtQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.1.3.tgz", + "integrity": "sha512-k/B4UvtWmGcHMLp6JnQminlex3Go5MHKXEiormmzTJECAiSQiwSon6USuwTyto8EMUQc9aYRJ7HojkfVLbBk+g==", "dependencies": { - "json5": "^2.2.0", - "onigasm": "^2.2.5", - "vscode-textmate": "5.2.0" - } - }, - "node_modules/vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - }, - "dependencies": { - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "requires": { - "minimist": "^1.2.5" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" + "@shikijs/core": "1.1.3" } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "requires": { - "lru-cache": "^5.1.1" - } - }, - "shiki": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.5.tgz", - "integrity": "sha512-XFn+rl3wIowDjzdr5DlHoHgQphXefgUTs2bNp/bZu4WF9gTrTLnKwio3f28VjiFG6Jpip7yQn/p4mMj6OrjrtQ==", - "requires": { - "json5": "^2.2.0", - "onigasm": "^2.2.5", - "vscode-textmate": "5.2.0" - } - }, - "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } } diff --git a/package.json b/package.json index ba29c1a..7e2607b 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,6 @@ "author": "Spatie", "license": "MIT", "dependencies": { - "shiki": "^0.9.5" + "shiki": "^1.1.3" } } diff --git a/src/Shiki.php b/src/Shiki.php index 09c7edc..dbda1d7 100644 --- a/src/Shiki.php +++ b/src/Shiki.php @@ -41,12 +41,7 @@ public function getAvailableLanguages(): array { $shikiResult = $this->callShiki('languages'); - $languageProperties = json_decode($shikiResult, true); - - $languages = array_map( - fn ($properties) => $properties['id'], - $languageProperties - ); + $languages = json_decode($shikiResult, true); sort($languages); @@ -112,7 +107,7 @@ protected function callShiki(...$arguments): string $process->run(); - if (! $process->isSuccessful()) { + if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } diff --git a/tests/testfiles/alt-bin/shiki.js b/tests/testfiles/alt-bin/shiki.js index b19bbea..c2ff3c1 100644 --- a/tests/testfiles/alt-bin/shiki.js +++ b/tests/testfiles/alt-bin/shiki.js @@ -1,84 +1,83 @@ -const shiki = require('shiki'); const fs = require('fs'); const path = require('path'); const renderer = require('./renderer'); +const args = JSON.parse(process.argv.slice(2)); -const arguments = JSON.parse(process.argv.slice(2)); - -const customLanguages = [ - { - id: 'antlers', +const customLanguages = { + antlers: { scopeName: 'text.html.statamic', - path: getLanguagePath('antlers'), embeddedLangs: ['html'], }, - { - id: 'blade', + blade: { scopeName: 'text.html.php.blade', - path: getLanguagePath('blade'), embeddedLangs: ['html', 'php'], }, -]; +}; -if (arguments[0] === 'themes') { - process.stdout.write(JSON.stringify(shiki.BUNDLED_THEMES)); - return; -} +async function main(args) { + const shiki = await import('shiki'); + const highlighter = await shiki.getHighlighter({}); -let allLanguages = shiki.BUNDLED_LANGUAGES; -allLanguages.push(...customLanguages); + for (const [lang, spec] of Object.entries(customLanguages)) { + for (const embedded of spec.embeddedLangs) { + await highlighter.loadLanguage(embedded); + } -if (arguments[0] === 'languages') { - process.stdout.write(JSON.stringify(allLanguages)); - return; -} + await highlighter.loadLanguage({ ...spec, ...loadLanguage(lang), name: lang }); + } + + const language = args[1] || 'php'; + let theme = args[2] || 'nord'; -const language = arguments[1] || 'php'; -let theme = arguments[2] || 'nord'; + if (fs.existsSync(theme)) { + theme = JSON.parse(fs.readFileSync(theme, 'utf-8')); + } else { + await highlighter.loadTheme(theme); + } -const languagesToLoad = allLanguages.filter(lang => lang.id === language || (lang.aliases && lang.aliases.includes(language))); + if (!customLanguages[language]) await highlighter.loadLanguage(language); -(function loadEmbeddedLangsRecursively() { - languagesToLoad.forEach(function (language) { - const embeddedLangs = language.embeddedLangs || []; - embeddedLangs.forEach(function (languageKey) { - if (languagesToLoad.find(lang => lang.id === languageKey || (lang.aliases && lang.aliases.includes(languageKey)))) { - return; - } + if (args[0] === 'languages') { + process.stdout.write(JSON.stringify(highlighter.getLoadedLanguages())); + return; + } - languagesToLoad.push(allLanguages.find(lang => lang.id === languageKey || (lang.aliases && lang.aliases.includes(languageKey)))); - loadEmbeddedLangsRecursively(); - }); + if (args[0] === 'themes') { + process.stdout.write(JSON.stringify(highlighter.getLoadedThemes())); + return; + } + + const { theme: theme$ } = highlighter.setTheme(theme) + + const result = highlighter.codeToTokens(args[0], { + theme: theme$, + lang: language, }); -})(); -if (fs.existsSync(theme)) { - theme = JSON.parse(fs.readFileSync(theme, 'utf-8')); -} + const options = args[3] || {}; -shiki.getHighlighter({ - theme, - langs: languagesToLoad, -}).then((highlighter) => { - const tokens = highlighter.codeToThemedTokens(arguments[0], language); - const theme = highlighter.getTheme(); - const options = arguments[3] || {}; - - process.stdout.write(renderer.renderToHtml(tokens, { - fg: theme.fg, - bg: theme.bg, + const rendered = renderer.renderToHtml(result.tokens, { + fg: theme$.fg, + bg: theme$.bg, highlightLines: options.highlightLines, addLines: options.addLines, deleteLines: options.deleteLines, focusLines: options.focusLines, - })); -}); + }); + + process.stdout.write(rendered); +} -function getLanguagePath(language) -{ - const pathToShikiDistDirectory = path.dirname(require.resolve('shiki')); - const pathToShikiLanguages = path.resolve(`${pathToShikiDistDirectory}/../languages`); - const relativeDirectory = path.relative(pathToShikiLanguages, `${__dirname}/../../../languages`); +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 `${relativeDirectory}/${language}.tmLanguage.json` + return path.normalize(url); }