├── CNAME ├── highlightjs-glimmer ├── .gitignore ├── .eslintignore ├── .prettierignore ├── .eslintrc.cjs ├── scripts │ ├── cdn-bootstrap.js │ ├── build.cjs │ └── hljs-test.sh ├── index.d.ts ├── .prettierrc.cjs ├── babel.config.cjs ├── package.json ├── src │ ├── index.js │ ├── glimmer-javascript.js │ └── glimmer.js └── CHANGELOG.md ├── tests-cjs ├── .prettierignore ├── .eslintrc.cjs ├── vitest.config.ts ├── .prettierrc.cjs ├── -utils.js ├── unit │ └── node.test.cjs ├── integration │ ├── markdown-it.test.cjs │ └── remark.test.cjs └── package.json ├── tests-esm ├── .prettierignore ├── integration │ ├── remark.js │ ├── markdown-it.test.js │ └── rehype.test.js ├── .eslintrc.cjs ├── vitest.config.ts ├── .prettierrc.cjs ├── package.json ├── unit │ ├── index.test.js │ ├── __snapshots__ │ │ └── injections.test.js.snap │ └── injections.test.js └── -utils.js ├── .prettierignore ├── .github ├── renovate.json5 └── workflows │ ├── push-dist.yml │ ├── release.yml │ └── ci.yml ├── pnpm-workspace.yaml ├── demo ├── DejaVuSansMono.ttf ├── .eslintrc.cjs ├── .prettierrc.cjs ├── package.json └── samples.js ├── .eslintignore ├── .prettierrc.cjs ├── .changeset ├── config.json └── README.md ├── .codesandbox └── tasks.json ├── .eslintrc.cjs ├── LICENSE ├── turbo.json ├── package.json ├── .gitignore ├── README.md └── index.html /CNAME: -------------------------------------------------------------------------------- 1 | hljs-glimmer.nullvoxpopuli.com -------------------------------------------------------------------------------- /highlightjs-glimmer/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /tests-cjs/.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | __snapshots__ 3 | -------------------------------------------------------------------------------- /tests-esm/.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | __snapshots__ 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # All folders 2 | */ 3 | .github/ 4 | .changeset/ 5 | *.md 6 | *.json5 7 | *.yaml 8 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>NullVoxPopuli/renovate:npm.json5" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /highlightjs-glimmer/.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | vendor/ 3 | coverage/ 4 | node_modules/ 5 | .git/ 6 | .github/ 7 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - './highlightjs-glimmer' 3 | - './tests-esm' 4 | - './tests-cjs' 5 | -------------------------------------------------------------------------------- /demo/DejaVuSansMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NullVoxPopuli/highlightjs-glimmer/HEAD/demo/DejaVuSansMono.ttf -------------------------------------------------------------------------------- /highlightjs-glimmer/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | vendor/ 3 | coverage/ 4 | node_modules/ 5 | .git/ 6 | .github/ 7 | *.md 8 | -------------------------------------------------------------------------------- /tests-esm/integration/remark.js: -------------------------------------------------------------------------------- 1 | // Remark-highlight.js is not supported by the authors 2 | // https://github.com/remarkjs/remark-highlight.js 3 | -------------------------------------------------------------------------------- /demo/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { configs } = require('@nullvoxpopuli/eslint-configs'); 4 | 5 | module.exports = configs.crossPlatform(); 6 | -------------------------------------------------------------------------------- /tests-cjs/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { configs } = require('@nullvoxpopuli/eslint-configs'); 4 | 5 | module.exports = configs.node(); 6 | -------------------------------------------------------------------------------- /tests-esm/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { configs } = require('@nullvoxpopuli/eslint-configs'); 4 | 5 | module.exports = configs.node(); 6 | -------------------------------------------------------------------------------- /highlightjs-glimmer/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { configs } = require('@nullvoxpopuli/eslint-configs'); 4 | 5 | module.exports = configs.crossPlatform(); 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | demo/ 3 | highlight 4 | coverage/ 5 | node_modules/ 6 | .git/ 7 | .github/ 8 | 9 | # All folders 10 | */ 11 | .github/ 12 | .changeset/ 13 | -------------------------------------------------------------------------------- /highlightjs-glimmer/scripts/cdn-bootstrap.js: -------------------------------------------------------------------------------- 1 | /* global hljs */ 2 | // requires hljs to be defined globally 3 | 4 | import glimmer from '../src/glimmer'; 5 | 6 | hljs.registerLanguage('glimmer', glimmer); 7 | -------------------------------------------------------------------------------- /tests-esm/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ['**/*.test.js'], 6 | }, 7 | esbuild: { 8 | format: 'esm', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /tests-cjs/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | include: ['**/*.test.cjs'], 7 | }, 8 | esbuild: { 9 | format: 'cjs', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /highlightjs-glimmer/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { HLJSApi, LanguageFn } from 'highlight.js'; 2 | 3 | export function setup(hljs: HLJSApi): void; 4 | export function registerLanguage(hljs: HLJSApi): void; 5 | export function registerInjections(hljs: HLJSApi): void; 6 | export function glimmer(hljs: HLJSApi): ReturnType; 7 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | printWidth: 100, 6 | overrides: [ 7 | { 8 | files: ['**/*.hbs'], 9 | options: { 10 | singleQuote: false, 11 | }, 12 | }, 13 | { 14 | files: ['**/*.gjs', '**/*.gts'], 15 | plugins: ['prettier-plugin-ember-template-tag'], 16 | }, 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /demo/.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | printWidth: 100, 6 | overrides: [ 7 | { 8 | files: ['**/*.hbs'], 9 | options: { 10 | singleQuote: false, 11 | }, 12 | }, 13 | { 14 | files: ['**/*.gjs', '**/*.gts'], 15 | plugins: ['prettier-plugin-ember-template-tag'], 16 | }, 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /tests-cjs/.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | printWidth: 100, 6 | overrides: [ 7 | { 8 | files: ['**/*.hbs'], 9 | options: { 10 | singleQuote: false, 11 | }, 12 | }, 13 | { 14 | files: ['**/*.gjs', '**/*.gts'], 15 | plugins: ['prettier-plugin-ember-template-tag'], 16 | }, 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /tests-esm/.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | printWidth: 100, 6 | overrides: [ 7 | { 8 | files: ['**/*.hbs'], 9 | options: { 10 | singleQuote: false, 11 | }, 12 | }, 13 | { 14 | files: ['**/*.gjs', '**/*.gts'], 15 | plugins: ['prettier-plugin-ember-template-tag'], 16 | }, 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /highlightjs-glimmer/.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | printWidth: 100, 6 | overrides: [ 7 | { 8 | files: ['**/*.hbs'], 9 | options: { 10 | singleQuote: false, 11 | }, 12 | }, 13 | { 14 | files: ['**/*.gjs', '**/*.gts'], 15 | plugins: ['prettier-plugin-ember-template-tag'], 16 | }, 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /highlightjs-glimmer/babel.config.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * used only for jest, because apparently we can't have 5 | * nice things when testing node 6 | */ 7 | module.exports = { 8 | env: { 9 | test: { 10 | plugins: [ 11 | [ 12 | require('@babel/plugin-transform-modules-commonjs'), 13 | { 14 | importInterop: 'babel', 15 | }, 16 | ], 17 | ], 18 | }, 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "NullVoxPopuli/highlightjs-glimmer" } 6 | ], 7 | "commit": false, 8 | "fixed": [], 9 | "linked": [], 10 | "access": "public", 11 | "baseBranch": "main", 12 | "updateInternalDependencies": "patch", 13 | "ignore": ["hljs-glimmer-tests-cjs", "hljs-glimmer-tests-esm"] 14 | } 15 | -------------------------------------------------------------------------------- /tests-cjs/-utils.js: -------------------------------------------------------------------------------- 1 | const hljs = require('highlight.js'); 2 | const { registerLanguage } = require('highlightjs-glimmer'); 3 | 4 | registerLanguage(hljs); 5 | 6 | function parse(code, lang = 'glimmer') { 7 | return hljs.highlight(code, { language: lang }).value; 8 | } 9 | 10 | function tag(klass, children = []) { 11 | let _children = Array.isArray(children) ? children : [children]; 12 | 13 | return `${_children.join('')}`; 14 | } 15 | 16 | module.exports = { parse, tag }; 17 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.github/workflows/push-dist.yml: -------------------------------------------------------------------------------- 1 | name: Push dist 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | 9 | jobs: 10 | push-dist: 11 | name: Push dist 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v6 15 | - uses: wyvox/action-setup-pnpm@v3 16 | - uses: kategengler/put-built-npm-package-contents-on-branch@v2.1.0 17 | with: 18 | branch: dist 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | working-directory: highlightjs-glimmer 21 | -------------------------------------------------------------------------------- /tests-cjs/unit/node.test.cjs: -------------------------------------------------------------------------------- 1 | /* globals describe, it, expect */ 2 | 'use strict'; 3 | 4 | const hljs = require('highlight.js'); 5 | 6 | const { glimmer } = require(`highlightjs-glimmer`); 7 | 8 | hljs.registerLanguage('glimmer', glimmer); 9 | 10 | function parse(code) { 11 | return hljs.highlight(code, { language: 'glimmer' }).value; 12 | } 13 | 14 | describe('NodeJS // require', () => { 15 | it('works', () => { 16 | expect(parse('')).toEqual( 17 | `<A />` 18 | ); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "lint:fix": "concurrently 'npm:lint:*:fix' --names 'fix:'", 8 | "lint:js": "eslint .", 9 | "lint:js:fix": "eslint . --fix", 10 | "lint:prettier": "prettier --check .", 11 | "lint:prettier:fix": "prettier --write ." 12 | }, 13 | "devDependencies": { 14 | "@nullvoxpopuli/eslint-configs": "^3.2.2", 15 | "@typescript-eslint/eslint-plugin": "^5.62.0", 16 | "@typescript-eslint/parser": "^5.62.0", 17 | "concurrently": "^8.2.2", 18 | "eslint": "^8.55.0", 19 | "prettier": "^2.8.8", 20 | "typescript": "^5.3.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/changesets/action 2 | name: Release 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | 9 | concurrency: ${{ github.workflow }}-${{ github.ref }} 10 | 11 | jobs: 12 | release: 13 | name: Release 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | packages: write 18 | pull-requests: write 19 | steps: 20 | - uses: wyvox/action@v1 21 | with: 22 | repo-token: ${{ secrets.GITHUB_TOKEN }} 23 | - run: pnpm build 24 | - name: Create Release Pull Request 25 | uses: changesets/action@v1 26 | with: 27 | publish: pnpm release 28 | title: "Release Preview" 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | -------------------------------------------------------------------------------- /.codesandbox/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // These tasks will run in order when initializing your CodeSandbox project. 3 | "setupTasks": [ 4 | { 5 | "name": "Install Dependencies", 6 | "command": "pnpm install" 7 | } 8 | ], 9 | 10 | // These tasks can be run from CodeSandbox. Running one will open a log in the app. 11 | "tasks": { 12 | "build": { 13 | "name": "build", 14 | "command": "pnpm build", 15 | "runAtStart": true 16 | }, 17 | "lint": { 18 | "name": "lint", 19 | "command": "pnpm lint", 20 | "runAtStart": true 21 | }, 22 | "test:modules": { 23 | "name": "ESM Tests", 24 | "command": "pnpm --filter 'hljs-glimmer-tests-esm' test", 25 | "runAtStart": false 26 | }, 27 | "test:cjs": { 28 | "name": "CJS Tests", 29 | "command": "pnpm --filter 'hljs-glimmer-tests-cjs' test", 30 | "runAtStart": false 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests-esm/integration/markdown-it.test.js: -------------------------------------------------------------------------------- 1 | import { stripIndent } from 'common-tags'; 2 | import hljs from 'highlight.js'; 3 | import { setup } from 'highlightjs-glimmer'; 4 | import markdownIt from 'markdown-it'; 5 | import { describe, expect, it } from 'vitest'; 6 | 7 | import { tag } from '../-utils.js'; 8 | 9 | setup(hljs); 10 | 11 | const md = markdownIt({ 12 | html: true, 13 | highlight: (str, lang) => { 14 | return hljs.highlight(str, { language: lang }).value; 15 | }, 16 | }); 17 | 18 | describe('markdownIt', () => { 19 | it('works', async () => { 20 | expect( 21 | md.render(stripIndent` 22 | \`\`\`glimmer 23 | {{@arg}} 24 | \`\`\` 25 | `) 26 | ).toEqual( 27 | '
  ' +
