astro-lilypond 0.17.0 → 1.0.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { AstroIntegration } from "astro";
2
2
  import { type AstroComponentFactory } from "astro/runtime/server/index.js";
3
- import type { AutoInstallOptions } from "./binary/index.js";
3
+ import { type AutoInstallOptions } from "./binary/index.js";
4
4
  import { type PluginOptions } from "./plugins/index.js";
5
5
  import { type LilypondDefaults } from "./render.js";
6
6
  import { type LilypondMetadata } from "./utils/index.js";
@@ -23,10 +23,10 @@ export interface LilypondScore {
23
23
  assetTitle: string;
24
24
  meta: LilypondMetadata;
25
25
  }
26
- export interface RenderOptions {
26
+ export interface GetScoreOptions {
27
27
  /**
28
28
  * Output format for `Score`.
29
- * @default "svg"
29
+ * @default the `defaults.format` configured on the integration ("svg" unless overridden)
30
30
  */
31
31
  format?: "svg" | "png";
32
32
  /**
@@ -47,9 +47,9 @@ export interface LilypondImageResult {
47
47
  export interface LilypondPdfResult {
48
48
  src: string;
49
49
  }
50
- export interface RenderResult {
50
+ export interface GetScoreResult {
51
51
  Score: AstroComponentFactory;
52
- pageCount: number;
52
+ pages: LilypondPage[];
53
53
  pdf?: LilypondPdfResult;
54
54
  meta: LilypondMetadata;
55
55
  raw: string;
@@ -64,35 +64,21 @@ interface ScoreImageProps {
64
64
  * Renders a `LilypondScore` (from a `.ly`/`.ily`/`.lilypond` import,
65
65
  * or a `lilypondLoader()` entry) to a renderable `<Score />` component.
66
66
  */
67
- export declare function render(score: LilypondScore, options?: RenderOptions): Promise<RenderResult>;
68
- export interface ScoreProps extends ScoreImageProps {
67
+ export declare function getScore(score: LilypondScore, options?: GetScoreOptions): Promise<GetScoreResult>;
68
+ export interface ScoreProps extends ScoreImageProps, Pick<GetScoreOptions, "format" | "crop"> {
69
69
  /**
70
70
  * A `LilypondScore` from a `.ly`/`.ily`/`.lilypond` import
71
71
  * or a `lilypondLoader()` entry.
72
72
  */
73
73
  content: LilypondScore;
74
- /**
75
- * @default "svg"
76
- */
77
- format?: "svg" | "png";
78
- /**
79
- * @default false
80
- */
81
- crop?: boolean;
82
74
  }
83
75
  /**
84
76
  * 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.
77
+ * `getScore()`'s `pages`, `meta`, `raw`, or `pdf` are needed. Use
78
+ * `getScore()` instead when you need those.
87
79
  */
88
80
  export declare const Score: AstroComponentFactory;
89
- export declare function renderAll(scores: LilypondScore[], options?: RenderOptions): Promise<RenderResult[]>;
90
81
  export interface LilypondOptions extends PluginOptions {
91
- /**
92
- * Output format used by Markdown fences and `lilypondLoader()` entries.
93
- * @default "svg"
94
- */
95
- format?: "svg" | "png";
96
82
  /**
97
83
  * Defaults passed to each score; can be overridden at render time.
98
84
  */
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { createComponent, renderComponent, renderTemplate, unescapeHTML, } from "astro/runtime/server/index.js";
2
2
  import emitAssetIntegration from "astro-emit-asset";
3
+ import { resolveAutoInstallOption, resolveLilypondBinary, } from "./binary/index.js";
3
4
  import { remarkPlugin, satteriPlugin, } from "./plugins/index.js";
4
- import { render as renderScore } from "./render.js";
5
- import { getRenderState, resolveAndSetRenderState } from "./renderState.js";
5
+ import { render } from "./render.js";
6
+ import { getLilypondState, setLilypondState } from "./state.js";
6
7
  import { altTextFor, emitLilypondAsset, emitLilypondPdfAsset, includePathsFor, lyTypeDeclarationsFor, parseLyHeaderFields, prependVersion, renderedErrorHtml, renderedHtml, resolveDefaults, sourceNameFor, titleFor, toLilypondMetadata, } from "./utils/index.js";
7
8
  export const LY_EXTENSIONS = [".ly", ".lilypond", ".ily"];
8
9
  function createScoreComponent(content) {
@@ -17,7 +18,7 @@ function createScoreComponent(content) {
17
18
  });
18
19
  }
19
20
  /**
20
- * Dev-only fallback for `render()`: renders an inline error block
21
+ * Dev-only fallback for `getScore()`: renders an inline error block
21
22
  * instead of the score.
22
23
  */
23
24
  function createErrorScoreComponent(error, title) {
@@ -29,13 +30,14 @@ function createErrorScoreComponent(error, title) {
29
30
  * Renders a `LilypondScore` (from a `.ly`/`.ily`/`.lilypond` import,
30
31
  * or a `lilypondLoader()` entry) to a renderable `<Score />` component.
31
32
  */
32
- export async function render(score, options = {}) {
33
- const state = getRenderState();
34
- const { resolution, cropScale } = resolveDefaults(state.defaults);
35
- const format = options.format ?? "svg";
33
+ export async function getScore(score, options = {}) {
34
+ const state = getLilypondState();
35
+ const { logger } = state;
36
+ const { resolution, cropScale, format: defaultFormat, } = resolveDefaults(state.defaults);
37
+ const format = options.format ?? defaultFormat;
36
38
  const crop = options.crop ?? false;
37
39
  try {
38
- const [{ Score, pageCount }, pdf] = await Promise.all([
40
+ const [{ Score, pages }, pdf] = await Promise.all([
39
41
  (async () => {
40
42
  const pages = await emitLilypondAsset({
41
43
  title: score.assetTitle,
@@ -45,7 +47,7 @@ export async function render(score, options = {}) {
45
47
  crop,
46
48
  sizeScale: crop ? cropScale : 1,
47
49
  binaryPath: state.binaryPath,
48
- render: () => renderScore(score.source, {
50
+ render: () => render(score.source, {
49
51
  format,
50
52
  crop,
51
53
  defaults: state.defaults,
@@ -53,12 +55,12 @@ export async function render(score, options = {}) {
53
55
  binaryPath: state.binaryPath,
54
56
  includePaths: score.includePaths,
55
57
  sourceName: score.sourceName,
56
- logger: state.logger,
58
+ logger,
57
59
  }),
58
60
  });
59
61
  return {
60
62
  Score: createScoreComponent({ pages, alt: score.alt }),
61
- pageCount: pages.length,
63
+ pages,
62
64
  };
63
65
  })(),
64
66
  options.pdf
@@ -66,7 +68,7 @@ export async function render(score, options = {}) {
66
68
  title: score.assetTitle,
67
69
  source: score.source,
68
70
  binaryPath: state.binaryPath,
69
- render: () => renderScore(score.source, {
71
+ render: () => render(score.source, {
70
72
  format: "pdf",
71
73
  crop: false,
72
74
  defaults: state.defaults,
@@ -74,19 +76,19 @@ export async function render(score, options = {}) {
74
76
  binaryPath: state.binaryPath,
75
77
  includePaths: score.includePaths,
76
78
  sourceName: score.sourceName,
77
- logger: state.logger,
79
+ logger,
78
80
  }),
79
81
  })
80
82
  : Promise.resolve(undefined),
81
83
  ]);
82
- return { Score, pageCount, pdf, meta: score.meta, raw: score.source };
84
+ return { Score, pages, pdf, meta: score.meta, raw: score.source };
83
85
  }
84
86
  catch (err) {
85
87
  if (!state.isDev)
86
88
  throw err;
87
89
  return {
88
90
  Score: createErrorScoreComponent(err, score.assetTitle),
89
- pageCount: 0,
91
+ pages: [],
90
92
  pdf: undefined,
91
93
  meta: score.meta,
92
94
  raw: score.source,
@@ -95,17 +97,14 @@ export async function render(score, options = {}) {
95
97
  }
96
98
  /**
97
99
  * Renders a `LilypondScore` directly, for the common case where none of
98
- * `render()`'s `pageCount`, `meta`, `raw`, or `pdf` are needed. Use
99
- * `render()` instead when you need those.
100
+ * `getScore()`'s `pages`, `meta`, `raw`, or `pdf` are needed. Use
101
+ * `getScore()` instead when you need those.
100
102
  */
101
103
  export const Score = createComponent(async (result, props) => {
102
104
  const { content, format, crop, ...imageProps } = props;
103
- const { Score: ContentScore } = await render(content, { format, crop });
105
+ const { Score: ContentScore } = await getScore(content, { format, crop });
104
106
  return renderTemplate `${renderComponent(result, "Score", ContentScore, imageProps)}`;
105
107
  });
106
- export async function renderAll(scores, options = {}) {
107
- return Promise.all(scores.map((score) => render(score, options)));
108
- }
109
108
  function lyFilePlugin(options) {
110
109
  return {
111
110
  name: "vite-plugin-astro-lilypond-ly",
@@ -142,8 +141,14 @@ export default function lilypond(options = {}) {
142
141
  const isDev = command === "dev";
143
142
  options.isDev = isDev;
144
143
  options.logger = logger;
145
- options.binaryPath = await resolveAndSetRenderState({
146
- autoInstall: options.autoInstall,
144
+ const binaryPath = await resolveLilypondBinary({
145
+ ...resolveAutoInstallOption(options.autoInstall),
146
+ log: (message) => logger.info(message),
147
+ warn: (message) => logger.warn(message),
148
+ });
149
+ options.binaryPath = binaryPath;
150
+ setLilypondState({
151
+ binaryPath,
147
152
  defaults: options.defaults,
148
153
  timeout: options.timeout,
149
154
  isDev,
package/dist/loader.js CHANGED
@@ -3,7 +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
+ import { getLilypondState } from "./state.js";
7
7
  import { altTextFor, includePathsFor, parseLyHeaderFields, prependVersion, resolveDefaults, sourceNameFor, titleFor, toLilypondMetadata, } from "./utils/index.js";
8
8
  const DEFAULT_PATTERN = `**/*.{${LY_EXTENSIONS.map((ext) => ext.slice(1)).join(",")}}`;
9
9
  // Field names must match `LilypondMetadata` in `utils/lilypondMetadata.ts`.
@@ -62,7 +62,7 @@ export function lilypondLoader({ pattern = DEFAULT_PATTERN, base, generateId = d
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
+ const { version } = resolveDefaults(getLilypondState().defaults);
66
66
  const rootDir = fileURLToPath(config.root);
67
67
  const baseUrl = resolveBaseUrl(base, config.root);
68
68
  const baseDir = fileURLToPath(baseUrl);
@@ -1,11 +1,12 @@
1
1
  import { visit } from "unist-util-visit";
2
- import { defaultOptions, render } from "../render.js";
3
- import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedErrorHtml, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
2
+ import { includePathsFor, isLilypondLang, sourceNameFor, titleFor, } from "../utils/index.js";
3
+ import { renderMarkdownBlock } from "./renderMarkdownBlock.js";
4
4
  export const remarkPlugin = (options) => {
5
5
  const { logger } = options;
6
6
  if (!logger) {
7
7
  throw new Error("astro-lilypond: please add the `lilypond()` integration to your Astro config.");
8
8
  }
9
+ const renderOptions = { ...options, logger };
9
10
  return async (tree, file) => {
10
11
  const promises = [];
11
12
  const includePaths = includePathsFor(file?.path);
@@ -14,42 +15,16 @@ export const remarkPlugin = (options) => {
14
15
  visit(tree, "code", (node, index, parent) => {
15
16
  if (!isLilypondLang(node.lang) || index === undefined || !parent)
16
17
  return;
17
- const { version, resolution, cropScale } = resolveDefaults(options.defaults);
18
- const source = version ? prependVersion(node.value, version) : node.value;
19
- const format = options.format ?? defaultOptions.format;
20
- const alt = altTextForBlock(node.meta, node.value);
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
- }
18
+ const promise = renderMarkdownBlock(renderOptions, {
19
+ title,
20
+ value: node.value,
21
+ meta: node.meta,
22
+ includePaths,
23
+ sourceName,
24
+ }).then((value) => {
50
25
  const htmlNode = { type: "html", value };
51
26
  parent.children[index] = htmlNode;
52
- })();
27
+ });
53
28
  promises.push(promise);
54
29
  });
55
30
  await Promise.all(promises);
@@ -0,0 +1,17 @@
1
+ import type { PluginOptions } from "./types.js";
2
+ export interface MarkdownBlock {
3
+ title: string;
4
+ value: string;
5
+ meta: string | null | undefined;
6
+ includePaths: string[];
7
+ sourceName: string | undefined;
8
+ }
9
+ /**
10
+ * Renders one fenced ```lilypond block to HTML. Shared by the remark and
11
+ * satteri plugins. `logger` is required here (unlike `PluginOptions`) since
12
+ * each plugin's registration-time "add the integration" guard already
13
+ * narrowed it before calling this.
14
+ */
15
+ export declare function renderMarkdownBlock(options: Omit<PluginOptions, "logger"> & {
16
+ logger: NonNullable<PluginOptions["logger"]>;
17
+ }, block: MarkdownBlock): Promise<string>;
@@ -0,0 +1,42 @@
1
+ import { render } from "../render.js";
2
+ import { altTextForBlock, emitLilypondAsset, prependVersion, renderedErrorHtml, renderedHtml, resolveDefaults, } from "../utils/index.js";
3
+ /**
4
+ * Renders one fenced ```lilypond block to HTML. Shared by the remark and
5
+ * satteri plugins. `logger` is required here (unlike `PluginOptions`) since
6
+ * each plugin's registration-time "add the integration" guard already
7
+ * narrowed it before calling this.
8
+ */
9
+ export async function renderMarkdownBlock(options, block) {
10
+ const { logger } = options;
11
+ const { title } = block;
12
+ const { version, format, resolution, cropScale } = resolveDefaults(options.defaults);
13
+ const source = version ? prependVersion(block.value, version) : block.value;
14
+ const alt = altTextForBlock(block.meta, block.value);
15
+ try {
16
+ const pages = await emitLilypondAsset({
17
+ title,
18
+ format,
19
+ source,
20
+ resolution,
21
+ crop: true,
22
+ sizeScale: cropScale,
23
+ binaryPath: options.binaryPath,
24
+ render: () => render(source, {
25
+ format,
26
+ crop: true,
27
+ defaults: options.defaults,
28
+ timeout: options.timeout,
29
+ binaryPath: options.binaryPath,
30
+ includePaths: block.includePaths,
31
+ sourceName: block.sourceName,
32
+ logger,
33
+ }),
34
+ });
35
+ return renderedHtml(pages, alt);
36
+ }
37
+ catch (err) {
38
+ if (!options.isDev)
39
+ throw err;
40
+ return renderedErrorHtml(err, title);
41
+ }
42
+ }
@@ -1,49 +1,25 @@
1
- import { defaultOptions, render } from "../render.js";
2
- import { altTextForBlock, emitLilypondAsset, includePathsFor, isLilypondLang, prependVersion, renderedErrorHtml, renderedHtml, resolveDefaults, sourceNameFor, titleFor, } from "../utils/index.js";
1
+ import { includePathsFor, isLilypondLang, sourceNameFor, titleFor, } from "../utils/index.js";
2
+ import { renderMarkdownBlock } from "./renderMarkdownBlock.js";
3
3
  export function satteriPlugin(options) {
4
4
  const { logger } = options;
5
5
  if (!logger) {
6
6
  throw new Error("astro-lilypond: please add the `lilypond()` integration to your Astro config.");
7
7
  }
8
+ const renderOptions = { ...options, logger };
8
9
  return {
9
10
  name: "astro-lilypond",
10
11
  async code(node, ctx) {
11
12
  if (!isLilypondLang(node.lang))
12
13
  return undefined;
13
- const { version, resolution, cropScale } = resolveDefaults(options.defaults);
14
- const source = version ? prependVersion(node.value, version) : node.value;
15
- const format = options.format ?? defaultOptions.format;
16
- const includePaths = includePathsFor(ctx.fileURL);
17
14
  const sourceName = sourceNameFor(ctx.fileURL);
18
- const title = titleFor(sourceName);
19
- const alt = altTextForBlock(node.meta, node.value);
20
- try {
21
- const pages = await emitLilypondAsset({
22
- title,
23
- format,
24
- source,
25
- resolution,
26
- crop: true,
27
- sizeScale: cropScale,
28
- binaryPath: options.binaryPath,
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
- }
15
+ const rawHtml = await renderMarkdownBlock(renderOptions, {
16
+ title: titleFor(sourceName),
17
+ value: node.value,
18
+ meta: node.meta,
19
+ includePaths: includePathsFor(ctx.fileURL),
20
+ sourceName,
21
+ });
22
+ return { rawHtml };
47
23
  },
48
24
  };
49
25
  }
@@ -1,7 +1,6 @@
1
1
  import type { AstroIntegrationLogger } from "astro";
2
2
  import type { LilypondDefaults } from "../render.js";
3
3
  export interface PluginOptions {
4
- format?: "svg" | "png";
5
4
  defaults?: LilypondDefaults;
6
5
  timeout?: number;
7
6
  binaryPath?: string;
package/dist/render.d.ts CHANGED
@@ -12,6 +12,11 @@ export interface LilypondDefaults {
12
12
  * @default "2.26.0"
13
13
  */
14
14
  version?: LilypondVersion;
15
+ /**
16
+ * Output format.
17
+ * @default "svg"
18
+ */
19
+ format?: "svg" | "png";
15
20
  /**
16
21
  * Resolution in DPI (only applies to PNG).
17
22
  * @default 144
@@ -29,10 +34,9 @@ export interface LilypondDefaults {
29
34
  }
30
35
  /**
31
36
  * The subset of `LilypondDefaults` that `render()` itself reads. `version`
32
- * is resolved by the caller before reaching `render()`, by prepending it to
33
- * source text.
37
+ * and `format` are resolved by the caller before reaching `render()`.
34
38
  */
35
- export type RenderDefaults = Omit<LilypondDefaults, "version">;
39
+ export type RenderDefaults = Omit<LilypondDefaults, "version" | "format">;
36
40
  export interface InternalRenderOptions {
37
41
  /**
38
42
  * Output format.
package/dist/render.js CHANGED
@@ -5,16 +5,18 @@ 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
- export const defaultOptions = {
8
+ const defaultLilypondDefaults = {
9
+ version: "2.26.0",
9
10
  format: "svg",
11
+ resolution: 144,
12
+ cropScale: 1.5,
13
+ };
14
+ export const defaultOptions = {
15
+ format: defaultLilypondDefaults.format,
10
16
  crop: true,
11
17
  binaryPath: "lilypond",
12
18
  timeout: 60_000,
13
- defaults: {
14
- version: "2.26.0",
15
- resolution: 144,
16
- cropScale: 1.5,
17
- },
19
+ defaults: defaultLilypondDefaults,
18
20
  };
19
21
  export async function render(source, options) {
20
22
  const { format = defaultOptions.format, crop = defaultOptions.crop, binaryPath = defaultOptions.binaryPath, timeout = defaultOptions.timeout, includePaths = [], sourceName, logger, } = options;
@@ -0,0 +1,12 @@
1
+ import type { AstroIntegrationLogger } from "astro";
2
+ import type { LilypondDefaults } from "./render.js";
3
+ export interface LilypondState {
4
+ binaryPath: string;
5
+ defaults: LilypondDefaults | undefined;
6
+ timeout: number | undefined;
7
+ isDev: boolean;
8
+ logger: Pick<AstroIntegrationLogger, "warn" | "error">;
9
+ }
10
+ export declare function setLilypondState(state: LilypondState): void;
11
+ export declare function getLilypondState(): LilypondState;
12
+ export declare function resetLilypondStateForTests(): void;
package/dist/state.js ADDED
@@ -0,0 +1,15 @@
1
+ const KEY = "astro-lilypond:state";
2
+ const store = globalThis;
3
+ export function setLilypondState(state) {
4
+ store[KEY] = state;
5
+ }
6
+ export function getLilypondState() {
7
+ const state = store[KEY];
8
+ if (!state) {
9
+ throw new Error("astro-lilypond: please add the `lilypond()` integration to your Astro config.");
10
+ }
11
+ return state;
12
+ }
13
+ export function resetLilypondStateForTests() {
14
+ store[KEY] = undefined;
15
+ }
@@ -1,12 +1,18 @@
1
1
  import { escapeHtml } from "./escapeHtml.js";
2
- const ERROR_STYLE = "all: initial; display: block; margin: 1em 0; padding: 1em; " +
3
- "color-scheme: light dark; " +
4
- "border: 2px solid light-dark(#dc2626, #f87171); border-radius: 6px; " +
5
- "background: light-dark(#fef2f2, #450a0a); " +
6
- "color: light-dark(#7f1d1d, #fca5a5); " +
7
- "font-family: ui-monospace, monospace; " +
8
- "font-size: 0.875rem; line-height: 1.5; white-space: pre-wrap; " +
9
- "overflow-wrap: break-word; text-align: left;";
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;`;
10
16
  function messageFor(error) {
11
17
  return error instanceof Error ? error.message : String(error);
12
18
  }
@@ -1,9 +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, cropScale } = defaultOptions.defaults;
4
+ const { version, format, resolution, cropScale } = defaultOptions.defaults;
5
5
  return {
6
6
  version: defaults?.version ?? version,
7
+ format: defaults?.format ?? format,
7
8
  resolution: defaults?.resolution ?? resolution,
8
9
  cropScale: defaults?.cropScale ?? cropScale,
9
10
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-lilypond",
3
- "version": "0.17.0",
3
+ "version": "1.0.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",
@@ -67,8 +67,7 @@
67
67
  },
68
68
  "scripts": {
69
69
  "build": "tsc --project tsconfig.build.json",
70
- "test": "vitest --project unit",
71
- "test:coverage": "vitest run --project unit --coverage",
72
- "test:integration": "vitest run --project integration"
70
+ "test": "vitest",
71
+ "test:coverage": "vitest run --coverage"
73
72
  }
74
73
  }
@@ -1,30 +0,0 @@
1
- import type { AstroIntegrationLogger } from "astro";
2
- import { type AutoInstallOptions } from "./binary/index.js";
3
- import type { LilypondDefaults } from "./render.js";
4
- export interface RenderState {
5
- binaryPath: string;
6
- defaults: LilypondDefaults | undefined;
7
- timeout: number | undefined;
8
- isDev: boolean;
9
- logger: Pick<AstroIntegrationLogger, "warn" | "error">;
10
- }
11
- /** Called by the `lilypond()` integration's `astro:config:setup` hook. */
12
- export declare function setRenderState(next: RenderState): void;
13
- /** Read by the public `render()`, called from user code rather than the integration's own closures. */
14
- export declare function getRenderState(): RenderState;
15
- /** Test-only: resets the shared singleton between test cases. */
16
- export declare function resetRenderStateForTests(): void;
17
- export interface ResolveAndSetRenderStateOptions {
18
- autoInstall?: boolean | AutoInstallOptions;
19
- defaults?: LilypondDefaults;
20
- timeout?: number;
21
- isDev: boolean;
22
- logger: Pick<AstroIntegrationLogger, "info" | "warn" | "error">;
23
- }
24
- /**
25
- * Shared by the `lilypond()` integration's `astro:config:setup` hook and
26
- * `lilypondLoader()`'s `load()`: resolves the `lilypond` binary and
27
- * populates the render state from it. Returns the resolved binary path for
28
- * callers (e.g. Markdown plugins) that also need it directly.
29
- */
30
- export declare function resolveAndSetRenderState(options: ResolveAndSetRenderStateOptions): Promise<string>;
@@ -1,48 +0,0 @@
1
- import { resolveAutoInstallOption, resolveLilypondBinary, } from "./binary/index.js";
2
- /**
3
- * Keyed on `globalThis` rather than module-scope state — some Astro adapters
4
- * bundle the render step as a separate chunk with its own copy of this
5
- * module, so a plain module `let` wouldn't survive from
6
- * `astro:config:setup` into an actual render.
7
- */
8
- const GLOBAL_KEY = "astro-lilypond:renderState";
9
- function globalStore() {
10
- return globalThis;
11
- }
12
- /** Called by the `lilypond()` integration's `astro:config:setup` hook. */
13
- export function setRenderState(next) {
14
- globalStore()[GLOBAL_KEY] = next;
15
- }
16
- /** Read by the public `render()`, called from user code rather than the integration's own closures. */
17
- export function getRenderState() {
18
- const state = globalStore()[GLOBAL_KEY];
19
- if (!state) {
20
- throw new Error("astro-lilypond: please add the `lilypond()` integration to your Astro config.");
21
- }
22
- return state;
23
- }
24
- /** Test-only: resets the shared singleton between test cases. */
25
- export function resetRenderStateForTests() {
26
- globalStore()[GLOBAL_KEY] = undefined;
27
- }
28
- /**
29
- * Shared by the `lilypond()` integration's `astro:config:setup` hook and
30
- * `lilypondLoader()`'s `load()`: resolves the `lilypond` binary and
31
- * populates the render state from it. Returns the resolved binary path for
32
- * callers (e.g. Markdown plugins) that also need it directly.
33
- */
34
- export async function resolveAndSetRenderState(options) {
35
- const binaryPath = await resolveLilypondBinary({
36
- ...resolveAutoInstallOption(options.autoInstall),
37
- log: (message) => options.logger.info(message),
38
- warn: (message) => options.logger.warn(message),
39
- });
40
- setRenderState({
41
- binaryPath,
42
- defaults: options.defaults,
43
- timeout: options.timeout,
44
- isDev: options.isDev,
45
- logger: options.logger,
46
- });
47
- return binaryPath;
48
- }