rapier-math 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +187 -85
- package/adoption-check.mjs +149 -0
- package/check.mjs +21 -0
- package/classic.d.ts +4 -0
- package/delimiters.d.ts +8 -0
- package/delimiters.mjs +39 -0
- package/examples/README.md +25 -0
- package/examples/browser.html +10 -0
- package/examples/browser.mjs +24 -0
- package/examples/formula.mjs +12 -0
- package/examples/markdown.mjs +9 -0
- package/examples/react.tsx +19 -0
- package/examples/stream.mjs +19 -0
- package/examples/unified.mjs +26 -0
- package/examples/worker.mjs +7 -0
- package/index.d.ts +43 -0
- package/index.mjs +205 -108
- package/integration.d.ts +9 -0
- package/integration.mjs +28 -0
- package/markdown-it.d.ts +6 -0
- package/markdown-it.mjs +45 -0
- package/markdown-rules.mjs +65 -0
- package/math-components.json +6 -6
- package/package.json +78 -5
- package/rapier-math-1.1.0.js +2980 -0
- package/rehype.d.ts +6 -0
- package/rehype.mjs +37 -0
- package/src/math/FONT-SOURCE.md +32 -6
- package/src/math/README.md +210 -0
- package/src/math/adoption-verify.mjs +149 -0
- package/src/math/api.js +37 -14
- package/src/math/browser.js +67 -30
- package/src/math/build-font.py +237 -95
- package/src/math/classic.d.ts +4 -0
- package/src/math/delimiters.d.ts +8 -0
- package/src/math/delimiters.mjs +39 -0
- package/src/math/examples/README.md +25 -0
- package/src/math/examples/browser.html +10 -0
- package/src/math/examples/browser.mjs +24 -0
- package/src/math/examples/formula.mjs +12 -0
- package/src/math/examples/markdown.mjs +9 -0
- package/src/math/examples/react.tsx +19 -0
- package/src/math/examples/stream.mjs +19 -0
- package/src/math/examples/unified.mjs +26 -0
- package/src/math/examples/worker.mjs +7 -0
- package/src/math/fallbacks.js +5 -5
- package/src/math/font.js +59 -26
- package/src/math/index.d.ts +43 -0
- package/src/math/integration.d.ts +9 -0
- package/src/math/integration.mjs +28 -0
- package/src/math/layout.js +37 -33
- package/src/math/markdown-it.d.ts +6 -0
- package/src/math/markdown-it.mjs +45 -0
- package/src/math/markdown-rules.mjs +65 -0
- package/src/math/package-build.mjs +4 -4
- package/src/math/package-check.mjs +118 -0
- package/src/math/package-readme.mjs +8 -0
- package/src/math/package-verify.mjs +7 -2
- package/src/math/rehype.d.ts +6 -0
- package/src/math/rehype.mjs +37 -0
- package/src/math/stream.d.ts +14 -0
- package/src/math/stream.mjs +46 -0
- package/src/package.json +1 -1
- package/src/shell/math-resources.json +184 -25
- package/src/tools/build-math.mjs +23 -4
- package/stream.d.ts +14 -0
- package/stream.mjs +46 -0
- package/verify.mjs +7 -2
- package/rapier-math-1.0.1.js +0 -2883
package/integration.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import type {RenderOptions} from './index.js';
|
|
3
|
+
export interface IntegrationOptions {
|
|
4
|
+
renderOptions?: RenderOptions;
|
|
5
|
+
/** Per document. Default 1024. Exhaustion leaves subsequent math as source. */
|
|
6
|
+
maxFormulas?: number;
|
|
7
|
+
/** Combined successful SVG code units per document. Default 16 * 1024 * 1024. */
|
|
8
|
+
maxOutputLength?: number;
|
|
9
|
+
}
|
package/integration.mjs
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {render} from './index.mjs';
|
|
3
|
+
|
|
4
|
+
export const escapeText = text => text.replace(/[&<>"'\r]/g, char =>
|
|
5
|
+
({'&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '\r': ' '}[char]));
|
|
6
|
+
|
|
7
|
+
// One budget per document render. Rejection leaves the host's text node intact.
|
|
8
|
+
export function createFormulaRenderer(options = {}) {
|
|
9
|
+
const maxFormulas = options.maxFormulas ?? 1024, maxOutputLength = options.maxOutputLength ?? 16 * 1024 * 1024;
|
|
10
|
+
for (const value of [maxFormulas, maxOutputLength])
|
|
11
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError('Math integration budgets must be finite nonnegative integers.');
|
|
12
|
+
let count = 0, length = 0;
|
|
13
|
+
return (source, displayMode) => {
|
|
14
|
+
let result;
|
|
15
|
+
if (count >= maxFormulas || length >= maxOutputLength) return {errors: [{code: 'resource-limit', offset: 0,
|
|
16
|
+
message: 'The document math budget is exhausted.'}]};
|
|
17
|
+
count++;
|
|
18
|
+
try { result = render(source, {...options.renderOptions, displayMode}); }
|
|
19
|
+
catch (error) { return {errors: [{code: error.code || 'invalid-tex', message: String(error.message), offset: error.offset || 0}]}; }
|
|
20
|
+
if (result.errors.length) return {errors: result.errors};
|
|
21
|
+
if (result.svg.length > maxOutputLength - length) {
|
|
22
|
+
length = maxOutputLength;
|
|
23
|
+
return {errors: [{code: 'resource-limit', offset: 0, message: 'The document math output budget is exhausted.'}]};
|
|
24
|
+
}
|
|
25
|
+
length += result.svg.length;
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
}
|
package/markdown-it.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import type MarkdownIt from 'markdown-it';
|
|
3
|
+
import type {DelimiterOptions} from './delimiters.js';
|
|
4
|
+
import type {IntegrationOptions} from './integration.js';
|
|
5
|
+
export interface MarkdownMathOptions extends DelimiterOptions, IntegrationOptions {}
|
|
6
|
+
export default function rapierMath(md: InstanceType<typeof MarkdownIt>, options?: MarkdownMathOptions): void;
|
package/markdown-it.mjs
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {installMathRules} from './markdown-rules.mjs';
|
|
3
|
+
import {createFormulaRenderer, escapeText} from './integration.mjs';
|
|
4
|
+
|
|
5
|
+
export default function rapierMath(md, options = {}) {
|
|
6
|
+
installMathRules(md, options);
|
|
7
|
+
let session;
|
|
8
|
+
function withSession(renderer, render, tokens, opts, env) {
|
|
9
|
+
const previous = session, current = {render: createFormulaRenderer(options), children: new WeakSet()};
|
|
10
|
+
const visit = tokens => {
|
|
11
|
+
for (const token of tokens) {
|
|
12
|
+
if (token.children) { current.children.add(token.children); visit(token.children); }
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
visit(tokens);
|
|
16
|
+
session = current;
|
|
17
|
+
try { return render.call(renderer, tokens, opts, env); }
|
|
18
|
+
finally { session = previous; }
|
|
19
|
+
}
|
|
20
|
+
const render = md.renderer.render, renderInline = md.renderer.renderInline;
|
|
21
|
+
md.renderer.render = function(tokens, opts, env) {
|
|
22
|
+
return withSession(this, render, tokens, opts, env);
|
|
23
|
+
};
|
|
24
|
+
md.renderer.renderInline = function(tokens, opts, env) {
|
|
25
|
+
// The block renderer's inline children share its document budget. A separate
|
|
26
|
+
// inline tree, including a nested host renderInline call, owns a fresh one.
|
|
27
|
+
if (session?.children.has(tokens)) return renderInline.call(this, tokens, opts, env);
|
|
28
|
+
return withSession(this, renderInline, tokens, opts, env);
|
|
29
|
+
};
|
|
30
|
+
function formula(tokens, index, _options, env) {
|
|
31
|
+
const token = tokens[index], display = token.type === 'math_block';
|
|
32
|
+
const result = (session?.render || createFormulaRenderer(options))(token.content, display);
|
|
33
|
+
if (result.errors.length) {
|
|
34
|
+
(env.rapierMathErrors ||= []).push({source: token.content, errors: result.errors});
|
|
35
|
+
return '<code data-math-error="' + escapeText(result.errors[0].code) + '">' +
|
|
36
|
+
escapeText(token.meta?.mathSource ?? token.content) + '</code>';
|
|
37
|
+
}
|
|
38
|
+
return result.svg;
|
|
39
|
+
}
|
|
40
|
+
md.renderer.rules.math_inline = formula;
|
|
41
|
+
md.renderer.rules.math_block = (tokens, index, opts, env) => {
|
|
42
|
+
const result = formula(tokens, index, opts, env);
|
|
43
|
+
return tokens[index].block ? '<div class="math-display">' + result + '</div>\n' : result;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Original neutral adapter. The host owns Markdown structure and normalization.
|
|
3
|
+
import {createMathMatcher} from './delimiters.mjs';
|
|
4
|
+
|
|
5
|
+
export function installMathRules(md, {singleDollar = false, brackets = true, ruleName = 'rapier_math', inLink = false} = {}) {
|
|
6
|
+
// The host's image-alt renderer ignores unknown token types. Keep math as
|
|
7
|
+
// literal source in text-only output, while leaving escaping to that host.
|
|
8
|
+
const renderText = md.renderer.renderInlineAsText;
|
|
9
|
+
md.renderer.renderInlineAsText = function(tokens, options, env) {
|
|
10
|
+
return renderText.call(this, tokens.map(token =>
|
|
11
|
+
token.type === 'math_inline' || token.type === 'math_block'
|
|
12
|
+
? {...token, type: 'text', content: token.meta?.mathSource ?? token.content}
|
|
13
|
+
: token), options, env);
|
|
14
|
+
};
|
|
15
|
+
const matchers = new WeakMap();
|
|
16
|
+
function match(state, at, limit) {
|
|
17
|
+
let entry = matchers.get(state);
|
|
18
|
+
if (!entry || entry.source !== state.src) {
|
|
19
|
+
entry = {source: state.src, match: createMathMatcher(state.src, {singleDollar, brackets})};
|
|
20
|
+
matchers.set(state, entry);
|
|
21
|
+
}
|
|
22
|
+
return entry.match(at, limit);
|
|
23
|
+
}
|
|
24
|
+
md.inline.ruler.before('escape', ruleName, (state, silent) => {
|
|
25
|
+
if (!inLink && state.linkLevel > 0) return false;
|
|
26
|
+
if (state.src[state.pos] !== '$' && state.src[state.pos] !== '\\') return false;
|
|
27
|
+
const span = match(state, state.pos, state.posMax);
|
|
28
|
+
if (!span) return false;
|
|
29
|
+
if (!silent) {
|
|
30
|
+
const token = state.push(span.displayMode ? 'math_block' : 'math_inline', '', 0);
|
|
31
|
+
token.markup = span.open;
|
|
32
|
+
token.content = state.src.slice(span.bodyStart, span.bodyEnd);
|
|
33
|
+
token.meta = {mathSource: state.src.slice(span.start, span.end)};
|
|
34
|
+
}
|
|
35
|
+
state.pos = span.end;
|
|
36
|
+
return true;
|
|
37
|
+
});
|
|
38
|
+
md.block.ruler.before('fence', ruleName, (state, startLine, endLine, silent) => {
|
|
39
|
+
// An indented code block belongs to the host even when it begins with dollars.
|
|
40
|
+
if (state.sCount[startLine] - state.blkIndent >= 4) return false;
|
|
41
|
+
const start = state.bMarks[startLine] + state.tShift[startLine];
|
|
42
|
+
if (state.src.slice(start, start + 2) !== '$$' && (!brackets || state.src.slice(start, start + 2) !== '\\[')) return false;
|
|
43
|
+
const span = match(state, start, state.eMarks[endLine - 1]);
|
|
44
|
+
if (!span?.displayMode) return false;
|
|
45
|
+
let last = startLine;
|
|
46
|
+
while (last < endLine && state.eMarks[last] < span.end) last++;
|
|
47
|
+
if (last >= endLine || span.bodyEnd < state.bMarks[last] + state.tShift[last] ||
|
|
48
|
+
state.src.slice(span.end, state.eMarks[last]).trim()) return false;
|
|
49
|
+
if (silent) return true;
|
|
50
|
+
const parts = [];
|
|
51
|
+
for (let line = startLine; line <= last; line++) {
|
|
52
|
+
const from = line === startLine ? span.bodyStart : state.bMarks[line] + state.tShift[line];
|
|
53
|
+
const to = line === last ? span.bodyEnd : state.eMarks[line];
|
|
54
|
+
parts.push(state.src.slice(from, to));
|
|
55
|
+
}
|
|
56
|
+
const token = state.push('math_block', 'math', 0);
|
|
57
|
+
token.block = true;
|
|
58
|
+
token.markup = span.open;
|
|
59
|
+
token.content = parts.join('\n');
|
|
60
|
+
token.meta = {mathSource: span.open + token.content + span.close};
|
|
61
|
+
token.map = [startLine, last + 1];
|
|
62
|
+
state.line = last + 1;
|
|
63
|
+
return true;
|
|
64
|
+
}, {alt: ['paragraph', 'reference', 'blockquote', 'list']});
|
|
65
|
+
}
|
package/math-components.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"spdxVersion": "SPDX-2.3",
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
|
-
"name": "rapier-math-1.0
|
|
6
|
-
"documentNamespace": "https://rapier.website/spdx/rapier-math-1.0
|
|
5
|
+
"name": "rapier-math-1.1.0",
|
|
6
|
+
"documentNamespace": "https://rapier.website/spdx/rapier-math-1.1.0-22e5e40abbd1087922335893ad546fb7ffaeaa710338c97e5686e7b7b0b62021",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-10-
|
|
8
|
+
"created": "2026-10-12T15:47:04.000Z",
|
|
9
9
|
"creators": [
|
|
10
10
|
"Tool: rapier-math-components",
|
|
11
11
|
"Organization: rapier.website"
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
{
|
|
16
16
|
"name": "rapier-math",
|
|
17
17
|
"SPDXID": "SPDXRef-rapier-math",
|
|
18
|
-
"versionInfo": "1.0
|
|
19
|
-
"downloadLocation": "
|
|
18
|
+
"versionInfo": "1.1.0",
|
|
19
|
+
"downloadLocation": "NOASSERTION",
|
|
20
20
|
"filesAnalyzed": false,
|
|
21
21
|
"licenseConcluded": "MIT AND LicenseRef-GUST-Font-License",
|
|
22
22
|
"licenseDeclared": "MIT AND LicenseRef-GUST-Font-License",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"checksums": [
|
|
25
25
|
{
|
|
26
26
|
"algorithm": "SHA256",
|
|
27
|
-
"checksumValue": "
|
|
27
|
+
"checksumValue": "22e5e40abbd1087922335893ad546fb7ffaeaa710338c97e5686e7b7b0b62021"
|
|
28
28
|
}
|
|
29
29
|
]
|
|
30
30
|
},
|
package/package.json
CHANGED
|
@@ -1,17 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rapier-math",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Self-contained TeX-to-SVG math renderer with Latin Modern outlines.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|
|
7
|
+
"types": "./index.d.ts",
|
|
8
|
+
"sideEffects": [
|
|
9
|
+
"./rapier-math-1.1.0.js"
|
|
10
|
+
],
|
|
7
11
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./index.d.ts",
|
|
14
|
+
"default": "./index.mjs"
|
|
15
|
+
},
|
|
16
|
+
"./delimiters": {
|
|
17
|
+
"types": "./delimiters.d.ts",
|
|
18
|
+
"default": "./delimiters.mjs"
|
|
19
|
+
},
|
|
20
|
+
"./markdown-it": {
|
|
21
|
+
"types": "./markdown-it.d.ts",
|
|
22
|
+
"default": "./markdown-it.mjs"
|
|
23
|
+
},
|
|
24
|
+
"./rehype": {
|
|
25
|
+
"types": "./rehype.d.ts",
|
|
26
|
+
"default": "./rehype.mjs"
|
|
27
|
+
},
|
|
28
|
+
"./stream": {
|
|
29
|
+
"types": "./stream.d.ts",
|
|
30
|
+
"default": "./stream.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./rapier-math-1.1.0.js": {
|
|
33
|
+
"types": "./classic.d.ts",
|
|
34
|
+
"default": "./rapier-math-1.1.0.js"
|
|
35
|
+
},
|
|
10
36
|
"./package.json": "./package.json"
|
|
11
37
|
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"markdown-it": "^14.1.0 || ^15.0.0",
|
|
40
|
+
"@types/markdown-it": "^14.1.0",
|
|
41
|
+
"hast-util-from-html": "^2.0.0",
|
|
42
|
+
"@types/hast": "^3.0.0",
|
|
43
|
+
"vfile": "^6.0.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependenciesMeta": {
|
|
46
|
+
"markdown-it": {
|
|
47
|
+
"optional": true
|
|
48
|
+
},
|
|
49
|
+
"@types/markdown-it": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"hast-util-from-html": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"@types/hast": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"vfile": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
12
62
|
"files": [
|
|
13
63
|
"index.mjs",
|
|
14
|
-
"rapier-math-1.0.
|
|
64
|
+
"rapier-math-1.1.0.js",
|
|
15
65
|
"LICENSE",
|
|
16
66
|
"LICENSE-MIT",
|
|
17
67
|
"math-LICENSE.txt",
|
|
@@ -20,10 +70,33 @@
|
|
|
20
70
|
"README.md",
|
|
21
71
|
"verify.mjs",
|
|
22
72
|
"check.mjs",
|
|
73
|
+
"adoption-check.mjs",
|
|
23
74
|
"src/math",
|
|
24
75
|
"src/tools/build-math.mjs",
|
|
25
76
|
"src/shell/math-resources.json",
|
|
26
|
-
"src/package.json"
|
|
77
|
+
"src/package.json",
|
|
78
|
+
"delimiters.mjs",
|
|
79
|
+
"markdown-rules.mjs",
|
|
80
|
+
"integration.mjs",
|
|
81
|
+
"markdown-it.mjs",
|
|
82
|
+
"rehype.mjs",
|
|
83
|
+
"stream.mjs",
|
|
84
|
+
"index.d.ts",
|
|
85
|
+
"classic.d.ts",
|
|
86
|
+
"delimiters.d.ts",
|
|
87
|
+
"integration.d.ts",
|
|
88
|
+
"markdown-it.d.ts",
|
|
89
|
+
"rehype.d.ts",
|
|
90
|
+
"stream.d.ts",
|
|
91
|
+
"examples/formula.mjs",
|
|
92
|
+
"examples/markdown.mjs",
|
|
93
|
+
"examples/stream.mjs",
|
|
94
|
+
"examples/unified.mjs",
|
|
95
|
+
"examples/browser.html",
|
|
96
|
+
"examples/browser.mjs",
|
|
97
|
+
"examples/react.tsx",
|
|
98
|
+
"examples/worker.mjs",
|
|
99
|
+
"examples/README.md"
|
|
27
100
|
],
|
|
28
101
|
"license": "SEE LICENSE IN LICENSE",
|
|
29
102
|
"author": "Jack Skipworth",
|