28 |         tag('punctuation mustache', ['{{', tag('punctuation', '@'), tag('params', 'arg'), '}}']) +
29 |         '\n' +
30 |         '
\n' 31 | ); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /tests-cjs/integration/markdown-it.test.cjs: -------------------------------------------------------------------------------- 1 | /* globals describe, it, expect */ 2 | const { stripIndent } = require('common-tags'); 3 | const markdownIt = require('markdown-it'); 4 | 5 | const hljs = require('highlight.js'); 6 | 7 | const { setup } = require('highlightjs-glimmer'); 8 | 9 | const { tag } = require('../-utils'); 10 | 11 | setup(hljs); 12 | 13 | const md = markdownIt({ 14 | html: true, 15 | highlight: (str, lang) => { 16 | return hljs.highlight(str, { language: lang }).value; 17 | }, 18 | }); 19 | 20 | describe('markdownIt', () => { 21 | it('works', async () => { 22 | expect( 23 | md.render(stripIndent` 24 | \`\`\`glimmer 25 | {{@arg}} 26 | \`\`\` 27 | `) 28 | ).toEqual( 29 | '
  ' +
30 |         tag('punctuation mustache', ['{{', tag('punctuation', '@'), tag('params', 'arg'), '}}']) +
31 |         '\n' +
32 |         '
\n' 33 | ); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | // .eslintrc.js 2 | 'use strict'; 3 | 4 | const { configBuilder } = require('@nullvoxpopuli/eslint-configs/configs/node'); 5 | const { forFiles } = require('@nullvoxpopuli/eslint-configs/configs/-utils'); 6 | 7 | const config = configBuilder(); 8 | const modulesBase = config.modules.js; 9 | 10 | module.exports = { 11 | overrides: [ 12 | // pull this in to eslint-configs? 13 | { 14 | files: ['*.html'], 15 | plugins: [modulesBase.plugins, 'html'].flat(), 16 | parser: '@babel/eslint-parser', 17 | parserOptions: { 18 | requireConfigFile: false, 19 | sourceType: 'module', 20 | ecmaVersion: 2021, 21 | }, 22 | env: { 23 | browser: true, 24 | node: false, 25 | }, 26 | extends: ['eslint:recommended', 'prettier'], 27 | rules: { 28 | ...modulesBase.rules, 29 | 'no-console': 'off', 30 | }, 31 | }, 32 | forFiles('*.cjs', config.commonjs.js), 33 | ], 34 | }; 35 | -------------------------------------------------------------------------------- /tests-cjs/integration/remark.test.cjs: -------------------------------------------------------------------------------- 1 | /* globals describe, it, expect */ 2 | 'use strict'; 3 | 4 | const { stripIndent } = require('common-tags'); 5 | const unified = require('unified'); 6 | const markdown = require('remark-parse'); 7 | const html = require('remark-html'); 8 | const highlight = require('remark-highlight.js'); 9 | const hljs = require('highlight.js'); 10 | 11 | const { setup } = require('highlightjs-glimmer'); 12 | 13 | const { tag } = require('../-utils'); 14 | 15 | setup(hljs); 16 | 17 | function parse(text) { 18 | return unified().use(markdown).use(highlight).use(html).processSync(text).toString(); 19 | } 20 | 21 | describe('Remark', () => { 22 | it.skip('works', async () => { 23 | expect( 24 | parse(stripIndent` 25 | \`\`\`glimmer 26 | {{@arg}} 27 | \`\`\` 28 | `) 29 | ).toEqual( 30 | '
' +
31 |         tag('punctuation mustache', ['{{', tag('punctuation', '@'), tag('params', 'arg'), '}}']) +
32 |         '
' 33 | ); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 NullVoxPopuli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /highlightjs-glimmer/scripts/build.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const esbuild = require('esbuild'); 5 | 6 | /** 7 | * CDN / script-only auto-registration 8 | */ 9 | esbuild.buildSync({ 10 | entryPoints: [path.join(__dirname, 'cdn-bootstrap.js')], 11 | bundle: true, 12 | minify: true, 13 | format: 'iife', 14 | sourcemap: false, 15 | target: ['chrome58', 'firefox57', 'safari11', 'edge16'], 16 | outfile: path.join(__dirname, '../dist/glimmer.min.js'), 17 | }); 18 | 19 | /** 20 | * CDN / import 21 | */ 22 | esbuild.buildSync({ 23 | entryPoints: [path.join(__dirname, '../src/index.js')], 24 | bundle: true, 25 | minify: true, 26 | format: 'esm', 27 | sourcemap: false, 28 | target: ['chrome58', 'firefox57', 'safari11', 'edge16'], 29 | outfile: path.join(__dirname, '../dist/glimmer.esm.min.js'), 30 | }); 31 | 32 | /** 33 | * Node / require 34 | */ 35 | esbuild.buildSync({ 36 | entryPoints: [path.join(__dirname, '../src/index.js')], 37 | bundle: true, 38 | minify: false, 39 | format: 'cjs', 40 | sourcemap: false, 41 | target: ['node14'], 42 | outfile: path.join(__dirname, '../dist/glimmer.cjs.cjs'), 43 | }); 44 | -------------------------------------------------------------------------------- /highlightjs-glimmer/scripts/hljs-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | glimmer=$PWD 4 | tmp_dir=$(mktemp -d -t ci-XXXXXXXXXX) 5 | hljs="$tmp_dir/highlight.js" 6 | 7 | function cleanup() { 8 | set +x 9 | 10 | echo "" 11 | echo "You can inspect the cloned hljs repo at" 12 | echo " $hljs" 13 | echo "" 14 | } 15 | 16 | trap cleanup EXIT 17 | set -eax 18 | 19 | cd $tmp_dir 20 | git clone https://github.com/highlightjs/highlight.js.git 21 | cd $hljs 22 | 23 | set +x 24 | 25 | export NODE_OPTIONS="--unhandled-rejections=strict" 26 | 27 | echo "" 28 | echo " Node: $(node --version)" 29 | echo " PWD: $PWD" 30 | echo "" 31 | 32 | set -x 33 | 34 | npm install 35 | mkdir -p $hljs/extra/glimmer/src/languages/ 36 | # index.js is the "main", but we plan on exporting some helper functions 37 | # since rollup has a weird way of representing default exports when 38 | # named exports are also present, we have to move the file with the default 39 | # export to the "main" location. 40 | # so, this is not a real representation of the highlightjs-glimmer package 41 | cp $glimmer/src/glimmer.js $hljs/extra/glimmer/src/languages/glimmer.js 42 | 43 | node ./tools/build.js -t node 44 | 45 | npm run test 46 | 47 | 48 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "pipeline": { 3 | ///////////////////////////////////////////////// 4 | ///////////////////////////////////////////////// 5 | // 6 | // C.I. / C.D. 7 | // 8 | ///////////////////////////////////////////////// 9 | ///////////////////////////////////////////////// 10 | "build": { 11 | "outputs": ["dist/**"], 12 | "dependsOn": ["^build"] 13 | }, 14 | "test": { 15 | "outputs": [], 16 | "dependsOn": ["^build"] 17 | }, 18 | 19 | ///////////////////////////////////////////////// 20 | ///////////////////////////////////////////////// 21 | // 22 | // Quality Checks 23 | // 24 | ///////////////////////////////////////////////// 25 | ///////////////////////////////////////////////// 26 | "lint": { 27 | "outputs": [], 28 | "dependsOn": ["lint:js", "lint:hbs", "lint:prettier", "lint:types"] 29 | }, 30 | "lint:js": { 31 | "outputs": [] 32 | }, 33 | "lint:hbs": { 34 | "outputs": [] 35 | }, 36 | "lint:prettier": { 37 | "outputs": [] 38 | }, 39 | "lint:types": { 40 | "outputs": [], 41 | "dependsOn": ["^build"] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests-cjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hljs-glimmer-tests-cjs", 3 | "private": true, 4 | "type": "commonjs", 5 | "scripts": { 6 | "test": "vitest run --coverage", 7 | "lint:fix": "concurrently 'npm:lint:*:fix' --names 'fix:'", 8 | "lint:js": "eslint .", 9 | "lint:js:fix": "eslint . --fix", 10 | "lint:prettier": "prettier --check .", 11 | "lint:prettier:fix": "prettier --write ." 12 | }, 13 | "dependencies": { 14 | "highlightjs-glimmer": "workspace:../highlightjs-glimmer" 15 | }, 16 | "devDependencies": { 17 | "@nullvoxpopuli/eslint-configs": "^3.2.2", 18 | "@vitest/coverage-c8": "^0.31.4", 19 | "common-tags": "^1.8.2", 20 | "concurrently": "^8.2.2", 21 | "eslint": "^8.55.0", 22 | "highlight.js": "^11.10.0", 23 | "markdown-it": "^14.0.0", 24 | "prettier": "^2.8.8", 25 | "rehype-highlight": "^6.0.0", 26 | "rehype-stringify": "^9.0.4", 27 | "remark-highlight.js": "^6.0.0", 28 | "remark-html": "^13.0.2", 29 | "remark-parse": "^9.0.0", 30 | "remark-rehype": "^9.1.0", 31 | "typescript": "^5.3.3", 32 | "unified": "^8.4.2", 33 | "vitest": "^0.31.4" 34 | }, 35 | "engines": { 36 | "node": ">= 18.0.0" 37 | }, 38 | "volta": { 39 | "extends": "../package.json" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests-esm/integration/rehype.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { stripIndent } from 'common-tags'; 4 | import { externalSetup } from 'highlightjs-glimmer'; 5 | import { rehype } from 'rehype'; 6 | import highlight from 'rehype-highlight'; 7 | import html from 'rehype-stringify'; 8 | import markdown from 'remark-parse'; 9 | import remark2rehype from 'remark-rehype'; 10 | import { describe, expect, it } from 'vitest'; 11 | 12 | import { tag } from '../-utils.js'; 13 | 14 | function parse(text) { 15 | return rehype() 16 | .data('settings', { fragment: true }) 17 | .use(markdown) 18 | .use(remark2rehype) 19 | .use(highlight, { 20 | aliases: { hbs: 'glimmer', handlebars: 'glimmer' }, 21 | languages: { 22 | // js: require('highlight.js/lib/languages/javascript'), 23 | glimmer: externalSetup, 24 | }, 25 | }) 26 | .use(html) 27 | .processSync(text) 28 | .toString(); 29 | } 30 | 31 | describe('Rehype', () => { 32 | it('works', async () => { 33 | expect( 34 | parse(stripIndent` 35 | \`\`\`glimmer 36 | {{@arg}} 37 | \`\`\` 38 | `) 39 | ).toEqual( 40 | '
  ' +
