Skip to content

Commit ed16747

Browse files
committed
feat: SyntaxHighlightElement.config getter/setter
- Configure the custom element via config setter - Expose configuration object
1 parent 5b4d7e4 commit ed16747

File tree

5 files changed

+110
-46
lines changed

5 files changed

+110
-46
lines changed

index.html

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
}
3131
</style>
3232
<script>
33+
// Global namespace config
3334
window.she = window.she || {};
3435
window.she.config = {
3536
// Define languages to support
@@ -92,16 +93,35 @@
9293

9394
<script type="module">
9495
/**
95-
* Option 1. autoload - default
96+
* Option 1. autoload - default with global namespace config
9697
*/
9798
import '/src/index.js'
9899

99100
/**
100-
* Option 2.a. Opt out of prism autoload by manually importing prism core
101+
* Option 2. configure with `SyntaxHighlightElement.config` and `?define=false` import query param
102+
*/
103+
// import { SyntaxHighlightElement } from '/src/index.js?define=false';
104+
// SyntaxHighlightElement.config = {
105+
// // Define languages to support
106+
// languages: ['html', 'css', 'js', 'jsx', 'md'],
107+
// // Language specific token type overwrites
108+
// languageTokens: {
109+
// css: ['important'],
110+
// md: ['title', 'list'],
111+
// },
112+
// // Customize setup/tokenize methods
113+
// // async setup() {},
114+
// // tokenize: () => [],
115+
// };
116+
// SyntaxHighlightElement.define();
117+
118+
/**
119+
* Option 3.a. Opt out of prism autoload by manually importing prism core
101120
*/
102121
// import 'prismjs/components/prism-core'
122+
103123
/**
104-
* Option 2.b. Load lang deps manually (without dependency resolution)
124+
* Option 3.b. Load lang deps manually (without dependency resolution)
105125
*/
106126
// import 'prismjs/components/prism-markup'
107127
// import 'prismjs/components/prism-css'

src/config.js

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,23 @@
1-
import { NAMESPACE } from './constants';
2-
import { loadPrismCore, loadPrismLanguage, tokenize, tokenTypes } from './tokenizer/prism';
1+
import { setup, tokenize, tokenTypes } from './tokenizer/prism';
32

43
/**
54
* @typedef Config
65
* @type {object}
7-
* @property {string[]} languages - Syntax language grammars to autoload.
8-
* @property {string[]} tokenTypes - Language token types.
9-
* @property {{[key: string]: string[]}} languageTokens - Language specific token types.
6+
* @property {string[]} languages - List of languages to support for syntax highlighting.
7+
* @property {string[]} tokenTypes - Token types used during lexing/parsing.
8+
* @property {{[key: string]: string[]}} languageTokens - Mapping of language names to their specific tokenization rules.
109
* @property {function} setup - Runs before the custom element gets defined in the registry.
11-
* @property {function} tokenize - Used to tokenize the text.
10+
* @property {function} tokenize - Tokenize text based on the specified language grammar
1211
*/
1312

14-
/** @type {Config} */
15-
export const config = Object.assign(
16-
{
17-
languages: ['markup', 'css', 'javascript'],
18-
tokenTypes,
19-
languageTokens: {},
20-
async setup() {
21-
try {
22-
if (!window.Prism) {
23-
const prismBaseUrl = 'https://cdn.jsdelivr.net/npm/prismjs@1.30.0';
24-
await loadPrismCore(prismBaseUrl);
25-
await loadPrismLanguage({
26-
baseUrl: prismBaseUrl,
27-
language: config.languages,
28-
});
29-
}
30-
} catch (error) {
31-
console.error(error);
32-
}
33-
},
34-
tokenize,
35-
},
36-
window[NAMESPACE]?.config || {},
37-
);
13+
/**
14+
* Default configuration object.
15+
* @type {Config}
16+
*/
17+
export const configDefaults = {
18+
languages: ['markup', 'css', 'javascript'],
19+
tokenTypes,
20+
languageTokens: {},
21+
setup,
22+
tokenize,
23+
};

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { NAMESPACE } from './constants';
2-
import { SyntaxHighlightElement } from './syntax-highlight-element';
2+
import SyntaxHighlightElement from './syntax-highlight-element';
33
import { setupTokenHighlights } from './utils';
44

55
export { SyntaxHighlightElement, setupTokenHighlights };

src/syntax-highlight-element.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,50 @@
1-
import { config } from './config';
1+
/** @import { Config } from './config.js' */
2+
3+
import { configDefaults } from './config';
4+
import { NAMESPACE } from './constants';
25
import { setupTokenHighlights } from './utils';
36

4-
export class SyntaxHighlightElement extends HTMLElement {
7+
export default class Component extends HTMLElement {
58
static async define(tagName = 'syntax-highlight', registry = customElements) {
69
if (!CSS.highlights) {
710
console.info('The CSS Custom Highlight API is not supported in this browser.');
811
return;
912
}
1013

1114
if (!registry.get(tagName)) {
12-
typeof config?.setup === 'function' && (await config.setup());
13-
setupTokenHighlights(config.tokenTypes, { languageTokens: config.languageTokens });
14-
registry.define(tagName, SyntaxHighlightElement);
15-
return SyntaxHighlightElement;
15+
// Setup custom element
16+
typeof Component.#config?.setup === 'function' && (await Component.#config.setup());
17+
setupTokenHighlights(Component.#config.tokenTypes, {
18+
languageTokens: Component.#config.languageTokens,
19+
});
20+
registry.define(tagName, Component);
21+
return Component;
1622
}
1723
}
1824

25+
/**
26+
* Configuration object with default values.
27+
* Merge defaults with global namespace config.
28+
* @type {Config}
29+
* */
30+
static #config = Object.assign(configDefaults, window[NAMESPACE]?.config || {});
31+
32+
/**
33+
* Configuration object
34+
* @type {Config}
35+
*/
36+
static get config() {
37+
return Component.#config;
38+
}
39+
40+
/**
41+
* Modify configuration object
42+
* @param {Config} properties
43+
*/
44+
static set config(properties) {
45+
Component.#config = Object.assign(Component.#config, properties);
46+
}
47+
1948
#internals;
2049
#highlights = new Set();
2150

@@ -56,8 +85,8 @@ export class SyntaxHighlightElement extends HTMLElement {
5685
*/
5786
paintTokenHighlights() {
5887
// Tokenize the text
59-
const tokens = config.tokenize(this.contentElement.innerText, this.language) || [];
60-
const languageTokenTypes = config.languageTokens?.[this.language] || [];
88+
const tokens = Component.#config.tokenize(this.contentElement.innerText, this.language) || [];
89+
const languageTokenTypes = Component.#config.languageTokens?.[this.language] || [];
6190

6291
// Paint highlights
6392
let pos = 0;

src/tokenizer/prism.js

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
11
/**
2+
* Asynchronously sets up the Prism tokenizer if not already available.
3+
* Runs before the custom element gets defined in the registry.
24
*
3-
* @param {string} text - The text to tokenize.
4-
* @param {string} language - The syntax language grammar.
5-
* @returns {Array} - An array of flattened prismjs tokens.
5+
* Loads Prism core and specified language grammars from a CDN.
6+
* Safe to call multiple times; will only load once.
7+
*
8+
* @async
9+
* @function setup
10+
* @returns {Promise<void>} Resolves when Prism and required languages are loaded, or rejects on network/error.
11+
* @throws {Error} Logs errors to console if loading fails.
12+
*/
13+
export async function setup() {
14+
try {
15+
if (!window.Prism) {
16+
const prismBaseUrl = 'https://cdn.jsdelivr.net/npm/prismjs@1.30.0';
17+
await loadPrismCore(prismBaseUrl);
18+
await loadPrismLanguage({
19+
baseUrl: prismBaseUrl,
20+
language: SyntaxHighlightElement.config.languages,
21+
});
22+
}
23+
} catch (error) {
24+
console.error(error);
25+
}
26+
}
27+
28+
/**
29+
* Tokenizes a given text using Prism.js based on the specified language grammar.
30+
*
31+
* @function tokenize
32+
* @param {string} text - The text string to tokenize.
33+
* @param {string} language - The language name associated with the code.
34+
* @returns {Array<Object>} A flat array of prismjs tokens representing the tokenized code.
635
*/
736
export function tokenize(text, language) {
8-
const lang = window.Prism.languages[language] || undefined;
37+
const lang = window.Prism?.languages[language] || undefined;
938
if (!lang) {
1039
console.warn(`window.Prism.languages.${language} is undefined.`);
1140
return [];

0 commit comments

Comments
 (0)