astro-lilypond 0.15.0 → 0.16.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/dist/index.d.ts +28 -1
- package/dist/index.js +14 -4
- package/dist/plugins/rehype.js +5 -6
- package/dist/plugins/remark.js +5 -6
- package/dist/plugins/satteri.js +5 -6
- package/dist/render.d.ts +3 -25
- package/dist/render.js +0 -8
- package/dist/utils/{emitLilypondAsset.fake.d.ts → __tests__/emitLilypondAsset.fake.d.ts} +1 -1
- package/dist/utils/{emitLilypondPdfAsset.fake.d.ts → __tests__/emitLilypondPdfAsset.fake.d.ts} +1 -1
- package/dist/utils/parseLyHeader.js +1 -2
- package/dist/utils/resolveDefaults.js +1 -2
- package/package.json +3 -1
- /package/dist/utils/{emitLilypondAsset.fake.js → __tests__/emitLilypondAsset.fake.js} +0 -0
- /package/dist/utils/{emitLilypondPdfAsset.fake.js → __tests__/emitLilypondPdfAsset.fake.js} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface RenderOptions {
|
|
|
31
31
|
format?: "svg" | "png";
|
|
32
32
|
/**
|
|
33
33
|
* Crop `Score` to a single tightly-fit image instead of full pages.
|
|
34
|
-
* @default
|
|
34
|
+
* @default false
|
|
35
35
|
*/
|
|
36
36
|
crop?: boolean;
|
|
37
37
|
/**
|
|
@@ -54,11 +54,38 @@ export interface RenderResult {
|
|
|
54
54
|
meta: LilypondMetadata;
|
|
55
55
|
raw: string;
|
|
56
56
|
}
|
|
57
|
+
interface ScoreImageProps {
|
|
58
|
+
pageLimit?: number;
|
|
59
|
+
class?: string;
|
|
60
|
+
style?: string;
|
|
61
|
+
alt?: string;
|
|
62
|
+
}
|
|
57
63
|
/**
|
|
58
64
|
* Renders a `LilypondScore` (from a `.ly`/`.ily`/`.lilypond` import,
|
|
59
65
|
* or a `lilypondLoader()` entry) to a renderable `<Score />` component.
|
|
60
66
|
*/
|
|
61
67
|
export declare function render(score: LilypondScore, options?: RenderOptions): Promise<RenderResult>;
|
|
68
|
+
export interface ScoreProps extends ScoreImageProps {
|
|
69
|
+
/**
|
|
70
|
+
* A `LilypondScore` from a `.ly`/`.ily`/`.lilypond` import
|
|
71
|
+
* or a `lilypondLoader()` entry.
|
|
72
|
+
*/
|
|
73
|
+
content: LilypondScore;
|
|
74
|
+
/**
|
|
75
|
+
* @default "svg"
|
|
76
|
+
*/
|
|
77
|
+
format?: "svg" | "png";
|
|
78
|
+
/**
|
|
79
|
+
* @default false
|
|
80
|
+
*/
|
|
81
|
+
crop?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Renders a `LilypondScore` directly, for the common case where none of
|
|
85
|
+
* `render()`'s `pageCount`, `meta`, `raw`, or `pdf` are needed. Use
|
|
86
|
+
* `render()` instead when you need those.
|
|
87
|
+
*/
|
|
88
|
+
export declare const Score: AstroComponentFactory;
|
|
62
89
|
export declare function renderAll(scores: LilypondScore[], options?: RenderOptions): Promise<RenderResult[]>;
|
|
63
90
|
export interface LilypondOptions extends PluginOptions {
|
|
64
91
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createComponent, renderTemplate, unescapeHTML, } from "astro/runtime/server/index.js";
|
|
1
|
+
import { createComponent, renderComponent, renderTemplate, unescapeHTML, } from "astro/runtime/server/index.js";
|
|
2
2
|
import emitAssetIntegration from "astro-emit-asset";
|
|
3
3
|
import { rehypePlugin, remarkPlugin, satteriPlugin, } from "./plugins/index.js";
|
|
4
|
-
import { render as renderScore
|
|
4
|
+
import { render as renderScore } from "./render.js";
|
|
5
5
|
import { getRenderState, resolveAndSetRenderState } from "./renderState.js";
|
|
6
6
|
import { altTextFor, emitLilypondAsset, emitLilypondPdfAsset, includePathsFor, lyTypeDeclarationsFor, parseLyHeaderFields, prependVersion, renderedHtml, resolveDefaults, sourceNameFor, titleFor, toLilypondMetadata, } from "./utils/index.js";
|
|
7
7
|
export const LY_EXTENSIONS = [".ly", ".lilypond", ".ily"];
|
|
@@ -22,9 +22,9 @@ function createScoreComponent(content) {
|
|
|
22
22
|
*/
|
|
23
23
|
export async function render(score, options = {}) {
|
|
24
24
|
const state = getRenderState();
|
|
25
|
-
const { resolution,
|
|
25
|
+
const { resolution, cropScale } = resolveDefaults(state.defaults);
|
|
26
26
|
const format = options.format ?? "svg";
|
|
27
|
-
const crop = options.crop ??
|
|
27
|
+
const crop = options.crop ?? false;
|
|
28
28
|
const [{ Score, pageCount }, pdf] = await Promise.all([
|
|
29
29
|
(async () => {
|
|
30
30
|
const pages = await emitLilypondAsset({
|
|
@@ -69,6 +69,16 @@ export async function render(score, options = {}) {
|
|
|
69
69
|
]);
|
|
70
70
|
return { Score, pageCount, pdf, meta: score.meta, raw: score.source };
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Renders a `LilypondScore` directly, for the common case where none of
|
|
74
|
+
* `render()`'s `pageCount`, `meta`, `raw`, or `pdf` are needed. Use
|
|
75
|
+
* `render()` instead when you need those.
|
|
76
|
+
*/
|
|
77
|
+
export const Score = createComponent(async (result, props) => {
|
|
78
|
+
const { content, format, crop, ...imageProps } = props;
|
|
79
|
+
const { Score: ContentScore } = await render(content, { format, crop });
|
|
80
|
+
return renderTemplate `${renderComponent(result, "Score", ContentScore, imageProps)}`;
|
|
81
|
+
});
|
|
72
82
|
export async function renderAll(scores, options = {}) {
|
|
73
83
|
return Promise.all(scores.map((score) => render(score, options)));
|
|
74
84
|
}
|
package/dist/plugins/rehype.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { visit } from "unist-util-visit";
|
|
2
|
-
import { defaultOptions, render
|
|
2
|
+
import { defaultOptions, render } from "../render.js";
|
|
3
3
|
import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
|
|
4
4
|
// Typed loosely so it's assignable to both RehypePlugin and the unified
|
|
5
5
|
// Plugin generic regardless of which @types/hast version the host project pins.
|
|
@@ -30,22 +30,21 @@ export function rehypePlugin(options) {
|
|
|
30
30
|
.filter((c) => c.type === "text")
|
|
31
31
|
.map((c) => c.value)
|
|
32
32
|
.join("");
|
|
33
|
-
const { version, resolution,
|
|
33
|
+
const { version, resolution, cropScale } = resolveDefaults(options.defaults);
|
|
34
34
|
const source = version ? prependVersion(raw, version) : raw;
|
|
35
35
|
const format = options.format ?? defaultOptions.format;
|
|
36
|
-
const crop = resolveCrop(cropSetting, "markdown");
|
|
37
36
|
const alt = altTextForBlock(codeNode.data?.meta, raw);
|
|
38
37
|
const promise = emitLilypondAsset({
|
|
39
38
|
title,
|
|
40
39
|
format,
|
|
41
40
|
source,
|
|
42
41
|
resolution,
|
|
43
|
-
crop,
|
|
44
|
-
sizeScale:
|
|
42
|
+
crop: true,
|
|
43
|
+
sizeScale: cropScale,
|
|
45
44
|
binaryPath: options.binaryPath,
|
|
46
45
|
render: () => render(source, {
|
|
47
46
|
format,
|
|
48
|
-
crop,
|
|
47
|
+
crop: true,
|
|
49
48
|
defaults: options.defaults,
|
|
50
49
|
timeout: options.timeout,
|
|
51
50
|
binaryPath: options.binaryPath,
|
package/dist/plugins/remark.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { visit } from "unist-util-visit";
|
|
2
|
-
import { defaultOptions, render
|
|
2
|
+
import { defaultOptions, render } from "../render.js";
|
|
3
3
|
import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
|
|
4
4
|
export const remarkPlugin = (options) => {
|
|
5
5
|
return async (tree, file) => {
|
|
@@ -10,22 +10,21 @@ export const remarkPlugin = (options) => {
|
|
|
10
10
|
visit(tree, "code", (node, index, parent) => {
|
|
11
11
|
if (!isLilypondLang(node.lang) || index === undefined || !parent)
|
|
12
12
|
return;
|
|
13
|
-
const { version, resolution,
|
|
13
|
+
const { version, resolution, cropScale } = resolveDefaults(options.defaults);
|
|
14
14
|
const source = version ? prependVersion(node.value, version) : node.value;
|
|
15
15
|
const format = options.format ?? defaultOptions.format;
|
|
16
|
-
const crop = resolveCrop(cropSetting, "markdown");
|
|
17
16
|
const alt = altTextForBlock(node.meta, node.value);
|
|
18
17
|
const promise = emitLilypondAsset({
|
|
19
18
|
title,
|
|
20
19
|
format,
|
|
21
20
|
source,
|
|
22
21
|
resolution,
|
|
23
|
-
crop,
|
|
24
|
-
sizeScale:
|
|
22
|
+
crop: true,
|
|
23
|
+
sizeScale: cropScale,
|
|
25
24
|
binaryPath: options.binaryPath,
|
|
26
25
|
render: () => render(source, {
|
|
27
26
|
format,
|
|
28
|
-
crop,
|
|
27
|
+
crop: true,
|
|
29
28
|
defaults: options.defaults,
|
|
30
29
|
timeout: options.timeout,
|
|
31
30
|
binaryPath: options.binaryPath,
|
package/dist/plugins/satteri.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defaultOptions, render
|
|
1
|
+
import { defaultOptions, render } from "../render.js";
|
|
2
2
|
import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
|
|
3
3
|
export function satteriPlugin(options) {
|
|
4
4
|
return {
|
|
@@ -9,25 +9,24 @@ export function satteriPlugin(options) {
|
|
|
9
9
|
async code(node, ctx) {
|
|
10
10
|
if (!isLilypondLang(node.lang))
|
|
11
11
|
return undefined;
|
|
12
|
-
const { version, resolution,
|
|
12
|
+
const { version, resolution, cropScale } = resolveDefaults(options.defaults);
|
|
13
13
|
const source = version ? prependVersion(node.value, version) : node.value;
|
|
14
14
|
const format = options.format ?? defaultOptions.format;
|
|
15
15
|
const includePaths = includePathsFor(ctx.fileURL);
|
|
16
16
|
const sourceName = sourceNameFor(ctx.fileURL);
|
|
17
17
|
const title = titleFor(sourceName);
|
|
18
|
-
const crop = resolveCrop(cropSetting, "markdown");
|
|
19
18
|
const alt = altTextForBlock(node.meta, node.value);
|
|
20
19
|
const pages = await emitLilypondAsset({
|
|
21
20
|
title,
|
|
22
21
|
format,
|
|
23
22
|
source,
|
|
24
23
|
resolution,
|
|
25
|
-
crop,
|
|
26
|
-
sizeScale:
|
|
24
|
+
crop: true,
|
|
25
|
+
sizeScale: cropScale,
|
|
27
26
|
binaryPath: options.binaryPath,
|
|
28
27
|
render: () => render(source, {
|
|
29
28
|
format,
|
|
30
|
-
crop,
|
|
29
|
+
crop: true,
|
|
31
30
|
defaults: options.defaults,
|
|
32
31
|
timeout: options.timeout,
|
|
33
32
|
binaryPath: options.binaryPath,
|
package/dist/render.d.ts
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import type { LilypondVersion } from "./types/lilypondVersion.js";
|
|
2
2
|
export declare const FORMATS: readonly ["png", "svg", "pdf"];
|
|
3
3
|
export type Format = (typeof FORMATS)[number];
|
|
4
|
-
/**
|
|
5
|
-
* Controls whether rendered output is cropped tightly to the content
|
|
6
|
-
* bounding box, or left as full, potentially multi-page, output.
|
|
7
|
-
*
|
|
8
|
-
* - `true` — crop everywhere
|
|
9
|
-
* - `false` — never crop by default
|
|
10
|
-
* - `"markdown-only"` — crop Markdown fences, but leave `render()` uncropped by default
|
|
11
|
-
*/
|
|
12
|
-
export type CropSetting = boolean | "markdown-only";
|
|
13
|
-
/** Which kind of consumer is resolving a `CropSetting` — see `resolveCrop`. */
|
|
14
|
-
export type CropContext = "markdown" | "component";
|
|
15
|
-
/**
|
|
16
|
-
* Resolves the three-way `CropSetting` into the plain boolean a given
|
|
17
|
-
* consumer needs, per the semantics documented on `CropSetting`.
|
|
18
|
-
*/
|
|
19
|
-
export declare function resolveCrop(cropSetting: CropSetting, context: CropContext): boolean;
|
|
20
4
|
/**
|
|
21
5
|
* Defaults passed to each score for rendering.
|
|
22
6
|
*/
|
|
@@ -32,12 +16,6 @@ export interface LilypondDefaults {
|
|
|
32
16
|
* @default 144
|
|
33
17
|
*/
|
|
34
18
|
resolution?: number;
|
|
35
|
-
/**
|
|
36
|
-
* Crop the output tightly to the content bounding box, producing one
|
|
37
|
-
* continuous image instead of paginated output.
|
|
38
|
-
* @default "markdown-only"
|
|
39
|
-
*/
|
|
40
|
-
crop?: CropSetting;
|
|
41
19
|
/**
|
|
42
20
|
* Multiplies the `width`/`height` on a cropped score's `<img>` tag.
|
|
43
21
|
* Helps compensate for LilyPond's internal size units (points/mm)
|
|
@@ -50,10 +28,10 @@ export interface LilypondDefaults {
|
|
|
50
28
|
}
|
|
51
29
|
/**
|
|
52
30
|
* The subset of `LilypondDefaults` that `render()` itself reads. `version`
|
|
53
|
-
*
|
|
54
|
-
*
|
|
31
|
+
* is resolved by the caller before reaching `render()`, by prepending it to
|
|
32
|
+
* source text.
|
|
55
33
|
*/
|
|
56
|
-
export type RenderDefaults = Omit<LilypondDefaults, "version"
|
|
34
|
+
export type RenderDefaults = Omit<LilypondDefaults, "version">;
|
|
57
35
|
export interface RenderOptions {
|
|
58
36
|
/**
|
|
59
37
|
* Output format.
|
package/dist/render.js
CHANGED
|
@@ -5,13 +5,6 @@ import { execLilyPond } from "./execLilyPond.js";
|
|
|
5
5
|
import { readOutputFile, safeInputFileName } from "./readOutputFile.js";
|
|
6
6
|
import { resolveDefaults } from "./utils/resolveDefaults.js";
|
|
7
7
|
export const FORMATS = ["png", "svg", "pdf"];
|
|
8
|
-
/**
|
|
9
|
-
* Resolves the three-way `CropSetting` into the plain boolean a given
|
|
10
|
-
* consumer needs, per the semantics documented on `CropSetting`.
|
|
11
|
-
*/
|
|
12
|
-
export function resolveCrop(cropSetting, context) {
|
|
13
|
-
return context === "markdown" ? cropSetting !== false : cropSetting === true;
|
|
14
|
-
}
|
|
15
8
|
export const defaultOptions = {
|
|
16
9
|
format: "svg",
|
|
17
10
|
crop: true,
|
|
@@ -20,7 +13,6 @@ export const defaultOptions = {
|
|
|
20
13
|
defaults: {
|
|
21
14
|
version: "2.26.0",
|
|
22
15
|
resolution: 144,
|
|
23
|
-
crop: "markdown-only",
|
|
24
16
|
cropScale: 1.5,
|
|
25
17
|
},
|
|
26
18
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { vi } from "vitest";
|
|
2
|
-
import type { emitLilypondAsset } from "
|
|
2
|
+
import type { emitLilypondAsset } from "../emitLilypondAsset.js";
|
|
3
3
|
type EmitLilypondAssetMock = ReturnType<typeof vi.mocked<typeof emitLilypondAsset>>;
|
|
4
4
|
export declare function fakeEmitLilypondAsset(mock: EmitLilypondAssetMock, urlBase?: string): void;
|
|
5
5
|
export declare function fakeEmitLilypondAssetPropagatingRenderErrors(mock: EmitLilypondAssetMock): void;
|
package/dist/utils/{emitLilypondPdfAsset.fake.d.ts → __tests__/emitLilypondPdfAsset.fake.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { vi } from "vitest";
|
|
2
|
-
import type { emitLilypondPdfAsset } from "
|
|
2
|
+
import type { emitLilypondPdfAsset } from "../emitLilypondPdfAsset.js";
|
|
3
3
|
type EmitLilypondPdfAssetMock = ReturnType<typeof vi.mocked<typeof emitLilypondPdfAsset>>;
|
|
4
4
|
export declare function fakeEmitLilypondPdfAsset(mock: EmitLilypondPdfAssetMock, urlBase?: string): void;
|
|
5
5
|
export {};
|
|
@@ -209,9 +209,8 @@ function parseFieldValue(body, start) {
|
|
|
209
209
|
i++;
|
|
210
210
|
if (body[i] !== "{")
|
|
211
211
|
return null;
|
|
212
|
+
// body is itself a properly-nested brace sequence (see allHeaderBodies), so this `{` always has a matching `}`.
|
|
212
213
|
const end = matchBalancedBraces(body, i);
|
|
213
|
-
if (end === -1)
|
|
214
|
-
return null;
|
|
215
214
|
const value = extractMarkupText(body.slice(i + 1, end));
|
|
216
215
|
return value ? { value, end: end + 1 } : null;
|
|
217
216
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { defaultOptions } from "../render.js";
|
|
2
2
|
/** Fills in any `defaults` fields left unset with `render.ts`'s own defaults. */
|
|
3
3
|
export function resolveDefaults(defaults) {
|
|
4
|
-
const { version, resolution,
|
|
4
|
+
const { version, resolution, cropScale } = defaultOptions.defaults;
|
|
5
5
|
return {
|
|
6
6
|
version: defaults?.version ?? version,
|
|
7
7
|
resolution: defaults?.resolution ?? resolution,
|
|
8
|
-
crop: defaults?.crop ?? crop,
|
|
9
8
|
cropScale: defaults?.cropScale ?? cropScale,
|
|
10
9
|
};
|
|
11
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-lilypond",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "An Astro integration for rendering Lilypond markup to music notation.",
|
|
5
5
|
"homepage": "https://lilypond.ky.fyi",
|
|
6
6
|
"author": "Ky Decker",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"@astrojs/markdown-satteri": "^0.3.4",
|
|
55
55
|
"@types/mdast": "^4.0.4",
|
|
56
56
|
"@types/node": "^26.1.1",
|
|
57
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
57
58
|
"astro": "^7.0.8",
|
|
58
59
|
"satteri": "^0.9.5",
|
|
59
60
|
"typescript": "^7.0.2",
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
"scripts": {
|
|
65
66
|
"build": "tsc --project tsconfig.build.json",
|
|
66
67
|
"test": "vitest --project unit",
|
|
68
|
+
"test:coverage": "vitest run --project unit --coverage",
|
|
67
69
|
"test:integration": "vitest run --project integration"
|
|
68
70
|
}
|
|
69
71
|
}
|
|
File without changes
|
|
File without changes
|