astro-lilypond 0.16.0 → 0.16.2
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 +1 -1
- package/dist/index.js +2 -6
- package/dist/loader.d.ts +27 -23
- package/dist/loader.js +20 -21
- package/dist/plugins/index.d.ts +0 -1
- package/dist/plugins/index.js +0 -1
- package/dist/plugins/satteri.js +1 -7
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +1 -1
- package/dist/utils/lilypondMetadata.d.ts +14 -5
- package/dist/utils/lilypondMetadata.js +0 -15
- package/package.json +4 -4
- package/dist/plugins/rehype.d.ts +0 -4
- package/dist/plugins/rehype.js +0 -65
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
An [Astro](https://astro.build) integration for rendering [LilyPond](https://lilypond.org) music notation to images.
|
|
4
4
|
|
|
5
5
|
- Render musical scores via Markdown or with an Astro component.
|
|
6
|
-
- Works with remark
|
|
6
|
+
- Works with remark and satteri Markdown processors out-of-the-box.
|
|
7
7
|
- Zero client-side JavaScript! Images are compiled at build time.
|
|
8
8
|
- Automatic `alt` text generated from the score’s title and composer. (Or supply your own.)
|
|
9
9
|
- Use the content loader to pass in a folder full of `.ly` files and get back an Astro content collection.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createComponent, renderComponent, renderTemplate, unescapeHTML, } from "astro/runtime/server/index.js";
|
|
2
2
|
import emitAssetIntegration from "astro-emit-asset";
|
|
3
|
-
import {
|
|
3
|
+
import { remarkPlugin, satteriPlugin, } from "./plugins/index.js";
|
|
4
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";
|
|
@@ -162,14 +162,10 @@ export default function lilypond(options = {}) {
|
|
|
162
162
|
...(existingOptions.remarkPlugins ?? []),
|
|
163
163
|
[remarkPlugin, options],
|
|
164
164
|
],
|
|
165
|
-
rehypePlugins: [
|
|
166
|
-
...(existingOptions.rehypePlugins ?? []),
|
|
167
|
-
[rehypePlugin, options],
|
|
168
|
-
],
|
|
169
165
|
}),
|
|
170
166
|
},
|
|
171
167
|
});
|
|
172
|
-
logger?.info("astro-lilypond: registered unified remark
|
|
168
|
+
logger?.info("astro-lilypond: registered unified remark plugin");
|
|
173
169
|
return;
|
|
174
170
|
}
|
|
175
171
|
throw new Error("astro-lilypond requires a processor-based Astro markdown config. " +
|
package/dist/loader.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LoaderContext } from "astro/loaders";
|
|
2
2
|
import { z } from "astro/zod";
|
|
3
|
-
import type { AutoInstallOptions } from "./binary/index.js";
|
|
4
3
|
import { type LilypondScore } from "./index.js";
|
|
5
4
|
import type { LilypondDefaults } from "./render.js";
|
|
6
5
|
export interface GenerateIdOptions {
|
|
@@ -32,27 +31,7 @@ export interface LilypondLoaderOptions {
|
|
|
32
31
|
* collection's entries.
|
|
33
32
|
*/
|
|
34
33
|
defaults?: LilypondDefaults;
|
|
35
|
-
/**
|
|
36
|
-
* Milliseconds to wait for a single `lilypond` invocation before
|
|
37
|
-
* aborting it, when `render()` is later called on one of this
|
|
38
|
-
* collection's entries.
|
|
39
|
-
* @default 60000
|
|
40
|
-
*/
|
|
41
|
-
timeout?: number;
|
|
42
|
-
/**
|
|
43
|
-
* When no `lilypond` binary is found on `PATH`, download a matching
|
|
44
|
-
* prebuilt release into a local cache and use that instead. Set to
|
|
45
|
-
* `false` to only ever use a `PATH` install, or pass an object to pick
|
|
46
|
-
* which version gets downloaded.
|
|
47
|
-
* @default true
|
|
48
|
-
*/
|
|
49
|
-
autoInstall?: boolean | AutoInstallOptions;
|
|
50
34
|
}
|
|
51
|
-
/**
|
|
52
|
-
* A collection entry is itself a `LilypondScore` (its `meta` carries the
|
|
53
|
-
* file's `\header` fields) — pass `entry.data` directly to the exported
|
|
54
|
-
* `render()`, exactly as you would a plain `.ly` import.
|
|
55
|
-
*/
|
|
56
35
|
export type LilypondCollectionEntry = LilypondScore;
|
|
57
36
|
export declare const lilypondEntrySchema: z.ZodObject<{
|
|
58
37
|
source: z.ZodString;
|
|
@@ -79,4 +58,29 @@ export declare const lilypondEntrySchema: z.ZodObject<{
|
|
|
79
58
|
/**
|
|
80
59
|
* Content Loader to turn a directory of `.ly` files into a content collection.
|
|
81
60
|
*/
|
|
82
|
-
export declare function lilypondLoader(
|
|
61
|
+
export declare function lilypondLoader({ pattern, base, generateId, defaults, }: LilypondLoaderOptions): {
|
|
62
|
+
name: string;
|
|
63
|
+
schema: z.ZodObject<{
|
|
64
|
+
source: z.ZodString;
|
|
65
|
+
alt: z.ZodString;
|
|
66
|
+
sourceName: z.ZodOptional<z.ZodString>;
|
|
67
|
+
includePaths: z.ZodArray<z.ZodString>;
|
|
68
|
+
assetTitle: z.ZodString;
|
|
69
|
+
meta: z.ZodObject<{
|
|
70
|
+
arranger: z.ZodOptional<z.ZodString>;
|
|
71
|
+
composer: z.ZodOptional<z.ZodString>;
|
|
72
|
+
copyright: z.ZodOptional<z.ZodString>;
|
|
73
|
+
dedication: z.ZodOptional<z.ZodString>;
|
|
74
|
+
instrument: z.ZodOptional<z.ZodString>;
|
|
75
|
+
meter: z.ZodOptional<z.ZodString>;
|
|
76
|
+
opus: z.ZodOptional<z.ZodString>;
|
|
77
|
+
piece: z.ZodOptional<z.ZodString>;
|
|
78
|
+
poet: z.ZodOptional<z.ZodString>;
|
|
79
|
+
subsubtitle: z.ZodOptional<z.ZodString>;
|
|
80
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
81
|
+
tagline: z.ZodOptional<z.ZodString>;
|
|
82
|
+
title: z.ZodOptional<z.ZodString>;
|
|
83
|
+
}, z.core.$catchall<z.ZodString>>;
|
|
84
|
+
}, z.core.$strip>;
|
|
85
|
+
load(context: LoaderContext): Promise<void>;
|
|
86
|
+
};
|
package/dist/loader.js
CHANGED
|
@@ -3,23 +3,32 @@ import { join, relative, sep } from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { z } from "astro/zod";
|
|
5
5
|
import { LY_EXTENSIONS } from "./index.js";
|
|
6
|
-
import {
|
|
7
|
-
import { altTextFor, includePathsFor, parseLyHeaderFields, prependVersion, resolveDefaults, STANDARD_HEADER_FIELDS, sourceNameFor, titleFor, toLilypondMetadata, } from "./utils/index.js";
|
|
6
|
+
import { altTextFor, includePathsFor, parseLyHeaderFields, prependVersion, resolveDefaults, sourceNameFor, titleFor, toLilypondMetadata, } from "./utils/index.js";
|
|
8
7
|
const DEFAULT_PATTERN = `**/*.{${LY_EXTENSIONS.map((ext) => ext.slice(1)).join(",")}}`;
|
|
9
|
-
//
|
|
10
|
-
// derived from, so the two can't drift. `.catchall()` lets any other header
|
|
11
|
-
// field (e.g. `mutopiacomposer`) sit alongside the standard ones, typed as
|
|
12
|
-
// `string` — matching `LilypondMetadata`'s index signature.
|
|
13
|
-
const lilypondMetadataSchema = z
|
|
14
|
-
.object(Object.fromEntries(STANDARD_HEADER_FIELDS.map((field) => [field, z.string().optional()])))
|
|
15
|
-
.catchall(z.string());
|
|
8
|
+
// Field names must match `LilypondMetadata` in `utils/lilypondMetadata.ts`.
|
|
16
9
|
export const lilypondEntrySchema = z.object({
|
|
17
10
|
source: z.string(),
|
|
18
11
|
alt: z.string(),
|
|
19
12
|
sourceName: z.string().optional(),
|
|
20
13
|
includePaths: z.array(z.string()),
|
|
21
14
|
assetTitle: z.string(),
|
|
22
|
-
meta:
|
|
15
|
+
meta: z
|
|
16
|
+
.object({
|
|
17
|
+
arranger: z.string().optional(),
|
|
18
|
+
composer: z.string().optional(),
|
|
19
|
+
copyright: z.string().optional(),
|
|
20
|
+
dedication: z.string().optional(),
|
|
21
|
+
instrument: z.string().optional(),
|
|
22
|
+
meter: z.string().optional(),
|
|
23
|
+
opus: z.string().optional(),
|
|
24
|
+
piece: z.string().optional(),
|
|
25
|
+
poet: z.string().optional(),
|
|
26
|
+
subsubtitle: z.string().optional(),
|
|
27
|
+
subtitle: z.string().optional(),
|
|
28
|
+
tagline: z.string().optional(),
|
|
29
|
+
title: z.string().optional(),
|
|
30
|
+
})
|
|
31
|
+
.catchall(z.string()),
|
|
23
32
|
});
|
|
24
33
|
function stripLyExtension(entryPath) {
|
|
25
34
|
for (const ext of LY_EXTENSIONS) {
|
|
@@ -46,8 +55,7 @@ function posixRelative(from, to) {
|
|
|
46
55
|
/**
|
|
47
56
|
* Content Loader to turn a directory of `.ly` files into a content collection.
|
|
48
57
|
*/
|
|
49
|
-
export function lilypondLoader(
|
|
50
|
-
const { pattern = DEFAULT_PATTERN, base, generateId = defaultGenerateId, defaults, timeout, autoInstall, } = options;
|
|
58
|
+
export function lilypondLoader({ pattern = DEFAULT_PATTERN, base, generateId = defaultGenerateId, defaults, }) {
|
|
51
59
|
const resolved = resolveDefaults(defaults);
|
|
52
60
|
return {
|
|
53
61
|
name: "astro-lilypond-loader",
|
|
@@ -57,15 +65,6 @@ export function lilypondLoader(options) {
|
|
|
57
65
|
const rootDir = fileURLToPath(config.root);
|
|
58
66
|
const baseUrl = resolveBaseUrl(base, config.root);
|
|
59
67
|
const baseDir = fileURLToPath(baseUrl);
|
|
60
|
-
// Populates the shared render() state, so this collection's entries
|
|
61
|
-
// can be rendered even when the `lilypond()` integration itself
|
|
62
|
-
// isn't registered.
|
|
63
|
-
await resolveAndSetRenderState({
|
|
64
|
-
autoInstall,
|
|
65
|
-
defaults,
|
|
66
|
-
timeout,
|
|
67
|
-
logger,
|
|
68
|
-
});
|
|
69
68
|
async function syncEntry(entry) {
|
|
70
69
|
const filePath = join(baseDir, entry);
|
|
71
70
|
let source;
|
package/dist/plugins/index.d.ts
CHANGED
package/dist/plugins/index.js
CHANGED
package/dist/plugins/satteri.js
CHANGED
|
@@ -3,9 +3,6 @@ import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, pr
|
|
|
3
3
|
export function satteriPlugin(options) {
|
|
4
4
|
return {
|
|
5
5
|
name: "astro-lilypond",
|
|
6
|
-
// Returning an mdast Html node (type: 'html') emits the value verbatim.
|
|
7
|
-
// Sätteri's { rawHtml } escape hatch applies MDX brace-escaping which
|
|
8
|
-
// would corrupt SVG content, so we use the plain html node form instead.
|
|
9
6
|
async code(node, ctx) {
|
|
10
7
|
if (!isLilypondLang(node.lang))
|
|
11
8
|
return undefined;
|
|
@@ -34,10 +31,7 @@ export function satteriPlugin(options) {
|
|
|
34
31
|
sourceName,
|
|
35
32
|
}),
|
|
36
33
|
});
|
|
37
|
-
return {
|
|
38
|
-
type: "html",
|
|
39
|
-
value: renderedHtml(pages, alt),
|
|
40
|
-
};
|
|
34
|
+
return { rawHtml: renderedHtml(pages, alt) };
|
|
41
35
|
},
|
|
42
36
|
};
|
|
43
37
|
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export type { ImageDimensions } from "./imageDimensions.js";
|
|
|
7
7
|
export { imageDimensionsFor } from "./imageDimensions.js";
|
|
8
8
|
export { includePathsFor } from "./includePathsFor.js";
|
|
9
9
|
export { isLilypondLang } from "./isLilypondLang.js";
|
|
10
|
-
export type {
|
|
11
|
-
export {
|
|
10
|
+
export type { LilypondMetadata } from "./lilypondMetadata.js";
|
|
11
|
+
export { toLilypondMetadata } from "./lilypondMetadata.js";
|
|
12
12
|
export { lyTypeDeclarationsFor } from "./lyTypeDeclarationsFor.js";
|
|
13
13
|
export { parseFenceMeta } from "./parseFenceMeta.js";
|
|
14
14
|
export type { LyHeaderFields } from "./parseLyHeader.js";
|
package/dist/utils/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { escapeHtmlAttribute } from "./escapeHtmlAttribute.js";
|
|
|
6
6
|
export { imageDimensionsFor } from "./imageDimensions.js";
|
|
7
7
|
export { includePathsFor } from "./includePathsFor.js";
|
|
8
8
|
export { isLilypondLang } from "./isLilypondLang.js";
|
|
9
|
-
export {
|
|
9
|
+
export { toLilypondMetadata } from "./lilypondMetadata.js";
|
|
10
10
|
export { lyTypeDeclarationsFor } from "./lyTypeDeclarationsFor.js";
|
|
11
11
|
export { parseFenceMeta } from "./parseFenceMeta.js";
|
|
12
12
|
export { extractMarkupText, parseLyHeader, parseLyHeaderFields, } from "./parseLyHeader.js";
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
export declare const STANDARD_HEADER_FIELDS: readonly ["arranger", "composer", "copyright", "dedication", "instrument", "meter", "opus", "piece", "poet", "subsubtitle", "subtitle", "tagline", "title"];
|
|
2
|
-
export type KnownLyHeaderFields = {
|
|
3
|
-
[K in (typeof STANDARD_HEADER_FIELDS)[number]]?: string;
|
|
4
|
-
};
|
|
5
1
|
/** Header metadata extracted from a score's `\header` block(s). */
|
|
6
|
-
export interface LilypondMetadata
|
|
2
|
+
export interface LilypondMetadata {
|
|
3
|
+
arranger?: string;
|
|
4
|
+
composer?: string;
|
|
5
|
+
copyright?: string;
|
|
6
|
+
dedication?: string;
|
|
7
|
+
instrument?: string;
|
|
8
|
+
meter?: string;
|
|
9
|
+
opus?: string;
|
|
10
|
+
piece?: string;
|
|
11
|
+
poet?: string;
|
|
12
|
+
subsubtitle?: string;
|
|
13
|
+
subtitle?: string;
|
|
14
|
+
tagline?: string;
|
|
15
|
+
title?: string;
|
|
7
16
|
[field: string]: string | undefined;
|
|
8
17
|
}
|
|
9
18
|
/** Tags a bag of parsed `\header` fields (see `parseLyHeaderFields`) as `LilypondMetadata`. */
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
export const STANDARD_HEADER_FIELDS = [
|
|
2
|
-
"arranger",
|
|
3
|
-
"composer",
|
|
4
|
-
"copyright",
|
|
5
|
-
"dedication",
|
|
6
|
-
"instrument",
|
|
7
|
-
"meter",
|
|
8
|
-
"opus",
|
|
9
|
-
"piece",
|
|
10
|
-
"poet",
|
|
11
|
-
"subsubtitle",
|
|
12
|
-
"subtitle",
|
|
13
|
-
"tagline",
|
|
14
|
-
"title",
|
|
15
|
-
];
|
|
16
1
|
/** Tags a bag of parsed `\header` fields (see `parseLyHeaderFields`) as `LilypondMetadata`. */
|
|
17
2
|
export function toLilypondMetadata(fields) {
|
|
18
3
|
return fields;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-lilypond",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
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",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"unist-util-visit": "^5.1.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@astrojs/markdown-remark": "^7.2.
|
|
54
|
-
"@astrojs/markdown-satteri": "^0.3.
|
|
53
|
+
"@astrojs/markdown-remark": "^7.2.2",
|
|
54
|
+
"@astrojs/markdown-satteri": "^0.3.5",
|
|
55
55
|
"@types/mdast": "^4.0.4",
|
|
56
56
|
"@types/node": "^26.1.1",
|
|
57
57
|
"@vitest/coverage-v8": "^4.1.10",
|
|
58
|
-
"astro": "^7.
|
|
58
|
+
"astro": "^7.1.6",
|
|
59
59
|
"satteri": "^0.9.5",
|
|
60
60
|
"typescript": "^7.0.2",
|
|
61
61
|
"unified": "^11.0.5",
|
package/dist/plugins/rehype.d.ts
DELETED
package/dist/plugins/rehype.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { visit } from "unist-util-visit";
|
|
2
|
-
import { defaultOptions, render } from "../render.js";
|
|
3
|
-
import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
|
|
4
|
-
// Typed loosely so it's assignable to both RehypePlugin and the unified
|
|
5
|
-
// Plugin generic regardless of which @types/hast version the host project pins.
|
|
6
|
-
export function rehypePlugin(options) {
|
|
7
|
-
return async (tree, file) => {
|
|
8
|
-
const promises = [];
|
|
9
|
-
const includePaths = includePathsFor(file?.path);
|
|
10
|
-
const sourceName = sourceNameFor(file?.path);
|
|
11
|
-
const title = titleFor(sourceName);
|
|
12
|
-
visit(tree, "element", (node, index, parent) => {
|
|
13
|
-
if (node.tagName !== "pre" ||
|
|
14
|
-
node.children?.length !== 1 ||
|
|
15
|
-
node.children[0].type !== "element" ||
|
|
16
|
-
node.children[0].tagName !== "code" ||
|
|
17
|
-
index === undefined ||
|
|
18
|
-
!parent)
|
|
19
|
-
return;
|
|
20
|
-
const codeNode = node.children[0];
|
|
21
|
-
const cls = codeNode.properties?.className;
|
|
22
|
-
if (!Array.isArray(cls))
|
|
23
|
-
return;
|
|
24
|
-
const lang = cls
|
|
25
|
-
.find((c) => c.startsWith("language-"))
|
|
26
|
-
?.slice("language-".length);
|
|
27
|
-
if (!isLilypondLang(lang))
|
|
28
|
-
return;
|
|
29
|
-
const raw = codeNode.children
|
|
30
|
-
.filter((c) => c.type === "text")
|
|
31
|
-
.map((c) => c.value)
|
|
32
|
-
.join("");
|
|
33
|
-
const { version, resolution, cropScale } = resolveDefaults(options.defaults);
|
|
34
|
-
const source = version ? prependVersion(raw, version) : raw;
|
|
35
|
-
const format = options.format ?? defaultOptions.format;
|
|
36
|
-
const alt = altTextForBlock(codeNode.data?.meta, raw);
|
|
37
|
-
const promise = emitLilypondAsset({
|
|
38
|
-
title,
|
|
39
|
-
format,
|
|
40
|
-
source,
|
|
41
|
-
resolution,
|
|
42
|
-
crop: true,
|
|
43
|
-
sizeScale: cropScale,
|
|
44
|
-
binaryPath: options.binaryPath,
|
|
45
|
-
render: () => render(source, {
|
|
46
|
-
format,
|
|
47
|
-
crop: true,
|
|
48
|
-
defaults: options.defaults,
|
|
49
|
-
timeout: options.timeout,
|
|
50
|
-
binaryPath: options.binaryPath,
|
|
51
|
-
includePaths,
|
|
52
|
-
sourceName,
|
|
53
|
-
}),
|
|
54
|
-
}).then((pages) => {
|
|
55
|
-
const rawNode = {
|
|
56
|
-
type: "raw",
|
|
57
|
-
value: renderedHtml(pages, alt),
|
|
58
|
-
};
|
|
59
|
-
parent.children[index] = rawNode;
|
|
60
|
-
});
|
|
61
|
-
promises.push(promise);
|
|
62
|
-
});
|
|
63
|
-
await Promise.all(promises);
|
|
64
|
-
};
|
|
65
|
-
}
|