41 |         tag('punctuation mustache', ['{{', tag('punctuation', '@'), tag('params', 'arg'), '}}']) +
42 |         '\n' +
43 |         '
' 44 | ); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "version": "0.0.0", 4 | "private": true, 5 | "author": "NullVoxPopuli", 6 | "license": "MIT", 7 | "files": [], 8 | "scripts": { 9 | "release": "changeset publish", 10 | "prepare": "pnpm build", 11 | "build": "turbo build", 12 | "lint": "turbo lint", 13 | "test": "turbo test", 14 | "debug": "npx html-pages . --no-cache", 15 | "lint:fix": "concurrently 'npm:lint:*:fix' --names 'fix:' && pnpm --filter '*' lint:fix", 16 | "lint:js": "eslint .", 17 | "lint:js:fix": "eslint . --fix", 18 | "lint:prettier": "prettier --check .", 19 | "lint:prettier:fix": "prettier --write ." 20 | }, 21 | "devDependencies": { 22 | "@babel/eslint-parser": "^7.23.3", 23 | "@changesets/changelog-github": "^0.5.0", 24 | "@changesets/cli": "^2.27.1", 25 | "@nullvoxpopuli/eslint-configs": "^3.2.2", 26 | "concurrently": "^8.2.2", 27 | "eslint": "^8.55.0", 28 | "eslint-config-prettier": "^8.10.0", 29 | "eslint-plugin-html": "^7.1.0", 30 | "prettier": "^2.8.8", 31 | "turbo": "^1.11.2" 32 | }, 33 | "pnpm": { 34 | "peerDependencyRules": { 35 | "ignoreMissing": [ 36 | "ember-cli-htmlbars" 37 | ] 38 | } 39 | }, 40 | "engines": { 41 | "node": ">= 18" 42 | }, 43 | "volta": { 44 | "node": "24.11.1", 45 | "yarn": "1.22.22", 46 | "npm": "11.6.4" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hljs-glimmer-tests-esm", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "test": "vitest run --coverage", 7 | "test:dev": "vitest watch --coverage --ui --open=false", 8 | "lint:fix": "concurrently 'npm:lint:*:fix' --names 'fix:'", 9 | "lint:js": "eslint .", 10 | "lint:js:fix": "eslint . --fix", 11 | "lint:prettier": "prettier --check .", 12 | "lint:prettier:fix": "prettier --write ." 13 | }, 14 | "dependencies": { 15 | "highlightjs-glimmer": "workspace:../highlightjs-glimmer" 16 | }, 17 | "devDependencies": { 18 | "@nullvoxpopuli/eslint-configs": "^3.2.2", 19 | "@typescript-eslint/eslint-plugin": "^5.62.0", 20 | "@typescript-eslint/parser": "^5.62.0", 21 | "@vitest/coverage-c8": "^0.31.4", 22 | "@vitest/ui": "^0.31.4", 23 | "common-tags": "^1.8.2", 24 | "concurrently": "^8.2.2", 25 | "eslint": "^8.55.0", 26 | "highlight.js": ">= 11.10.0", 27 | "markdown-it": "^14.0.0", 28 | "prettier": "^2.8.8", 29 | "rehype": "^12.0.1", 30 | "rehype-highlight": "^6.0.0", 31 | "rehype-stringify": "^9.0.4", 32 | "remark-html": "^15.0.2", 33 | "remark-parse": "^10.0.2", 34 | "remark-rehype": "^10.1.0", 35 | "typescript": "^5.3.3", 36 | "unified": "^10.1.2", 37 | "vitest": "^0.31.4" 38 | }, 39 | "engines": { 40 | "node": ">= 18.0.0" 41 | }, 42 | "volta": { 43 | "extends": "../package.json" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .turbo/ 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | coverage/ 11 | 12 | # Diagnostic reports (https://nodejs.org/api/report.html) 13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Microbundle cache 60 | .rpt2_cache/ 61 | .rts2_cache_cjs/ 62 | .rts2_cache_es/ 63 | .rts2_cache_umd/ 64 | 65 | # Optional REPL history 66 | .node_repl_history 67 | 68 | # Output of 'npm pack' 69 | *.tgz 70 | 71 | # Yarn Integrity file 72 | .yarn-integrity 73 | 74 | # dotenv environment variables file 75 | .env 76 | .env.test 77 | -------------------------------------------------------------------------------- /tests-esm/unit/index.test.js: -------------------------------------------------------------------------------- 1 | import { stripIndent } from 'common-tags'; 2 | import { describe, expect, test } from 'vitest'; 3 | 4 | import { parse, tag } from '../-utils.js'; 5 | 6 | describe('Component Invocation', () => { 7 | test('argument', () => { 8 | expect(parse('{{@arg}}')).toEqual( 9 | tag('punctuation mustache', ['{{', tag('punctuation', '@'), tag('params', 'arg'), '}}']) 10 | ); 11 | }); 12 | 13 | test('self-closed', () => { 14 | expect(parse('')).toEqual( 15 | `${tag('tag', ['<', tag('title', 'Component'), '::', tag('title', 'Name'), ' />'])}` 16 | ); 17 | }); 18 | 19 | test('block, no args', () => { 20 | expect( 21 | parse(stripIndent` 22 | 23 | body 24 | 25 | `) 26 | ).toEqual( 27 | stripIndent` 28 | <Component> 29 | body 30 | </Component> 31 | ` 32 | ); 33 | }); 34 | }); 35 | 36 | describe('Expressions', () => { 37 | describe('Mustache', () => { 38 | test('this.property', () => { 39 | let result = parse('{{this.property}}'); 40 | 41 | expect(result).toEqual( 42 | stripIndent` 43 | {{this.property}} 44 | ` 45 | ); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /highlightjs-glimmer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "highlightjs-glimmer", 3 | "version": "2.2.2", 4 | "description": "Glimmer syntax highlighting with Highlight.JS", 5 | "main": "dist/glimmer.cjs.cjs", 6 | "browser": "dist/glimmer.esm.min.js", 7 | "type": "module", 8 | "repository": "git@github.com:NullVoxPopuli/highlightjs-glimmer.git", 9 | "author": "NullVoxPopuli", 10 | "license": "MIT", 11 | "exports": { 12 | ".": { 13 | "import": "./dist/glimmer.esm.min.js", 14 | "require": "./dist/glimmer.cjs.cjs", 15 | "types": "./index.d.ts" 16 | } 17 | }, 18 | "scripts": { 19 | "release": "changeset publish", 20 | "prepare": "node ./scripts/build.cjs", 21 | "build": "node ./scripts/build.cjs", 22 | "lint:fix": "concurrently 'npm:lint:*:fix' --names 'fix:'", 23 | "lint:js": "eslint .", 24 | "lint:js:fix": "eslint . --fix", 25 | "lint:prettier": "prettier --check .", 26 | "lint:prettier:fix": "prettier --write ." 27 | }, 28 | "engines": { 29 | "node": "^14 || ^16 || ^18 || >= 20" 30 | }, 31 | "devDependencies": { 32 | "@babel/core": "^7.23.5", 33 | "@babel/node": "^7.22.19", 34 | "@babel/plugin-transform-modules-commonjs": "^7.23.3", 35 | "@babel/preset-env": "^7.23.5", 36 | "@nullvoxpopuli/eslint-configs": "^3.2.2", 37 | "common-tags": "^1.8.2", 38 | "concurrently": "^8.2.2", 39 | "esbuild": "^0.19.9", 40 | "eslint": "^8.55.0", 41 | "highlight.js": ">= 11.9.0", 42 | "prettier": "^2.8.8", 43 | "typescript": "^5.3.3" 44 | }, 45 | "peerDependencies": { 46 | "highlight.js": ">= 11.0.0" 47 | }, 48 | "volta": { 49 | "extends": "../package.json" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /highlightjs-glimmer/src/index.js: -------------------------------------------------------------------------------- 1 | import _glimmer from './glimmer.js'; 2 | import _glimmerJavascript from './glimmer-javascript.js'; 3 | 4 | export const glimmer = _glimmer; 5 | export const glimmerJavascript = _glimmerJavascript; 6 | 7 | /** 8 | * The convenience method for configuring everything related to 9 | * glimmer highlighting. This wraps `registerLanguage` and `registerInjections`. 10 | * For most use cases, this should be the only method you need. 11 | */ 12 | export function setup(hljs) { 13 | registerLanguage(hljs); 14 | registerInjections(hljs); 15 | } 16 | 17 | export function externalSetup(hljs) { 18 | let grammar = _glimmer(hljs); 19 | 20 | registerInjections(hljs); 21 | 22 | return grammar; 23 | } 24 | 25 | /** 26 | * Convenience method for registering the glimmer template syntax with 27 | * highlight.js under the name "glimmer" 28 | */ 29 | export function registerLanguage(hljs) { 30 | return hljs.registerLanguage('glimmer', _glimmer); 31 | } 32 | 33 | /** 34 | * Registers the glimmer-javascript grammar, and installes `javascript`, 35 | * `js`, `mjs` and `cjs` as aliases of it 36 | */ 37 | export function registerInjections(hljs) { 38 | registerGlimmerJsWithJsOverrides(hljs); 39 | } 40 | 41 | function registerGlimmerJsWithJsOverrides(hljs) { 42 | const newJsLanguageName = '_js-in-gjs'; 43 | 44 | // Rename original language so that we can still use it 45 | let js = hljs.getLanguage('javascript'); 46 | 47 | hljs.registerLanguage(newJsLanguageName, (hljs) => js.rawDefinition(hljs)); 48 | hljs.unregisterLanguage('javascript'); 49 | 50 | hljs.registerLanguage('glimmer-javascript', (hljs) => { 51 | const definition = glimmerJavascript(hljs, newJsLanguageName); 52 | 53 | definition.aliases.push('javascript', 'js', 'mjs', 'cjs', 'mjs'); 54 | 55 | return definition; 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | schedule: 10 | - cron: "0 3 * * 0" # every Sunday at 3am 11 | 12 | concurrency: ${{ github.workflow }}-${{ github.ref }} 13 | 14 | env: 15 | CI: true 16 | TURBO_API: http://127.0.0.1:9080 17 | TURBO_TOKEN: this-is-not-a-secret 18 | TURBO_TEAM: myself 19 | 20 | jobs: 21 | install_dependencies: 22 | name: Install Dependencies 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v6 26 | - uses: felixmosh/turborepo-gh-artifacts@v3 27 | with: 28 | repo-token: ${{ secrets.GITHUB_TOKEN }} 29 | - uses: NullVoxPopuli/action-setup-pnpm@v2.3.1 30 | 31 | lint: 32 | name: Lint Source 33 | runs-on: ubuntu-latest 34 | needs: [install_dependencies] 35 | steps: 36 | - uses: actions/checkout@v6 37 | - uses: felixmosh/turborepo-gh-artifacts@v3 38 | with: 39 | repo-token: ${{ secrets.GITHUB_TOKEN }} 40 | - uses: NullVoxPopuli/action-setup-pnpm@v2.3.1 41 | - run: pnpm lint 42 | 43 | esm_tests: 44 | name: ESM Tests 45 | timeout-minutes: 5 46 | runs-on: ubuntu-latest 47 | needs: [install_dependencies] 48 | strategy: 49 | matrix: 50 | node: 51 | - "14" 52 | - "16" 53 | - "18" 54 | - "20" 55 | steps: 56 | - uses: actions/checkout@v6 57 | - uses: felixmosh/turborepo-gh-artifacts@v3 58 | with: 59 | repo-token: ${{ secrets.GITHUB_TOKEN }} 60 | - uses: NullVoxPopuli/action-setup-pnpm@v2.3.1 61 | - name: Test Modules with ${{ matrix.node }} 62 | run: pnpm test 63 | working-directory: tests-esm 64 | 65 | cjs_tests: 66 | name: CJS Tests 67 | timeout-minutes: 5 68 | runs-on: ubuntu-latest 69 | needs: [install_dependencies] 70 | strategy: 71 | matrix: 72 | node: 73 | - "14" 74 | - "16" 75 | - "18" 76 | - "20" 77 | steps: 78 | - uses: actions/checkout@v6 79 | - uses: felixmosh/turborepo-gh-artifacts@v3 80 | with: 81 | repo-token: ${{ secrets.GITHUB_TOKEN }} 82 | - uses: NullVoxPopuli/action-setup-pnpm@v2.3.1 83 | - name: Test CJS with ${{ matrix.node }} 84 | run: pnpm test 85 | working-directory: tests-cjs 86 | 87 | 88 | hljs_tests: 89 | name: "Highlight.js Tests" 90 | timeout-minutes: 5 91 | runs-on: ubuntu-latest 92 | needs: [install_dependencies] 93 | steps: 94 | - uses: actions/checkout@v6 95 | - uses: felixmosh/turborepo-gh-artifacts@v3 96 | with: 97 | repo-token: ${{ secrets.GITHUB_TOKEN }} 98 | - uses: NullVoxPopuli/action-setup-pnpm@v2.3.1 99 | - run: scripts/hljs-test.sh 100 | working-directory: highlightjs-glimmer 101 | 102 | -------------------------------------------------------------------------------- /tests-esm/-utils.js: -------------------------------------------------------------------------------- 1 | import hljs from 'highlight.js'; 2 | import { setup } from 'highlightjs-glimmer'; 3 | import { expect } from 'vitest'; 4 | 5 | setup(hljs); 6 | 7 | import prettier from 'prettier'; 8 | 9 | // Used to resolve indentation issues. 10 | // We don't care about that in these tests -- we only care about tag identification. 11 | export function format(code) { 12 | return prettier.format(code, { parser: 'html', htmlWhitespaceSensitivity: 'ignore' }); 13 | } 14 | 15 | export function formattedEquals(actual, expected) { 16 | expect(format(actual)).toEqual(format(expected)); 17 | } 18 | 19 | export function parse(code, lang = 'glimmer') { 20 | return hljs.highlight(code, { language: lang }).value; 21 | } 22 | 23 | export function glimmer(...children) { 24 | return `${children.join('')}`; 25 | } 26 | 27 | export const templateTags = { 28 | get begin() { 29 | return tag('tag', ['<', tag('title', 'template'), '>']); 30 | }, 31 | get end() { 32 | return tag('tag', ['</', tag('title', 'template'), '>']); 33 | }, 34 | }; 35 | 36 | export function template(...content) { 37 | return list(templateTags.begin, '\n', ...indentLines(content), '\n', templateTags.end); 38 | } 39 | 40 | export function selfClosing(name, attributes = []) { 41 | return tag('tag', ['<', tag('title', name), ...attributes, ' />']); 42 | } 43 | 44 | export function indentLines(lines, amount = 2) { 45 | let indent = Array(amount + 1).join(' '); 46 | 47 | return lines.map((line) => { 48 | return `${indent}${line}`; 49 | }); 50 | } 51 | 52 | export function element(name, content = []) { 53 | return list( 54 | tag('tag', ['<', tag('title', name), '>']), 55 | '\n', 56 | ...indentLines(content), 57 | '\n', 58 | tag('tag', ['</', tag('title', name), '>']) 59 | ); 60 | } 61 | 62 | export function mustache(...content) { 63 | return tag('punctuation mustache', ['{{', ...content, '}}']); 64 | } 65 | 66 | export function arg(name) { 67 | return list(tag('punctuation', '@'), tag('params', name)); 68 | } 69 | 70 | export const keyword = { 71 | export: tag('keyword', 'export'), 72 | const: tag('keyword', 'const'), 73 | default: tag('keyword', 'default'), 74 | }; 75 | 76 | export const operator = { 77 | equals: tag('operator', '='), 78 | }; 79 | 80 | export function string(content) { 81 | return tag('string', ['"', content, '"']); 82 | } 83 | 84 | export const tags = { 85 | template, 86 | mustache, 87 | element, 88 | selfClosing, 89 | arg, 90 | keyword, 91 | operator, 92 | string, 93 | }; 94 | 95 | export function tag(klass, children = []) { 96 | let _children = Array.isArray(children) ? children : [children]; 97 | 98 | return `${_children.join('')}`; 99 | } 100 | 101 | export function list(...children) { 102 | return children.join(''); 103 | } 104 | -------------------------------------------------------------------------------- /highlightjs-glimmer/src/glimmer-javascript.js: -------------------------------------------------------------------------------- 1 | const CSS_RULE_BEGIN = [ 2 | 'css`', // hljs <= 11.9.0 3 | '.?css`', // hljs >= 11.10.0 4 | ]; 5 | 6 | function buildHbsLiteralRule(hljs, js) { 7 | const rawJs = js.rawDefinition(hljs); 8 | const css = rawJs.contains.find((rule) => CSS_RULE_BEGIN.includes(rule?.begin)); 9 | 10 | const rule = hljs.inherit(css, { begin: /hbs`/ }); 11 | 12 | rule.starts.subLanguage = 'glimmer'; 13 | 14 | return rule; 15 | } 16 | 17 | /** 18 | * A lot of this is stolen from XML 19 | */ 20 | const GLIMMER_TEMPLATE_TAG = { 21 | begin: /