@riebeckite/core 0.0.21 → 0.0.22

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.js CHANGED
@@ -1515,9 +1515,9 @@ import remarkDirective from "remark-directive";
1515
1515
  import remarkFrontmatter from "remark-frontmatter";
1516
1516
  import remarkGfm from "remark-gfm";
1517
1517
  import remarkMath from "remark-math";
1518
- import remarkParse from "remark-parse";
1518
+ import remarkParse2 from "remark-parse";
1519
1519
  import remarkRehype from "remark-rehype";
1520
- import { unified } from "unified";
1520
+ import { unified as unified2 } from "unified";
1521
1521
  import { matter as matter2 } from "vfile-matter";
1522
1522
 
1523
1523
  // src/content/content_dependency_tracker.ts
@@ -1896,6 +1896,117 @@ function extractFrontmatter(markdown) {
1896
1896
  return vfile.data.matter ?? {};
1897
1897
  }
1898
1898
 
1899
+ // src/content/markdown_image.ts
1900
+ import remarkParse from "remark-parse";
1901
+ import { unified } from "unified";
1902
+ function extractMarkdownImages(markdown, sourceSlug) {
1903
+ const tree = unified().use(remarkParse).parse(markdown);
1904
+ const definitions = /* @__PURE__ */ new Map();
1905
+ const references = [];
1906
+ const urls = [];
1907
+ visitMarkdownNodes(tree, (node) => {
1908
+ if (node.type === "definition" && typeof node.url === "string") {
1909
+ const identifier = normalizeIdentifier(node.identifier);
1910
+ if (identifier) definitions.set(identifier, node.url);
1911
+ }
1912
+ if (node.type === "image" && typeof node.url === "string") {
1913
+ urls.push(node.url);
1914
+ }
1915
+ if (node.type === "imageReference") {
1916
+ const identifier = normalizeIdentifier(node.identifier);
1917
+ if (identifier) references.push(identifier);
1918
+ }
1919
+ });
1920
+ for (const reference of references) {
1921
+ const url = definitions.get(reference);
1922
+ if (url) urls.push(url);
1923
+ }
1924
+ return uniqueImages(
1925
+ urls.map((url) => resolveMarkdownImage(url, sourceSlug)).filter((image) => image !== null)
1926
+ );
1927
+ }
1928
+ function normalizeMarkdownImages(sourceSlug) {
1929
+ return () => (tree) => {
1930
+ const definitions = /* @__PURE__ */ new Map();
1931
+ const references = /* @__PURE__ */ new Set();
1932
+ visitMarkdownNodes(tree, (node) => {
1933
+ if (node.type === "definition" && typeof node.url === "string") {
1934
+ const identifier = normalizeIdentifier(node.identifier);
1935
+ if (identifier) definitions.set(identifier, node);
1936
+ }
1937
+ if (node.type === "image" && typeof node.url === "string") {
1938
+ const image = resolveMarkdownImage(node.url, sourceSlug);
1939
+ if (image) node.url = image.url;
1940
+ }
1941
+ if (node.type === "imageReference") {
1942
+ const identifier = normalizeIdentifier(node.identifier);
1943
+ if (identifier) references.add(identifier);
1944
+ }
1945
+ });
1946
+ for (const identifier of references) {
1947
+ const definition = definitions.get(identifier);
1948
+ if (!definition || typeof definition.url !== "string") continue;
1949
+ const image = resolveMarkdownImage(definition.url, sourceSlug);
1950
+ if (image) definition.url = image.url;
1951
+ }
1952
+ };
1953
+ }
1954
+ function resolveMarkdownImage(input, sourceSlug) {
1955
+ if (input.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(input)) {
1956
+ return null;
1957
+ }
1958
+ const [pathAndQuery, hash4 = ""] = splitOnce(input, "#");
1959
+ const [rawPath, query = ""] = splitOnce(pathAndQuery, "?");
1960
+ const decodedPath = decodeUriPath(rawPath);
1961
+ if (!decodedPath || !isImagePath(decodedPath)) return null;
1962
+ const path6 = resolveContentAssetPath(sourceSlug, decodedPath);
1963
+ if (!path6) return null;
1964
+ const encodedPath = path6.split("/").map(encodeURIComponent).join("/");
1965
+ const suffix = `${query ? `?${query}` : ""}${hash4 ? `#${hash4}` : ""}`;
1966
+ return { path: path6, url: `/${encodedPath}${suffix}` };
1967
+ }
1968
+ function resolveContentAssetPath(sourceSlug, targetPath) {
1969
+ const sourceDirectory = sourceSlug.includes("/") ? sourceSlug.slice(0, sourceSlug.lastIndexOf("/")) : "";
1970
+ const parts = [
1971
+ ...targetPath.startsWith("/") ? [] : sourceDirectory.split("/"),
1972
+ ...normalizeContentPath(targetPath).replace(/^\/+/, "").split("/")
1973
+ ];
1974
+ const normalized = [];
1975
+ for (const part of parts) {
1976
+ if (!part || part === ".") continue;
1977
+ if (part === "..") {
1978
+ if (normalized.length === 0) return null;
1979
+ normalized.pop();
1980
+ continue;
1981
+ }
1982
+ normalized.push(part);
1983
+ }
1984
+ return normalized.length > 0 ? normalized.join("/").normalize("NFC") : null;
1985
+ }
1986
+ function visitMarkdownNodes(node, visitor) {
1987
+ const markdownNode = node;
1988
+ visitor(markdownNode);
1989
+ for (const child of markdownNode.children ?? [])
1990
+ visitMarkdownNodes(child, visitor);
1991
+ }
1992
+ function normalizeIdentifier(value) {
1993
+ return typeof value === "string" ? value.toLowerCase() : null;
1994
+ }
1995
+ function decodeUriPath(path6) {
1996
+ try {
1997
+ return decodeURI(path6);
1998
+ } catch {
1999
+ return null;
2000
+ }
2001
+ }
2002
+ function splitOnce(value, separator) {
2003
+ const index = value.indexOf(separator);
2004
+ return index < 0 ? [value] : [value.slice(0, index), value.slice(index + 1)];
2005
+ }
2006
+ function uniqueImages(images) {
2007
+ return [...new Map(images.map((image) => [image.path, image])).values()];
2008
+ }
2009
+
1899
2010
  // src/plugin/plugin_cache.ts
