astro-lilypond 0.16.2 → 0.17.1
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/binary/downloadLilypond.js +4 -4
- package/dist/binary/resolveLilypondBinary.js +2 -2
- package/dist/execLilyPond.d.ts +2 -0
- package/dist/execLilyPond.js +11 -6
- package/dist/index.js +71 -43
- package/dist/loader.d.ts +1 -7
- package/dist/loader.js +7 -6
- package/dist/plugins/remark.js +36 -24
- package/dist/plugins/satteri.js +29 -17
- package/dist/plugins/types.d.ts +3 -0
- package/dist/render.d.ts +8 -3
- package/dist/render.js +3 -2
- package/dist/renderState.d.ts +5 -4
- package/dist/renderState.js +2 -0
- package/dist/utils/escapeHtml.d.ts +4 -0
- package/dist/utils/escapeHtml.js +9 -0
- package/dist/utils/escapeHtmlAttribute.d.ts +2 -4
- package/dist/utils/escapeHtmlAttribute.js +2 -4
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/renderedErrorHtml.d.ts +1 -0
- package/dist/utils/renderedErrorHtml.js +22 -0
- package/package.json +4 -1
|
@@ -60,7 +60,7 @@ export async function downloadLilypond({ version, cacheDir = lilypondCacheDir(),
|
|
|
60
60
|
if (await pathExists(markerPath))
|
|
61
61
|
return binaryPath;
|
|
62
62
|
const url = downloadUrlFor(version, platform, arch, archiveExt);
|
|
63
|
-
log(`
|
|
63
|
+
log(`Downloading LilyPond ${version} (${platform}-${arch})…`);
|
|
64
64
|
const response = await fetchImpl(url);
|
|
65
65
|
if (!response.ok || !response.body) {
|
|
66
66
|
throw new Error(`failed to download LilyPond ${version} from ${url} (HTTP ${response.status})`);
|
|
@@ -86,7 +86,7 @@ export async function downloadLilypond({ version, cacheDir = lilypondCacheDir(),
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
else {
|
|
89
|
-
log("
|
|
89
|
+
log("Download response had no checksum header — skipping integrity verification");
|
|
90
90
|
}
|
|
91
91
|
await writeFile(archivePath, data);
|
|
92
92
|
const extractDir = join(workDir, "extract");
|
|
@@ -106,7 +106,7 @@ export async function downloadLilypond({ version, cacheDir = lilypondCacheDir(),
|
|
|
106
106
|
const reuseConcurrentInstall = async () => {
|
|
107
107
|
if (!(await pathExists(markerPath)))
|
|
108
108
|
return false;
|
|
109
|
-
log(`
|
|
109
|
+
log(`LilyPond ${version} was installed concurrently by another process; reusing it`);
|
|
110
110
|
return true;
|
|
111
111
|
};
|
|
112
112
|
if (await reuseConcurrentInstall())
|
|
@@ -120,7 +120,7 @@ export async function downloadLilypond({ version, cacheDir = lilypondCacheDir(),
|
|
|
120
120
|
return binaryPath;
|
|
121
121
|
throw err;
|
|
122
122
|
}
|
|
123
|
-
log(`
|
|
123
|
+
log(`Installed LilyPond ${version} to ${installDir}`);
|
|
124
124
|
return binaryPath;
|
|
125
125
|
}
|
|
126
126
|
finally {
|
|
@@ -21,12 +21,12 @@ export async function resolveLilypondBinary({ version = DEFAULT_LILYPOND_VERSION
|
|
|
21
21
|
if (await isOnPath())
|
|
22
22
|
return "lilypond";
|
|
23
23
|
if (!autoInstall) {
|
|
24
|
-
warn("
|
|
24
|
+
warn("No `lilypond` binary found on PATH — LilyPond blocks will render as errors. " +
|
|
25
25
|
"Install LilyPond and ensure it is on PATH, or enable the `autoInstall` option (on by default).");
|
|
26
26
|
return "lilypond";
|
|
27
27
|
}
|
|
28
28
|
if (!resolvePlatformTarget()) {
|
|
29
|
-
warn(`
|
|
29
|
+
warn(`No \`lilypond\` binary found on PATH, and no prebuilt LilyPond ${version} ` +
|
|
30
30
|
`is available for ${process.platform}/${process.arch}. Install LilyPond manually and ensure it is on PATH.`);
|
|
31
31
|
return "lilypond";
|
|
32
32
|
}
|
package/dist/execLilyPond.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AstroIntegrationLogger } from "astro";
|
|
1
2
|
import type { Format } from "./render.js";
|
|
2
3
|
export interface ExecLilypondOptions {
|
|
3
4
|
binaryPath: string;
|
|
@@ -8,6 +9,7 @@ export interface ExecLilypondOptions {
|
|
|
8
9
|
timeout: number;
|
|
9
10
|
inputPath: string;
|
|
10
11
|
outputBase: string;
|
|
12
|
+
logger: Pick<AstroIntegrationLogger, "warn" | "error">;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Invokes the `lilypond` binary against an already-written input file.
|
package/dist/execLilyPond.js
CHANGED
|
@@ -8,10 +8,14 @@ const MAX_BUFFER = 16 * 1024 * 1024; // 16 MiB
|
|
|
8
8
|
* build output); throws a normalized Error on a non-zero exit or timeout.
|
|
9
9
|
*/
|
|
10
10
|
export async function execLilyPond(options) {
|
|
11
|
-
const { binaryPath, format, crop, resolution, includePaths, timeout, inputPath, outputBase, } = options;
|
|
11
|
+
const { binaryPath, format, crop, resolution, includePaths, timeout, inputPath, outputBase, logger, } = options;
|
|
12
12
|
const args = [
|
|
13
13
|
// Render the correct format
|
|
14
14
|
`--format=${format}`,
|
|
15
|
+
// Suppress LilyPond's own progress trace (Processing/Parsing/Interpreting
|
|
16
|
+
// music/.../Success), which is noise once Astro is reporting the build
|
|
17
|
+
// itself. Warnings and errors still print at this level.
|
|
18
|
+
"--loglevel=WARN",
|
|
15
19
|
// Define defaults — these can be overridden with the appropriate flags
|
|
16
20
|
// inside of individual .ly files
|
|
17
21
|
// Disable point-and-click behavior on SVGs when clicking noteheads
|
|
@@ -29,9 +33,10 @@ export async function execLilyPond(options) {
|
|
|
29
33
|
outputBase,
|
|
30
34
|
inputPath,
|
|
31
35
|
];
|
|
32
|
-
// LilyPond writes
|
|
33
|
-
//
|
|
34
|
-
//
|
|
36
|
+
// LilyPond writes any warnings to stderr even on success (errors on
|
|
37
|
+
// failure). Surface it through Astro's logger so it's visible and styled
|
|
38
|
+
// consistently with the rest of the build output, but let the exit code
|
|
39
|
+
// (not stderr content) decide pass/fail.
|
|
35
40
|
let stderr;
|
|
36
41
|
try {
|
|
37
42
|
({ stderr } = await execFileAsync(binaryPath, args, {
|
|
@@ -49,9 +54,9 @@ export async function execLilyPond(options) {
|
|
|
49
54
|
? String(err.stderr)
|
|
50
55
|
: undefined;
|
|
51
56
|
if (errStderr)
|
|
52
|
-
|
|
57
|
+
logger.error(errStderr);
|
|
53
58
|
throw new Error(errStderr || (err instanceof Error ? err.message : String(err)));
|
|
54
59
|
}
|
|
55
60
|
if (stderr)
|
|
56
|
-
|
|
61
|
+
logger.warn(stderr);
|
|
57
62
|
}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import emitAssetIntegration from "astro-emit-asset";
|
|
|
3
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
|
-
import { altTextFor, emitLilypondAsset, emitLilypondPdfAsset, includePathsFor, lyTypeDeclarationsFor, parseLyHeaderFields, prependVersion, renderedHtml, resolveDefaults, sourceNameFor, titleFor, toLilypondMetadata, } from "./utils/index.js";
|
|
6
|
+
import { altTextFor, emitLilypondAsset, emitLilypondPdfAsset, includePathsFor, lyTypeDeclarationsFor, parseLyHeaderFields, prependVersion, renderedErrorHtml, renderedHtml, resolveDefaults, sourceNameFor, titleFor, toLilypondMetadata, } from "./utils/index.js";
|
|
7
7
|
export const LY_EXTENSIONS = [".ly", ".lilypond", ".ily"];
|
|
8
8
|
function createScoreComponent(content) {
|
|
9
9
|
return createComponent((_result, props) => {
|
|
@@ -16,6 +16,15 @@ function createScoreComponent(content) {
|
|
|
16
16
|
return renderTemplate `${unescapeHTML(html)}`;
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Dev-only fallback for `render()`: renders an inline error block
|
|
21
|
+
* instead of the score.
|
|
22
|
+
*/
|
|
23
|
+
function createErrorScoreComponent(error, title) {
|
|
24
|
+
return createComponent(() => {
|
|
25
|
+
return renderTemplate `${unescapeHTML(renderedErrorHtml(error, title))}`;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
19
28
|
/**
|
|
20
29
|
* Renders a `LilypondScore` (from a `.ly`/`.ily`/`.lilypond` import,
|
|
21
30
|
* or a `lilypondLoader()` entry) to a renderable `<Score />` component.
|
|
@@ -25,49 +34,64 @@ export async function render(score, options = {}) {
|
|
|
25
34
|
const { resolution, cropScale } = resolveDefaults(state.defaults);
|
|
26
35
|
const format = options.format ?? "svg";
|
|
27
36
|
const crop = options.crop ?? false;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
source: score.source,
|
|
34
|
-
resolution,
|
|
35
|
-
crop,
|
|
36
|
-
sizeScale: crop ? cropScale : 1,
|
|
37
|
-
binaryPath: state.binaryPath,
|
|
38
|
-
render: () => renderScore(score.source, {
|
|
37
|
+
try {
|
|
38
|
+
const [{ Score, pageCount }, pdf] = await Promise.all([
|
|
39
|
+
(async () => {
|
|
40
|
+
const pages = await emitLilypondAsset({
|
|
41
|
+
title: score.assetTitle,
|
|
39
42
|
format,
|
|
43
|
+
source: score.source,
|
|
44
|
+
resolution,
|
|
40
45
|
crop,
|
|
41
|
-
|
|
42
|
-
timeout: state.timeout,
|
|
46
|
+
sizeScale: crop ? cropScale : 1,
|
|
43
47
|
binaryPath: state.binaryPath,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
48
|
+
render: () => renderScore(score.source, {
|
|
49
|
+
format,
|
|
50
|
+
crop,
|
|
51
|
+
defaults: state.defaults,
|
|
52
|
+
timeout: state.timeout,
|
|
53
|
+
binaryPath: state.binaryPath,
|
|
54
|
+
includePaths: score.includePaths,
|
|
55
|
+
sourceName: score.sourceName,
|
|
56
|
+
logger: state.logger,
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
Score: createScoreComponent({ pages, alt: score.alt }),
|
|
61
|
+
pageCount: pages.length,
|
|
62
|
+
};
|
|
63
|
+
})(),
|
|
64
|
+
options.pdf
|
|
65
|
+
? emitLilypondPdfAsset({
|
|
66
|
+
title: score.assetTitle,
|
|
67
|
+
source: score.source,
|
|
63
68
|
binaryPath: state.binaryPath,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
render: () => renderScore(score.source, {
|
|
70
|
+
format: "pdf",
|
|
71
|
+
crop: false,
|
|
72
|
+
defaults: state.defaults,
|
|
73
|
+
timeout: state.timeout,
|
|
74
|
+
binaryPath: state.binaryPath,
|
|
75
|
+
includePaths: score.includePaths,
|
|
76
|
+
sourceName: score.sourceName,
|
|
77
|
+
logger: state.logger,
|
|
78
|
+
}),
|
|
79
|
+
})
|
|
80
|
+
: Promise.resolve(undefined),
|
|
81
|
+
]);
|
|
82
|
+
return { Score, pageCount, pdf, meta: score.meta, raw: score.source };
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (!state.isDev)
|
|
86
|
+
throw err;
|
|
87
|
+
return {
|
|
88
|
+
Score: createErrorScoreComponent(err, score.assetTitle),
|
|
89
|
+
pageCount: 0,
|
|
90
|
+
pdf: undefined,
|
|
91
|
+
meta: score.meta,
|
|
92
|
+
raw: score.source,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
71
95
|
}
|
|
72
96
|
/**
|
|
73
97
|
* Renders a `LilypondScore` directly, for the common case where none of
|
|
@@ -114,11 +138,15 @@ export default function lilypond(options = {}) {
|
|
|
114
138
|
return {
|
|
115
139
|
name: "astro-lilypond",
|
|
116
140
|
hooks: {
|
|
117
|
-
"astro:config:setup": async ({ config, updateConfig, logger }) => {
|
|
141
|
+
"astro:config:setup": async ({ command, config, updateConfig, logger, }) => {
|
|
142
|
+
const isDev = command === "dev";
|
|
143
|
+
options.isDev = isDev;
|
|
144
|
+
options.logger = logger;
|
|
118
145
|
options.binaryPath = await resolveAndSetRenderState({
|
|
119
146
|
autoInstall: options.autoInstall,
|
|
120
147
|
defaults: options.defaults,
|
|
121
148
|
timeout: options.timeout,
|
|
149
|
+
isDev,
|
|
122
150
|
logger,
|
|
123
151
|
});
|
|
124
152
|
updateConfig({
|
|
@@ -144,7 +172,7 @@ export default function lilypond(options = {}) {
|
|
|
144
172
|
}),
|
|
145
173
|
},
|
|
146
174
|
});
|
|
147
|
-
logger?.info("
|
|
175
|
+
logger?.info("Registered Sätteri mdast plugin");
|
|
148
176
|
return;
|
|
149
177
|
}
|
|
150
178
|
if (existingProcessor?.name === "unified") {
|
|
@@ -165,7 +193,7 @@ export default function lilypond(options = {}) {
|
|
|
165
193
|
}),
|
|
166
194
|
},
|
|
167
195
|
});
|
|
168
|
-
logger?.info("
|
|
196
|
+
logger?.info("Registered unified remark plugin");
|
|
169
197
|
return;
|
|
170
198
|
}
|
|
171
199
|
throw new Error("astro-lilypond requires a processor-based Astro markdown config. " +
|
package/dist/loader.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { LoaderContext } from "astro/loaders";
|
|
2
2
|
import { z } from "astro/zod";
|
|
3
3
|
import { type LilypondScore } from "./index.js";
|
|
4
|
-
import type { LilypondDefaults } from "./render.js";
|
|
5
4
|
export interface GenerateIdOptions {
|
|
6
5
|
/** The path to the entry file, relative to `base`. */
|
|
7
6
|
entry: string;
|
|
@@ -26,11 +25,6 @@ export interface LilypondLoaderOptions {
|
|
|
26
25
|
* @default the file's path relative to `base`, POSIX-separated, with its extension stripped.
|
|
27
26
|
*/
|
|
28
27
|
generateId?: (options: GenerateIdOptions) => string;
|
|
29
|
-
/**
|
|
30
|
-
* Defaults used when `render()` is later called on one of this
|
|
31
|
-
* collection's entries.
|
|
32
|
-
*/
|
|
33
|
-
defaults?: LilypondDefaults;
|
|
34
28
|
}
|
|
35
29
|
export type LilypondCollectionEntry = LilypondScore;
|
|
36
30
|
export declare const lilypondEntrySchema: z.ZodObject<{
|
|
@@ -58,7 +52,7 @@ export declare const lilypondEntrySchema: z.ZodObject<{
|
|
|
58
52
|
/**
|
|
59
53
|
* Content Loader to turn a directory of `.ly` files into a content collection.
|
|
60
54
|
*/
|
|
61
|
-
export declare function lilypondLoader({ pattern, base, generateId,
|
|
55
|
+
export declare function lilypondLoader({ pattern, base, generateId, }: LilypondLoaderOptions): {
|
|
62
56
|
name: string;
|
|
63
57
|
schema: z.ZodObject<{
|
|
64
58
|
source: z.ZodString;
|
package/dist/loader.js
CHANGED
|
@@ -3,6 +3,7 @@ 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 { getRenderState } from "./renderState.js";
|
|
6
7
|
import { altTextFor, includePathsFor, parseLyHeaderFields, prependVersion, resolveDefaults, sourceNameFor, titleFor, toLilypondMetadata, } from "./utils/index.js";
|
|
7
8
|
const DEFAULT_PATTERN = `**/*.{${LY_EXTENSIONS.map((ext) => ext.slice(1)).join(",")}}`;
|
|
8
9
|
// Field names must match `LilypondMetadata` in `utils/lilypondMetadata.ts`.
|
|
@@ -55,13 +56,13 @@ function posixRelative(from, to) {
|
|
|
55
56
|
/**
|
|
56
57
|
* Content Loader to turn a directory of `.ly` files into a content collection.
|
|
57
58
|
*/
|
|
58
|
-
export function lilypondLoader({ pattern = DEFAULT_PATTERN, base, generateId = defaultGenerateId,
|
|
59
|
-
const resolved = resolveDefaults(defaults);
|
|
59
|
+
export function lilypondLoader({ pattern = DEFAULT_PATTERN, base, generateId = defaultGenerateId, }) {
|
|
60
60
|
return {
|
|
61
61
|
name: "astro-lilypond-loader",
|
|
62
62
|
schema: lilypondEntrySchema,
|
|
63
63
|
async load(context) {
|
|
64
64
|
const { config, store, logger, watcher, generateDigest, parseData } = context;
|
|
65
|
+
const { version } = resolveDefaults(getRenderState().defaults);
|
|
65
66
|
const rootDir = fileURLToPath(config.root);
|
|
66
67
|
const baseUrl = resolveBaseUrl(base, config.root);
|
|
67
68
|
const baseDir = fileURLToPath(baseUrl);
|
|
@@ -72,13 +73,13 @@ export function lilypondLoader({ pattern = DEFAULT_PATTERN, base, generateId = d
|
|
|
72
73
|
source = await readFile(filePath, "utf8");
|
|
73
74
|
}
|
|
74
75
|
catch (err) {
|
|
75
|
-
logger.error(`
|
|
76
|
+
logger.error(`Error reading ${entry}: ${err.message}`);
|
|
76
77
|
return undefined;
|
|
77
78
|
}
|
|
78
79
|
const headerFields = parseLyHeaderFields(source);
|
|
79
80
|
const id = generateId({ entry, base: baseUrl, header: headerFields });
|
|
80
81
|
const digest = generateDigest(source);
|
|
81
|
-
const src = prependVersion(source,
|
|
82
|
+
const src = prependVersion(source, version);
|
|
82
83
|
const includePaths = includePathsFor(filePath);
|
|
83
84
|
const sourceName = sourceNameFor(filePath);
|
|
84
85
|
const assetTitle = titleFor(sourceName);
|
|
@@ -116,7 +117,7 @@ export function lilypondLoader({ pattern = DEFAULT_PATTERN, base, generateId = d
|
|
|
116
117
|
entries.push(entry);
|
|
117
118
|
}
|
|
118
119
|
if (entries.length === 0) {
|
|
119
|
-
logger.warn(`
|
|
120
|
+
logger.warn(`No files matched "${pattern}" in "${baseDir}"`);
|
|
120
121
|
}
|
|
121
122
|
const results = await Promise.all(entries.map(async (entry) => ({ entry, id: await syncEntry(entry) })));
|
|
122
123
|
for (const { entry, id } of results) {
|
|
@@ -146,7 +147,7 @@ export function lilypondLoader({ pattern = DEFAULT_PATTERN, base, generateId = d
|
|
|
146
147
|
if (!changedPath.startsWith(baseDir))
|
|
147
148
|
return;
|
|
148
149
|
queue = queue.then(() => runSync().catch((err) => {
|
|
149
|
-
logger.error(`
|
|
150
|
+
logger.error(`Reload failed: ${err.message}`);
|
|
150
151
|
}));
|
|
151
152
|
};
|
|
152
153
|
watcher.on("add", onFsEvent);
|
package/dist/plugins/remark.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { visit } from "unist-util-visit";
|
|
2
2
|
import { defaultOptions, render } from "../render.js";
|
|
3
|
-
import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
|
|
3
|
+
import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedErrorHtml, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
|
|
4
4
|
export const remarkPlugin = (options) => {
|
|
5
|
+
const { logger } = options;
|
|
6
|
+
if (!logger) {
|
|
7
|
+
throw new Error("astro-lilypond: please add the `lilypond()` integration to your Astro config.");
|
|
8
|
+
}
|
|
5
9
|
return async (tree, file) => {
|
|
6
10
|
const promises = [];
|
|
7
11
|
const includePaths = includePathsFor(file?.path);
|
|
@@ -14,30 +18,38 @@ export const remarkPlugin = (options) => {
|
|
|
14
18
|
const source = version ? prependVersion(node.value, version) : node.value;
|
|
15
19
|
const format = options.format ?? defaultOptions.format;
|
|
16
20
|
const alt = altTextForBlock(node.meta, node.value);
|
|
17
|
-
const promise =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
const promise = (async () => {
|
|
22
|
+
let value;
|
|
23
|
+
try {
|
|
24
|
+
const pages = await emitLilypondAsset({
|
|
25
|
+
title,
|
|
26
|
+
format,
|
|
27
|
+
source,
|
|
28
|
+
resolution,
|
|
29
|
+
crop: true,
|
|
30
|
+
sizeScale: cropScale,
|
|
31
|
+
binaryPath: options.binaryPath,
|
|
32
|
+
render: () => render(source, {
|
|
33
|
+
format,
|
|
34
|
+
crop: true,
|
|
35
|
+
defaults: options.defaults,
|
|
36
|
+
timeout: options.timeout,
|
|
37
|
+
binaryPath: options.binaryPath,
|
|
38
|
+
includePaths,
|
|
39
|
+
sourceName,
|
|
40
|
+
logger,
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
value = renderedHtml(pages, alt);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (!options.isDev)
|
|
47
|
+
throw err;
|
|
48
|
+
value = renderedErrorHtml(err, title);
|
|
49
|
+
}
|
|
50
|
+
const htmlNode = { type: "html", value };
|
|
39
51
|
parent.children[index] = htmlNode;
|
|
40
|
-
});
|
|
52
|
+
})();
|
|
41
53
|
promises.push(promise);
|
|
42
54
|
});
|
|
43
55
|
await Promise.all(promises);
|
package/dist/plugins/satteri.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { defaultOptions, render } from "../render.js";
|
|
2
|
-
import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
|
|
2
|
+
import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedErrorHtml, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
|
|
3
3
|
export function satteriPlugin(options) {
|
|
4
|
+
const { logger } = options;
|
|
5
|
+
if (!logger) {
|
|
6
|
+
throw new Error("astro-lilypond: please add the `lilypond()` integration to your Astro config.");
|
|
7
|
+
}
|
|
4
8
|
return {
|
|
5
9
|
name: "astro-lilypond",
|
|
6
10
|
async code(node, ctx) {
|
|
@@ -13,25 +17,33 @@ export function satteriPlugin(options) {
|
|
|
13
17
|
const sourceName = sourceNameFor(ctx.fileURL);
|
|
14
18
|
const title = titleFor(sourceName);
|
|
15
19
|
const alt = altTextForBlock(node.meta, node.value);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
source,
|
|
20
|
-
resolution,
|
|
21
|
-
crop: true,
|
|
22
|
-
sizeScale: cropScale,
|
|
23
|
-
binaryPath: options.binaryPath,
|
|
24
|
-
render: () => render(source, {
|
|
20
|
+
try {
|
|
21
|
+
const pages = await emitLilypondAsset({
|
|
22
|
+
title,
|
|
25
23
|
format,
|
|
24
|
+
source,
|
|
25
|
+
resolution,
|
|
26
26
|
crop: true,
|
|
27
|
-
|
|
28
|
-
timeout: options.timeout,
|
|
27
|
+
sizeScale: cropScale,
|
|
29
28
|
binaryPath: options.binaryPath,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
render: () => render(source, {
|
|
30
|
+
format,
|
|
31
|
+
crop: true,
|
|
32
|
+
defaults: options.defaults,
|
|
33
|
+
timeout: options.timeout,
|
|
34
|
+
binaryPath: options.binaryPath,
|
|
35
|
+
includePaths,
|
|
36
|
+
sourceName,
|
|
37
|
+
logger,
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
return { rawHtml: renderedHtml(pages, alt) };
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
if (!options.isDev)
|
|
44
|
+
throw err;
|
|
45
|
+
return { rawHtml: renderedErrorHtml(err, title) };
|
|
46
|
+
}
|
|
35
47
|
},
|
|
36
48
|
};
|
|
37
49
|
}
|
package/dist/plugins/types.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import type { AstroIntegrationLogger } from "astro";
|
|
1
2
|
import type { LilypondDefaults } from "../render.js";
|
|
2
3
|
export interface PluginOptions {
|
|
3
4
|
format?: "svg" | "png";
|
|
4
5
|
defaults?: LilypondDefaults;
|
|
5
6
|
timeout?: number;
|
|
6
7
|
binaryPath?: string;
|
|
8
|
+
isDev?: boolean;
|
|
9
|
+
logger?: Pick<AstroIntegrationLogger, "warn" | "error">;
|
|
7
10
|
}
|
package/dist/render.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AstroIntegrationLogger } from "astro";
|
|
1
2
|
import type { LilypondVersion } from "./types/lilypondVersion.js";
|
|
2
3
|
export declare const FORMATS: readonly ["png", "svg", "pdf"];
|
|
3
4
|
export type Format = (typeof FORMATS)[number];
|
|
@@ -32,7 +33,7 @@ export interface LilypondDefaults {
|
|
|
32
33
|
* source text.
|
|
33
34
|
*/
|
|
34
35
|
export type RenderDefaults = Omit<LilypondDefaults, "version">;
|
|
35
|
-
export interface
|
|
36
|
+
export interface InternalRenderOptions {
|
|
36
37
|
/**
|
|
37
38
|
* Output format.
|
|
38
39
|
* @default "svg"
|
|
@@ -72,8 +73,12 @@ export interface RenderOptions {
|
|
|
72
73
|
* @default 60000
|
|
73
74
|
*/
|
|
74
75
|
timeout?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Warning and failure logging from LilyPond.
|
|
78
|
+
*/
|
|
79
|
+
logger: Pick<AstroIntegrationLogger, "warn" | "error">;
|
|
75
80
|
}
|
|
76
|
-
export declare const defaultOptions: Required<Omit<
|
|
81
|
+
export declare const defaultOptions: Required<Omit<InternalRenderOptions, "includePaths" | "sourceName" | "defaults" | "logger">> & {
|
|
77
82
|
defaults: Required<LilypondDefaults>;
|
|
78
83
|
};
|
|
79
|
-
export declare function render(source: string, options
|
|
84
|
+
export declare function render(source: string, options: InternalRenderOptions): Promise<Buffer[]>;
|
package/dist/render.js
CHANGED
|
@@ -16,8 +16,8 @@ export const defaultOptions = {
|
|
|
16
16
|
cropScale: 1.5,
|
|
17
17
|
},
|
|
18
18
|
};
|
|
19
|
-
export async function render(source, options
|
|
20
|
-
const { format = defaultOptions.format, crop = defaultOptions.crop, binaryPath = defaultOptions.binaryPath, timeout = defaultOptions.timeout, includePaths = [], sourceName, } = options;
|
|
19
|
+
export async function render(source, options) {
|
|
20
|
+
const { format = defaultOptions.format, crop = defaultOptions.crop, binaryPath = defaultOptions.binaryPath, timeout = defaultOptions.timeout, includePaths = [], sourceName, logger, } = options;
|
|
21
21
|
const { resolution } = resolveDefaults(options.defaults);
|
|
22
22
|
if (!FORMATS.includes(format)) {
|
|
23
23
|
throw new Error(`${format} is not a supported format`);
|
|
@@ -36,6 +36,7 @@ export async function render(source, options = {}) {
|
|
|
36
36
|
timeout,
|
|
37
37
|
inputPath,
|
|
38
38
|
outputBase,
|
|
39
|
+
logger,
|
|
39
40
|
});
|
|
40
41
|
return await readOutputFile(outputBase, format, crop);
|
|
41
42
|
}
|
package/dist/renderState.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import type { AstroIntegrationLogger } from "astro";
|
|
1
2
|
import { type AutoInstallOptions } from "./binary/index.js";
|
|
2
3
|
import type { LilypondDefaults } from "./render.js";
|
|
3
4
|
export interface RenderState {
|
|
4
5
|
binaryPath: string;
|
|
5
6
|
defaults: LilypondDefaults | undefined;
|
|
6
7
|
timeout: number | undefined;
|
|
8
|
+
isDev: boolean;
|
|
9
|
+
logger: Pick<AstroIntegrationLogger, "warn" | "error">;
|
|
7
10
|
}
|
|
8
11
|
/** Called by the `lilypond()` integration's `astro:config:setup` hook. */
|
|
9
12
|
export declare function setRenderState(next: RenderState): void;
|
|
@@ -15,10 +18,8 @@ export interface ResolveAndSetRenderStateOptions {
|
|
|
15
18
|
autoInstall?: boolean | AutoInstallOptions;
|
|
16
19
|
defaults?: LilypondDefaults;
|
|
17
20
|
timeout?: number;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
warn: (message: string) => void;
|
|
21
|
-
};
|
|
21
|
+
isDev: boolean;
|
|
22
|
+
logger: Pick<AstroIntegrationLogger, "info" | "warn" | "error">;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Shared by the `lilypond()` integration's `astro:config:setup` hook and
|
package/dist/renderState.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* attribute value.
|
|
4
|
-
* integration already emits — is arbitrary user/header-derived content that
|
|
5
|
-
* can contain `"`, `&`, `<`.
|
|
2
|
+
* Escape a string for safe interpolation inside a double-quoted
|
|
3
|
+
* HTML attribute value.
|
|
6
4
|
*/
|
|
7
5
|
export declare function escapeHtmlAttribute(value: string): string;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* attribute value.
|
|
4
|
-
* integration already emits — is arbitrary user/header-derived content that
|
|
5
|
-
* can contain `"`, `&`, `<`.
|
|
2
|
+
* Escape a string for safe interpolation inside a double-quoted
|
|
3
|
+
* HTML attribute value.
|
|
6
4
|
*/
|
|
7
5
|
export function escapeHtmlAttribute(value) {
|
|
8
6
|
return value
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { altTextFor } from "./altTextFor.js";
|
|
|
2
2
|
export { altTextForBlock } from "./altTextForBlock.js";
|
|
3
3
|
export { type EmitLilypondAssetOptions, emitLilypondAsset, } from "./emitLilypondAsset.js";
|
|
4
4
|
export { type EmitLilypondPdfAssetOptions, emitLilypondPdfAsset, } from "./emitLilypondPdfAsset.js";
|
|
5
|
+
export { escapeHtml } from "./escapeHtml.js";
|
|
5
6
|
export { escapeHtmlAttribute } from "./escapeHtmlAttribute.js";
|
|
6
7
|
export type { ImageDimensions } from "./imageDimensions.js";
|
|
7
8
|
export { imageDimensionsFor } from "./imageDimensions.js";
|
|
@@ -14,6 +15,7 @@ export { parseFenceMeta } from "./parseFenceMeta.js";
|
|
|
14
15
|
export type { LyHeaderFields } from "./parseLyHeader.js";
|
|
15
16
|
export { extractMarkupText, parseLyHeader, parseLyHeaderFields, } from "./parseLyHeader.js";
|
|
16
17
|
export { prependVersion } from "./prependVersion.js";
|
|
18
|
+
export { renderedErrorHtml } from "./renderedErrorHtml.js";
|
|
17
19
|
export { renderedHtml } from "./renderedHtml.js";
|
|
18
20
|
export { resolveDefaults } from "./resolveDefaults.js";
|
|
19
21
|
export { sourceNameFor } from "./sourceNameFor.js";
|
package/dist/utils/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export { altTextFor } from "./altTextFor.js";
|
|
|
2
2
|
export { altTextForBlock } from "./altTextForBlock.js";
|
|
3
3
|
export { emitLilypondAsset, } from "./emitLilypondAsset.js";
|
|
4
4
|
export { emitLilypondPdfAsset, } from "./emitLilypondPdfAsset.js";
|
|
5
|
+
export { escapeHtml } from "./escapeHtml.js";
|
|
5
6
|
export { escapeHtmlAttribute } from "./escapeHtmlAttribute.js";
|
|
6
7
|
export { imageDimensionsFor } from "./imageDimensions.js";
|
|
7
8
|
export { includePathsFor } from "./includePathsFor.js";
|
|
@@ -11,6 +12,7 @@ export { lyTypeDeclarationsFor } from "./lyTypeDeclarationsFor.js";
|
|
|
11
12
|
export { parseFenceMeta } from "./parseFenceMeta.js";
|
|
12
13
|
export { extractMarkupText, parseLyHeader, parseLyHeaderFields, } from "./parseLyHeader.js";
|
|
13
14
|
export { prependVersion } from "./prependVersion.js";
|
|
15
|
+
export { renderedErrorHtml } from "./renderedErrorHtml.js";
|
|
14
16
|
export { renderedHtml } from "./renderedHtml.js";
|
|
15
17
|
export { resolveDefaults } from "./resolveDefaults.js";
|
|
16
18
|
export { sourceNameFor } from "./sourceNameFor.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderedErrorHtml(error: unknown, title: string): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { escapeHtml } from "./escapeHtml.js";
|
|
2
|
+
const ERROR_STYLE = `display: block;
|
|
3
|
+
margin: 1em 0;
|
|
4
|
+
padding: 1em;
|
|
5
|
+
color-scheme: light dark;
|
|
6
|
+
border: 2px solid light-dark(#dc2626, #f87171);
|
|
7
|
+
border-radius: 6px;
|
|
8
|
+
background: light-dark(#fef2f2, #450a0a);
|
|
9
|
+
color: light-dark(#5d1516, #fcd1d2);
|
|
10
|
+
font-family: ui-monospace, monospace;
|
|
11
|
+
font-size: 0.875rem;
|
|
12
|
+
line-height: 1.5;
|
|
13
|
+
white-space: pre-wrap;
|
|
14
|
+
overflow-wrap: break-word;
|
|
15
|
+
text-align: left;`;
|
|
16
|
+
function messageFor(error) {
|
|
17
|
+
return error instanceof Error ? error.message : String(error);
|
|
18
|
+
}
|
|
19
|
+
export function renderedErrorHtml(error, title) {
|
|
20
|
+
const heading = `LilyPond failed to render "${escapeHtml(title)}"`;
|
|
21
|
+
return `<pre style="${ERROR_STYLE}">${heading}\n\n${escapeHtml(messageFor(error))}</pre>`;
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-lilypond",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
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",
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"dist"
|
|
27
27
|
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22.12.0"
|
|
30
|
+
},
|
|
28
31
|
"keywords": [
|
|
29
32
|
"astro",
|
|
30
33
|
"astro-integration",
|