Comparing version 0.8.2 to 0.9.0-beta.0
@@ -7,3 +7,2 @@ "use strict"; | ||
exports.diff = diff; | ||
exports.getColor = getColor; | ||
exports.language = void 0; | ||
@@ -14,13 +13,10 @@ exports.parse = parse; | ||
var _starryNight = require("@wooorm/starry-night"); | ||
var _shiki = require("shiki"); | ||
var _darkStyle = _interopRequireDefault(require("./dark-style")); | ||
let highlighter = null; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const starryNight = (0, _starryNight.createStarryNight)(_starryNight.all); | ||
let starryNightCache = null; | ||
async function ready() { | ||
starryNightCache = await starryNight; | ||
highlighter = await (0, _shiki.getHighlighter)({ | ||
theme: 'github-dark' | ||
}); | ||
} | ||
@@ -45,96 +41,124 @@ | ||
const raw = integrate(reindent(code)); | ||
if (starryNightCache == null) throw new Error(`you must await ready()`); | ||
const sn = starryNightCache; | ||
const scope = sn.flagToScope(code.language); | ||
if (typeof scope !== 'string') { | ||
throw new Error(`language ${code.language} not found`); | ||
if (highlighter == null) { | ||
throw new Error('you must await ready() before parsing code'); | ||
} | ||
const parsed = sn.highlight(raw, scope); | ||
return parsed.children.map(child => colorRecurse(child, (options === null || options === void 0 ? void 0 : options.codeStyle) ?? {})).flat().map(({ | ||
color, | ||
...rest | ||
}) => ({ ...rest, | ||
color: color === '' ? '#c9d1d9' : color | ||
})); | ||
const parsed = highlighter.codeToThemedTokens(raw, code.language); | ||
return parsed.flatMap(line => line.concat([{ | ||
content: '\n', | ||
explanation: [{ | ||
content: '\n', | ||
scopes: [] | ||
}] | ||
}])).flatMap(token => maybeReplace(token, (options === null || options === void 0 ? void 0 : options.codeStyle) ?? {})).map(themed => ({ | ||
code: themed.content, | ||
color: themed.color || '#C9D1D9' | ||
})).slice(0, -1); | ||
} | ||
const rules = new Map(Object.entries(_darkStyle.default).map(([k, v]) => [k, new Map(Object.entries(v))])); | ||
const styleMap = new Map([['pl-s', 'stringContent'], ['pl-pds', 'stringPunctuation'], ['pl-c', 'comment'], ['pl-smi', 'variable'], ['pl-v', 'parameter'], ['pl-sr', 'regexpContent'], ['pl-c1', 'literal'], ['pl-k', 'keyword'], ['pl-en', 'entityName']]); | ||
function getColor(scopes, codeStyle, fallback = '#C9D1D9') { | ||
function hasScope(target) { | ||
return scopes.some(scope => scope.scopeName.startsWith(target)); | ||
} | ||
function getColor(classList, codeStyle) { | ||
console.assert(classList.length <= 1, `classList too long`); | ||
const isStringPunctuation = hasScope('punctuation.definition.string'); | ||
const isRegex = hasScope('string.regexp'); | ||
const isKeyword = hasScope('keyword'); | ||
const isString = hasScope('string'); | ||
const isVaraible = hasScope('variable'); | ||
const isParameter = hasScope('variable.parameter'); | ||
const isComment = hasScope('comment'); | ||
const isNumber = hasScope('constant.numeric'); | ||
const isBoolean = hasScope('constant.language.boolean'); | ||
const isStorageType = hasScope('storage.type'); | ||
const isEntityName = hasScope('entity.name'); | ||
if (classList.length === 1) { | ||
const key = styleMap.get(classList[0]); | ||
if (isRegex && !isStringPunctuation && !isKeyword) { | ||
var _codeStyle$regexpCont, _codeStyle$regexp, _codeStyle$regexp2; | ||
if (key != null && codeStyle[key] != null) { | ||
var _codeStyle$key; | ||
return ((_codeStyle$regexpCont = codeStyle.regexpContent) === null || _codeStyle$regexpCont === void 0 ? void 0 : _codeStyle$regexpCont.text) ?? ((_codeStyle$regexp = codeStyle.regexp) === null || _codeStyle$regexp === void 0 ? void 0 : _codeStyle$regexp.content) ?? ((_codeStyle$regexp2 = codeStyle.regexp) === null || _codeStyle$regexp2 === void 0 ? void 0 : _codeStyle$regexp2.text) ?? fallback; | ||
} else if (isRegex && isStringPunctuation) { | ||
var _codeStyle$regexp3, _codeStyle$regexp4; | ||
return (_codeStyle$key = codeStyle[key]) === null || _codeStyle$key === void 0 ? void 0 : _codeStyle$key.text; | ||
} | ||
return ((_codeStyle$regexp3 = codeStyle.regexp) === null || _codeStyle$regexp3 === void 0 ? void 0 : _codeStyle$regexp3.brackets) ?? ((_codeStyle$regexp4 = codeStyle.regexp) === null || _codeStyle$regexp4 === void 0 ? void 0 : _codeStyle$regexp4.text) ?? fallback; | ||
} else if (isRegex && isKeyword) { | ||
var _codeStyle$regexp5, _codeStyle$regexp6; | ||
const styles = rules.get(`.${classList[0]}`); | ||
console.assert(((styles === null || styles === void 0 ? void 0 : styles.size) ?? 0) <= 1, `more styles than just color`); | ||
return styles === null || styles === void 0 ? void 0 : styles.get('color'); | ||
} | ||
return ((_codeStyle$regexp5 = codeStyle.regexp) === null || _codeStyle$regexp5 === void 0 ? void 0 : _codeStyle$regexp5.flags) ?? ((_codeStyle$regexp6 = codeStyle.regexp) === null || _codeStyle$regexp6 === void 0 ? void 0 : _codeStyle$regexp6.text) ?? fallback; | ||
} else if (isStringPunctuation) { | ||
var _codeStyle$stringPunc; | ||
return undefined; | ||
} | ||
return ((_codeStyle$stringPunc = codeStyle.stringPunctuation) === null || _codeStyle$stringPunc === void 0 ? void 0 : _codeStyle$stringPunc.text) ?? fallback; | ||
} else if (isString) { | ||
var _codeStyle$stringCont; | ||
function colorRecurse(parsed, codeStyle) { | ||
if (parsed.type === 'text') { | ||
return [{ | ||
code: parsed.value, | ||
color: '' | ||
}]; | ||
} else if (parsed.type === 'element') { | ||
var _parsed$properties; | ||
return ((_codeStyle$stringCont = codeStyle.stringContent) === null || _codeStyle$stringCont === void 0 ? void 0 : _codeStyle$stringCont.text) ?? fallback; | ||
} else if (isParameter) { | ||
var _codeStyle$parameter; | ||
const className = (_parsed$properties = parsed.properties) === null || _parsed$properties === void 0 ? void 0 : _parsed$properties.className; | ||
const color = getColor(className ?? [], codeStyle); | ||
const children = parsed.children.map(child => colorRecurse(child, codeStyle)); | ||
const result = []; | ||
return ((_codeStyle$parameter = codeStyle.parameter) === null || _codeStyle$parameter === void 0 ? void 0 : _codeStyle$parameter.text) ?? fallback; | ||
} else if (isVaraible) { | ||
var _codeStyle$variable; | ||
const emit = () => { | ||
if (temp !== '') { | ||
if (color != '') { | ||
result.push({ | ||
code: temp, | ||
color | ||
}); | ||
} else { | ||
result.push({ | ||
code: temp, | ||
color: '' | ||
}); | ||
} | ||
return ((_codeStyle$variable = codeStyle.variable) === null || _codeStyle$variable === void 0 ? void 0 : _codeStyle$variable.text) ?? fallback; | ||
} else if (isComment) { | ||
var _codeStyle$comment; | ||
temp = ''; | ||
} | ||
}; | ||
return ((_codeStyle$comment = codeStyle.comment) === null || _codeStyle$comment === void 0 ? void 0 : _codeStyle$comment.text) ?? fallback; | ||
} else if (isNumber) { | ||
var _codeStyle$number, _codeStyle$literal; | ||
let temp = ''; | ||
return ((_codeStyle$number = codeStyle.number) === null || _codeStyle$number === void 0 ? void 0 : _codeStyle$number.text) ?? ((_codeStyle$literal = codeStyle.literal) === null || _codeStyle$literal === void 0 ? void 0 : _codeStyle$literal.text) ?? fallback; | ||
} else if (isBoolean) { | ||
var _codeStyle$boolean, _codeStyle$literal2; | ||
for (const child of children) { | ||
for (const item of child) { | ||
if (item.color === '') { | ||
temp += item.code; | ||
} else { | ||
emit(); | ||
result.push(item); | ||
} | ||
} | ||
return ((_codeStyle$boolean = codeStyle.boolean) === null || _codeStyle$boolean === void 0 ? void 0 : _codeStyle$boolean.text) ?? ((_codeStyle$literal2 = codeStyle.literal) === null || _codeStyle$literal2 === void 0 ? void 0 : _codeStyle$literal2.text) ?? fallback; | ||
} else if (isStorageType) { | ||
var _codeStyle$keyword; | ||
emit(); | ||
} | ||
return ((_codeStyle$keyword = codeStyle.keyword) === null || _codeStyle$keyword === void 0 ? void 0 : _codeStyle$keyword.text) ?? fallback; | ||
} else if (isEntityName) { | ||
var _codeStyle$entityName; | ||
emit(); | ||
return result; | ||
return ((_codeStyle$entityName = codeStyle.entityName) === null || _codeStyle$entityName === void 0 ? void 0 : _codeStyle$entityName.text) ?? fallback; | ||
} else { | ||
throw new Error(); | ||
return fallback; | ||
} | ||
} | ||
function maybeReplace(node, codeStyle) { | ||
const result = []; | ||
for (const { | ||
content, | ||
scopes | ||
} of node.explanation ?? []) { | ||
result.push({ | ||
content, | ||
color: getColor(scopes, codeStyle, node.color) | ||
}); | ||
} | ||
const aggregated = []; | ||
for (const { | ||
content, | ||
color | ||
} of result) { | ||
const last = aggregated.at(-1); | ||
if (last == null || last.color !== color) { | ||
aggregated.push({ | ||
content, | ||
color | ||
}); | ||
} else { | ||
last.content += content; | ||
} | ||
} | ||
return aggregated; | ||
} | ||
function toString(tokens) { | ||
@@ -141,0 +165,0 @@ return tokens.map(token => token.code).join(''); |
@@ -1,7 +0,7 @@ | ||
import { createStarryNight, all } from '@wooorm/starry-night'; | ||
import style from './dark-style'; | ||
const starryNight = createStarryNight(all); | ||
let starryNightCache = null; | ||
import { getHighlighter } from 'shiki'; | ||
let highlighter = null; | ||
export async function ready() { | ||
starryNightCache = await starryNight; | ||
highlighter = await getHighlighter({ | ||
theme: 'github-dark' | ||
}); | ||
} | ||
@@ -23,88 +23,98 @@ const handler = { | ||
const raw = integrate(reindent(code)); | ||
if (starryNightCache == null) throw new Error(`you must await ready()`); | ||
const sn = starryNightCache; | ||
const scope = sn.flagToScope(code.language); | ||
if (typeof scope !== 'string') { | ||
throw new Error(`language ${code.language} not found`); | ||
if (highlighter == null) { | ||
throw new Error('you must await ready() before parsing code'); | ||
} | ||
const parsed = sn.highlight(raw, scope); | ||
return parsed.children.map(child => colorRecurse(child, options?.codeStyle ?? {})).flat().map(({ | ||
color, | ||
...rest | ||
}) => ({ ...rest, | ||
color: color === '' ? '#c9d1d9' : color | ||
})); | ||
const parsed = highlighter.codeToThemedTokens(raw, code.language); | ||
return parsed.flatMap(line => line.concat([{ | ||
content: '\n', | ||
explanation: [{ | ||
content: '\n', | ||
scopes: [] | ||
}] | ||
}])).flatMap(token => maybeReplace(token, options?.codeStyle ?? {})).map(themed => ({ | ||
code: themed.content, | ||
color: themed.color || '#C9D1D9' | ||
})).slice(0, -1); | ||
} | ||
const rules = new Map(Object.entries(style).map(([k, v]) => [k, new Map(Object.entries(v))])); | ||
const styleMap = new Map([['pl-s', 'stringContent'], ['pl-pds', 'stringPunctuation'], ['pl-c', 'comment'], ['pl-smi', 'variable'], ['pl-v', 'parameter'], ['pl-sr', 'regexpContent'], ['pl-c1', 'literal'], ['pl-k', 'keyword'], ['pl-en', 'entityName']]); | ||
export function getColor(classList, codeStyle) { | ||
console.assert(classList.length <= 1, `classList too long`); | ||
if (classList.length === 1) { | ||
const key = styleMap.get(classList[0]); | ||
function getColor(scopes, codeStyle, fallback = '#C9D1D9') { | ||
function hasScope(target) { | ||
return scopes.some(scope => scope.scopeName.startsWith(target)); | ||
} | ||
if (key != null && codeStyle[key] != null) { | ||
return codeStyle[key]?.text; | ||
} | ||
const isStringPunctuation = hasScope('punctuation.definition.string'); | ||
const isRegex = hasScope('string.regexp'); | ||
const isKeyword = hasScope('keyword'); | ||
const isString = hasScope('string'); | ||
const isVaraible = hasScope('variable'); | ||
const isParameter = hasScope('variable.parameter'); | ||
const isComment = hasScope('comment'); | ||
const isNumber = hasScope('constant.numeric'); | ||
const isBoolean = hasScope('constant.language.boolean'); | ||
const isStorageType = hasScope('storage.type'); | ||
const isEntityName = hasScope('entity.name'); | ||
const styles = rules.get(`.${classList[0]}`); | ||
console.assert((styles?.size ?? 0) <= 1, `more styles than just color`); | ||
return styles?.get('color'); | ||
if (isRegex && !isStringPunctuation && !isKeyword) { | ||
return codeStyle.regexpContent?.text ?? codeStyle.regexp?.content ?? codeStyle.regexp?.text ?? fallback; | ||
} else if (isRegex && isStringPunctuation) { | ||
return codeStyle.regexp?.brackets ?? codeStyle.regexp?.text ?? fallback; | ||
} else if (isRegex && isKeyword) { | ||
return codeStyle.regexp?.flags ?? codeStyle.regexp?.text ?? fallback; | ||
} else if (isStringPunctuation) { | ||
return codeStyle.stringPunctuation?.text ?? fallback; | ||
} else if (isString) { | ||
return codeStyle.stringContent?.text ?? fallback; | ||
} else if (isParameter) { | ||
return codeStyle.parameter?.text ?? fallback; | ||
} else if (isVaraible) { | ||
return codeStyle.variable?.text ?? fallback; | ||
} else if (isComment) { | ||
return codeStyle.comment?.text ?? fallback; | ||
} else if (isNumber) { | ||
return codeStyle.number?.text ?? codeStyle.literal?.text ?? fallback; | ||
} else if (isBoolean) { | ||
return codeStyle.boolean?.text ?? codeStyle.literal?.text ?? fallback; | ||
} else if (isStorageType) { | ||
return codeStyle.keyword?.text ?? fallback; | ||
} else if (isEntityName) { | ||
return codeStyle.entityName?.text ?? fallback; | ||
} else { | ||
return fallback; | ||
} | ||
return undefined; | ||
} | ||
function colorRecurse(parsed, codeStyle) { | ||
if (parsed.type === 'text') { | ||
return [{ | ||
code: parsed.value, | ||
color: '' | ||
}]; | ||
} else if (parsed.type === 'element') { | ||
const className = parsed.properties?.className; | ||
const color = getColor(className ?? [], codeStyle); | ||
const children = parsed.children.map(child => colorRecurse(child, codeStyle)); | ||
const result = []; | ||
function maybeReplace(node, codeStyle) { | ||
const result = []; | ||
const emit = () => { | ||
if (temp !== '') { | ||
if (color != '') { | ||
result.push({ | ||
code: temp, | ||
color | ||
}); | ||
} else { | ||
result.push({ | ||
code: temp, | ||
color: '' | ||
}); | ||
} | ||
for (const { | ||
content, | ||
scopes | ||
} of node.explanation ?? []) { | ||
result.push({ | ||
content, | ||
color: getColor(scopes, codeStyle, node.color) | ||
}); | ||
} | ||
temp = ''; | ||
} | ||
}; | ||
const aggregated = []; | ||
let temp = ''; | ||
for (const { | ||
content, | ||
color | ||
} of result) { | ||
const last = aggregated.at(-1); | ||
for (const child of children) { | ||
for (const item of child) { | ||
if (item.color === '') { | ||
temp += item.code; | ||
} else { | ||
emit(); | ||
result.push(item); | ||
} | ||
} | ||
emit(); | ||
if (last == null || last.color !== color) { | ||
aggregated.push({ | ||
content, | ||
color | ||
}); | ||
} else { | ||
last.content += content; | ||
} | ||
} | ||
emit(); | ||
return result; | ||
} else { | ||
throw new Error(); | ||
} | ||
return aggregated; | ||
} | ||
@@ -111,0 +121,0 @@ |
@@ -20,5 +20,5 @@ export declare type Colors = { | ||
/** | ||
* The color for an erroneous token, like a mismatched bracket. | ||
* The color for a variable | ||
* ```ts | ||
* (5 + 3)) // <== extra token | ||
* const what; // "what" is a variable | ||
* ``` | ||
@@ -42,2 +42,36 @@ */ | ||
/** | ||
* The colors for a regular expressions | ||
*/ | ||
regexp?: { | ||
/** | ||
* The colors for the whole Regular Expression literal | ||
* ```ts | ||
* /regex/g | ||
* ``` | ||
*/ | ||
text?: string; | ||
/** | ||
* The colors for a Regular Expression's content. | ||
* ```ts | ||
* /regex/g // "regex" is the content | ||
* ``` | ||
*/ | ||
content?: string; | ||
/** | ||
* The colors for a Regular Expression's brackets. | ||
* ```ts | ||
* /regex/g // "/" are the brackets | ||
* ``` | ||
*/ | ||
brackets?: string; | ||
/** | ||
* The colors for a Regular Expression's falgs. | ||
* ```ts | ||
* /regex/g // "g" is the only flag | ||
* ``` | ||
*/ | ||
flags?: string; | ||
}; | ||
/** | ||
* @deprecated Use {@link regexp} instead | ||
* The colors for a Regular Expression's content. | ||
@@ -50,2 +84,3 @@ * ```ts | ||
/** | ||
* @deprecated Use {@link number} or {@link boolean} instead | ||
* The colors for a literal value, like "true" or "5". | ||
@@ -58,2 +93,16 @@ * ```ts | ||
/** | ||
* The colors for a literal number | ||
* ```ts | ||
* 5 | ||
* ``` | ||
*/ | ||
number?: Colors; | ||
/** | ||
* The colors for a literal boolean | ||
* ```ts | ||
* true | ||
* ``` | ||
*/ | ||
boolean?: Colors; | ||
/** | ||
* The colors for a language keyword, like `function`. | ||
@@ -60,0 +109,0 @@ */ |
@@ -1,7 +0,2 @@ | ||
import type { Root } from 'hast'; | ||
import type { CodeStyle } from './style'; | ||
export interface StarryNight { | ||
flagToScope: (flag: string) => string | undefined; | ||
highlight: (value: string, scope: string) => Root; | ||
} | ||
export declare function ready(): Promise<void>; | ||
@@ -30,3 +25,2 @@ export interface CodeTree { | ||
}): Token[]; | ||
export declare function getColor(classList: string[], codeStyle: CodeStyle): string | undefined; | ||
export declare function toString(tokens: Token[]): string; | ||
@@ -33,0 +27,0 @@ export declare function diff(start: CodeTree, end: CodeTree, options?: { |
{ | ||
"name": "code-fns", | ||
"version": "0.8.2", | ||
"version": "0.9.0-beta.0", | ||
"description": "A library for visualizing code.", | ||
@@ -59,3 +59,3 @@ "license": "MIT", | ||
"dependencies": { | ||
"@wooorm/starry-night": "^1.2.0" | ||
"shiki": "^0.14.1" | ||
}, | ||
@@ -62,0 +62,0 @@ "prettier": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37925
19
1088
+ Addedshiki@^0.14.1
+ Addedansi-sequence-parser@1.1.1(transitive)
+ Addedjsonc-parser@3.3.1(transitive)
+ Addedshiki@0.14.7(transitive)
+ Addedvscode-textmate@8.0.0(transitive)
- Removed@wooorm/starry-night@^1.2.0
- Removed@types/hast@2.3.10(transitive)
- Removed@types/unist@2.0.11(transitive)
- Removed@wooorm/starry-night@1.7.0(transitive)
- Removedimport-meta-resolve@2.2.2(transitive)
- Removedvscode-textmate@9.1.0(transitive)