1900
2011
  import { createHash as createHash5, randomUUID as randomUUID2 } from "node:crypto";
1901
2012
  import fs3 from "node:fs/promises";
@@ -2178,9 +2289,9 @@ var Pipeline = class {
2178
2289
  this.options.onPersistentContentCacheResult?.("bypass");
2179
2290
  }
2180
2291
  }
2181
- const processor = unified();
2292
+ const processor = unified2();
2182
2293
  const plugins = this.resolvePlugins();
2183
- this.use(processor, remarkParse);
2294
+ this.use(processor, remarkParse2);
2184
2295
  this.use(processor, remarkDirective);
2185
2296
  this.use(processor, remarkFrontmatter, ["yaml", "toml"]);
2186
2297
  this.use(processor, function loadFrontmatter() {
@@ -2220,6 +2331,9 @@ var Pipeline = class {
2220
2331
  ...markdownPipelineContext,
2221
2332
  recordLinkResolution: dependencyTracker ? (id, value) => dependencyTracker.recordLinkResolution(id, value) : void 0
2222
2333
  });
2334
+ if (context.sourceSlug) {
2335
+ this.use(processor, normalizeMarkdownImages(context.sourceSlug));
2336
+ }
2223
2337
  this.use(processor, remarkRehype, { allowDangerousHtml: true });
2224
2338
  this.use(processor, rehypeRaw);
2225
2339
  for (const plugin of plugins) {
@@ -2464,9 +2578,9 @@ function visitMarkdownLinkNodes(node, visitor) {
2464
2578
  function normalizeMarkdownLinkUrl(url, context) {
2465
2579
  if (!context.sourceSlug) return null;
2466
2580
  if (!isMarkdownContentLink(url)) return null;
2467
- const [pathAndQuery, hash4 = ""] = splitOnce(url, "#");
2468
- const [rawPath, query = ""] = splitOnce(pathAndQuery, "?");
2469
- const decodedPath = decodeUriPath(rawPath);
2581
+ const [pathAndQuery, hash4 = ""] = splitOnce2(url, "#");
2582
+ const [rawPath, query = ""] = splitOnce2(pathAndQuery, "?");
2583
+ const decodedPath = decodeUriPath2(rawPath);
2470
2584
  if (!decodedPath?.toLowerCase().endsWith(".md")) return null;
2471
2585
  const resolvedSlug = resolveRelativeMarkdownSlug(
2472
2586
  context.sourceSlug,
@@ -2527,14 +2641,14 @@ function resolveRelativeMarkdownSlug(sourceSlug, targetPath) {
2527
2641
  }
2528
2642
  return normalizedParts.join("/").replace(/\.md$/i, "");
2529
2643
  }
2530
- function decodeUriPath(path6) {
2644
+ function decodeUriPath2(path6) {
2531
2645
  try {
2532
2646
  return decodeURI(path6);
2533
2647
  } catch {
2534
2648
  return null;
2535
2649
  }
2536
2650
  }
2537
- function splitOnce(value, separator) {
2651
+ function splitOnce2(value, separator) {
2538
2652
  const index = value.indexOf(separator);
2539
2653
  if (index < 0) return [value];
2540
2654
  return [value.slice(0, index), value.slice(index + separator.length)];
@@ -2857,6 +2971,13 @@ var PluginRuntime = class {
2857
2971
  collectGeneratedOutputs() {
2858
2972
  return this.generatedOutputs.all();
2859
2973
  }
2974
+ emitCoreAsset(path6, content) {
2975
+ this.generatedOutputs.sinkFor("core").emitAsset({
2976
+ path: path6,
2977
+ content,
2978
+ dependencies: [{ type: "file", path: path6 }]
2979
+ });
2980
+ }
2860
2981
  async resolvePage(pathname, manifest, contentIndex) {
2861
2982
  const matches = [];
2862
2983
  const requestedPathname = normalizePagePath(pathname);
@@ -4008,9 +4129,14 @@ function readOptionalStableId(value) {
4008
4129
  var ManifestBuilder = class {
4009
4130
  createEntry(slug, markdown, processed, contentIndex, location, publishing) {
4010
4131
  const links = extractContentLinks(markdown, contentIndex);
4011
- const assets = links.filter(
4012
- (link) => (link.kind === "image" || link.kind === "attachment") && link.slug !== null
4013
- ).map((link) => ({ path: link.slug }));
4132
+ const assets = [
4133
+ ...links.filter(
4134
+ (link) => (link.kind === "image" || link.kind === "attachment") && link.slug !== null
4135
+ ).map((link) => ({ path: link.slug })),
4136
+ ...extractMarkdownImages(markdown, slug).map((image) => ({
4137
+ path: image.path
4138
+ }))
4139
+ ];
4014
4140
  const contentId = resolveContentStableId(processed.frontmatter);
4015
4141
  return {
4016
4142
  slug,
@@ -4139,6 +4265,11 @@ function determineOutputChanges(input) {
4139
4265
  (input.previousState?.outputs ?? []).map((output) => [output.path, output])
4140
4266
  );
4141
4267
  const changedContent = collectChangedContent(input);
4268
+ const changedFiles = /* @__PURE__ */ new Set([
4269
+ ...input.changeSet.added,
4270
+ ...input.changeSet.changed,
4271
+ ...input.changeSet.removed
4272
+ ]);
4142
4273
  const changedTags = collectChangedTags(
4143
4274
  input.previousState,
4144
4275
  input.manifest,
@@ -4152,7 +4283,13 @@ function determineOutputChanges(input) {
4152
4283
  const affected = /* @__PURE__ */ new Map();
4153
4284
  const unchanged = [];
4154
4285
  for (const output of current) {
4155
- if (isOutputAffected(output, changedContent, changedTags, changedFolders)) {
4286
+ if (isOutputAffected(
4287
+ output,
4288
+ changedContent,
4289
+ changedTags,
4290
+ changedFolders,
4291
+ changedFiles
4292
+ )) {
4156
4293
  affected.set(output.path, output);
4157
4294
  } else {
4158
4295
  unchanged.push(output);
@@ -4225,7 +4362,7 @@ function contentOutput(entry, dependencies) {
4225
4362
  dependencies: [{ type: "content", slug: entry.slug }, ...dependencies]
4226
4363
  };
4227
4364
  }
4228
- function isOutputAffected(output, changedContent, changedTags, changedFolders) {
4365
+ function isOutputAffected(output, changedContent, changedTags, changedFolders, changedFiles) {
4229
4366
  for (const dependency of output.dependencies) {
4230
4367
  if (dependency.type === "unknown") return true;
4231
4368
  if (dependency.type === "global") return changedContent.size > 0;
@@ -4237,6 +4374,9 @@ function isOutputAffected(output, changedContent, changedTags, changedFolders) {
4237
4374
  if (dependency.type === "folder" && changedFolders.has(dependency.folder)) {
4238
4375
  return true;
4239
4376
  }
4377
+ if (dependency.type === "file" && changedFiles.has(dependency.path)) {
4378
+ return true;
4379
+ }
4240
4380
  }
4241
4381
  return false;
4242
4382
  }
@@ -4592,6 +4732,7 @@ var ContentManager = class {
4592
4732
  ...await this.pluginRuntime.collectDiagnostics(contentIndex)
4593
4733
  ];
4594
4734
  await this.pluginRuntime.runBuildEnd(manifest, contentIndex);
4735
+ await this.emitPublicImageAssets(manifest);
4595
4736
  manifest.generatedOutputs = this.pluginRuntime.collectGeneratedOutputs();
4596
4737
  const pluginPageOutputs = await this.pluginRuntime.getPageOutputs(
4597
4738
  manifest,
@@ -4834,6 +4975,21 @@ var ContentManager = class {
4834
4975
  this.isBuildTime = true;
4835
4976
  this.pluginRuntime.enableBuildTime();
4836
4977
  }
4978
+ async emitPublicImageAssets(manifest) {
4979
+ const imagePaths = new Set(
4980
+ manifest.publicEntries.flatMap(
4981
+ (entry) => entry.assets.map((asset) => normalizeContentPath(asset.path)).filter(isImagePath)
4982
+ )
4983
+ );
4984
+ const entriesByPath = new Map(
4985
+ (await this.source.scan()).map((entry) => [entry.path, entry])
4986
+ );
4987
+ for (const path6 of imagePaths) {
4988
+ const entry = entriesByPath.get(path6);
4989
+ if (entry)
4990
+ this.pluginRuntime.emitCoreAsset(path6, await this.source.read(entry));
4991
+ }
4992
+ }
4837
4993
  observability() {
4838
4994
  return this.pipelineOptions.observability ?? noopObservability;
4839
4995
  }
@@ -80,6 +80,7 @@ export declare class ContentManager {
80
80
  private ensureRoutableSlugs;
81
81
  private resolveRoutableSlugs;
82
82
  private enableBuildTime;
83
+ private emitPublicImageAssets;
83
84
  private observability;
84
85
  }
85
86
  declare module "../pipeline" {
@@ -0,0 +1,8 @@
1
+ import type { Node } from "unist";
2
+ type MarkdownImage = {
3
+ readonly path: string;
4
+ readonly url: string;
5
+ };
6
+ export declare function extractMarkdownImages(markdown: string, sourceSlug: string): readonly MarkdownImage[];
7
+ export declare function normalizeMarkdownImages(sourceSlug: string): () => (tree: Node) => void;
8
+ export {};
@@ -2,7 +2,7 @@ import type { OutputDescriptor } from "../content/output_dependency.js";
2
2
  import type { PipelineOptions } from "../pipeline.js";
3
3
  import type { ContentLocationInput, ContentManifest, ContentManifestEntry, ContentPublicLocation, PluginPageRoute } from "../types/content_manifest.js";
4
4
  import type { Diagnostic } from "../types/diagnostic.js";
5
- import type { GeneratedOutput } from "../types/generated_output.js";
5
+ import type { GeneratedOutput, GeneratedOutputContent } from "../types/generated_output.js";
6
6
  import type { OutputDependency } from "../types/output_dependency.js";
7
7
  import { type PluginClientEntry } from "../types/plugin_asset.js";
8
8
  import type { ResolvedPluginPage } from "../types/plugin_page.js";
@@ -58,6 +58,7 @@ export declare class PluginRuntime {
58
58
  collectClientEntries(): PluginClientEntry[];
59
59
  /** Returns registered outputs sorted by path for a deterministic build. */
60
60
  collectGeneratedOutputs(): GeneratedOutput[];
61
+ emitCoreAsset(path: string, content: GeneratedOutputContent): void;
61
62
  resolvePage(pathname: string, manifest: ContentManifest, contentIndex: Map<string, string>): Promise<ResolvedPluginPage | null>;
62
63
  getPagePaths(manifest: ContentManifest, contentIndex: Map<string, string>): Promise<readonly string[]>;
63
64
  getPageRoutes(manifest: ContentManifest, contentIndex: Map<string, string>): Promise<readonly PluginPageRoute[]>;
@@ -7,6 +7,9 @@ export type OutputDependency = {
7
7
  } | {
8
8
  readonly type: "folder";
9
9
  readonly folder: string;
10
+ } | {
11
+ readonly type: "file";
12
+ readonly path: string;
10
13
  } | {
11
14
  readonly type: "global";
12
15
  } | {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/core",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "tsx": "^4.22.4",
52
- "@riebeckite/test": "0.0.21"
52
+ "@riebeckite/test": "0.0.22"
53
53
  },
54
54
  "repository": {
55
55
  "type": "git",