@mzebley/mark-down 1.0.0 → 1.1.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.
@@ -1,215 +1,412 @@
1
- import matter from "gray-matter";
2
- import { marked } from "marked";
1
+ import { normalizeSlug } from "./slug";
2
+ import { parseFrontMatter } from "./front-matter";
3
+ import { renderMarkdown } from "./markdown";
4
+ import { ManifestLoadError, SnippetNotFoundError } from "./errors";
3
5
  import type {
4
- ListOptions,
5
6
  ManifestSource,
6
7
  ResponseLike,
7
8
  Snippet,
8
9
  SnippetClientOptions,
9
- SnippetFilter,
10
- SnippetMeta
10
+ SnippetFetcherResult,
11
+ SnippetMeta,
12
+ SnippetSearchFilter
11
13
  } from "./types";
12
14
 
13
- const OPTIONAL_FIELDS: Array<keyof SnippetMeta> = [
14
- "title",
15
- "order",
16
- "type",
17
- "tags",
18
- "draft"
19
- ];
15
+ export { ManifestLoadError, SnippetNotFoundError } from "./errors";
16
+
17
+ interface SnippetClientInternalOptions {
18
+ manifest: ManifestSource;
19
+ base?: string;
20
+ fetch: (url: string) => Promise<string>;
21
+ frontMatter: boolean;
22
+ cache: boolean;
23
+ verbose: boolean;
24
+ render: (markdown: string) => Promise<string>;
25
+ }
26
+
27
+ const HTTP_PATTERN = /^https?:\/\//i;
20
28
 
21
29
  export class SnippetClient {
22
- private readonly manifestSource: ManifestSource;
23
- private readonly fetcher: (input: string) => Promise<ResponseLike>;
24
- private readonly markdownRenderer: (markdown: string) => Promise<string> | string;
25
- private readonly resolveSnippetPath: (meta: SnippetMeta) => string;
30
+ private readonly options: SnippetClientInternalOptions;
31
+ private readonly manifestUrl?: string;
32
+ private readonly inferredBase?: string;
33
+
26
34
  private manifestPromise?: Promise<SnippetMeta[]>;
27
- private snippetCache = new Map<string, Promise<Snippet>>();
35
+ private readonly snippetCache = new Map<string, Promise<Snippet>>();
28
36
 
29
37
  constructor(options: SnippetClientOptions) {
30
- this.manifestSource = options.manifest;
31
- this.fetcher = options.fetcher ?? defaultFetch;
32
- this.markdownRenderer = options.markdownRenderer ?? marked.parse;
33
- this.resolveSnippetPath = options.resolveSnippetPath ?? ((meta) => meta.path);
38
+ if (!options || !options.manifest) {
39
+ throw new ManifestLoadError("A manifest source must be provided to SnippetClient.");
40
+ }
41
+
42
+ this.manifestUrl = typeof options.manifest === "string" ? options.manifest : undefined;
43
+ this.inferredBase = this.manifestUrl ? deriveBaseFromManifest(this.manifestUrl) : undefined;
44
+
45
+ const fetcher = options.fetch ?? defaultFetch;
46
+
47
+ const renderOption = options.render;
48
+
49
+ const renderer = renderOption
50
+ ? async (markdown: string) => Promise.resolve(renderOption(markdown))
51
+ : async (markdown: string) => Promise.resolve(renderMarkdown(markdown));
52
+
53
+ this.options = {
54
+ manifest: options.manifest,
55
+ base: options.base,
56
+ fetch: async (url: string) => {
57
+ const response = await fetcher(url);
58
+ if (typeof response === "string") {
59
+ return response;
60
+ }
61
+ return resolveResponseText(response, url);
62
+ },
63
+ frontMatter: options.frontMatter !== false,
64
+ cache: options.cache !== false,
65
+ verbose: options.verbose === true,
66
+ render: renderer
67
+ };
34
68
  }
35
69
 
36
- async get(slug: string): Promise<Snippet | undefined> {
70
+ async get(slug: string): Promise<Snippet> {
37
71
  const manifest = await this.loadManifest();
38
- const entry = manifest.find((snippet) => snippet.slug === slug);
72
+ const entry = manifest.find((item) => item.slug === slug);
39
73
  if (!entry) {
40
- return undefined;
74
+ throw new SnippetNotFoundError(slug);
41
75
  }
42
-
43
76
  return this.loadSnippet(entry);
44
77
  }
45
78
 
46
- async list(filter?: SnippetFilter | ListOptions, options?: ListOptions): Promise<SnippetMeta[]> {
79
+ async listAll(): Promise<SnippetMeta[]> {
47
80
  const manifest = await this.loadManifest();
48
- let predicate: SnippetFilter | undefined;
49
- let finalOptions: ListOptions = {};
50
-
51
- if (typeof filter === "function") {
52
- predicate = filter;
53
- finalOptions = options ?? {};
54
- } else if (filter) {
55
- finalOptions = filter;
56
- } else if (options) {
57
- finalOptions = options;
58
- }
81
+ return manifest.map(cloneMeta);
82
+ }
59
83
 
60
- let items = manifest;
61
- const filters: SnippetFilter[] = [];
62
- if (predicate) {
63
- filters.push(predicate);
64
- }
65
- if (finalOptions.filter) {
66
- filters.push(finalOptions.filter);
67
- }
68
- if (filters.length) {
69
- items = items.filter((entry) => filters.every((fn) => fn(entry)));
70
- }
84
+ async listByGroup(group: string): Promise<SnippetMeta[]> {
85
+ const manifest = await this.loadManifest();
86
+ return manifest
87
+ .filter((item) => (item.group ?? null) === group)
88
+ .map(cloneMeta);
89
+ }
71
90
 
72
- const offset = finalOptions.offset ?? 0;
73
- const limit = finalOptions.limit;
74
- if (offset > 0) {
75
- items = items.slice(offset);
76
- }
77
- if (typeof limit === "number") {
78
- items = items.slice(0, limit);
79
- }
91
+ async listByType(type: string): Promise<SnippetMeta[]> {
92
+ const manifest = await this.loadManifest();
93
+ return manifest
94
+ .filter((item) => item.type === type)
95
+ .map(cloneMeta);
96
+ }
80
97
 
81
- return [...items];
98
+ async search(filter: SnippetSearchFilter): Promise<SnippetMeta[]> {
99
+ const manifest = await this.loadManifest();
100
+ const tags = filter.tags ?? [];
101
+ const mode = filter.tagsMode ?? "any";
102
+
103
+ return manifest
104
+ .filter((item) => {
105
+ if (filter.type && item.type !== filter.type) {
106
+ return false;
107
+ }
108
+ if (filter.group && (item.group ?? undefined) !== filter.group) {
109
+ return false;
110
+ }
111
+ if (!tags.length) {
112
+ return true;
113
+ }
114
+ const metaTags = item.tags ?? [];
115
+ if (!metaTags.length) {
116
+ return false;
117
+ }
118
+ if (mode === "all") {
119
+ return tags.every((tag) => metaTags.includes(tag));
120
+ }
121
+ return tags.some((tag) => metaTags.includes(tag));
122
+ })
123
+ .map(cloneMeta);
124
+ }
125
+
126
+ async getHtml(slug: string): Promise<string> {
127
+ const snippet = await this.get(slug);
128
+ return snippet.html;
129
+ }
130
+
131
+ invalidate(): void {
132
+ this.manifestPromise = undefined;
133
+ this.snippetCache.clear();
82
134
  }
83
135
 
84
- listByType(type: string, options?: ListOptions): Promise<SnippetMeta[]> {
85
- return this.list((entry) => entry.type === type, options);
136
+ invalidateSlug(slug: string): void {
137
+ this.snippetCache.delete(slug);
138
+ }
139
+
140
+ private async loadManifest(): Promise<SnippetMeta[]> {
141
+ if (this.options.cache && this.manifestPromise) {
142
+ return this.manifestPromise;
143
+ }
144
+
145
+ const promise = this.resolveManifest();
146
+ if (this.options.cache) {
147
+ this.manifestPromise = promise;
148
+ }
149
+ return promise;
86
150
  }
87
151
 
88
- listByGroup(group: string, options?: ListOptions): Promise<SnippetMeta[]> {
89
- return this.list((entry) => entry.group === group, options);
152
+ private async resolveManifest(): Promise<SnippetMeta[]> {
153
+ const source = this.options.manifest;
154
+ let entries: SnippetMeta[];
155
+
156
+ try {
157
+ if (Array.isArray(source)) {
158
+ entries = source.map(normalizeManifestEntry);
159
+ } else if (typeof source === "function") {
160
+ const result = await source();
161
+ if (!Array.isArray(result)) {
162
+ throw new ManifestLoadError("Manifest loader must resolve to an array of snippet metadata.");
163
+ }
164
+ entries = result.map(normalizeManifestEntry);
165
+ } else {
166
+ const raw = await this.options.fetch(source);
167
+ entries = parseManifest(raw, source).map(normalizeManifestEntry);
168
+ }
169
+ } catch (error) {
170
+ if (error instanceof ManifestLoadError) {
171
+ throw error;
172
+ }
173
+ throw new ManifestLoadError("Failed to load snippet manifest.", error);
174
+ }
175
+
176
+ return entries.map(cloneMeta);
90
177
  }
91
178
 
92
179
  private loadSnippet(meta: SnippetMeta): Promise<Snippet> {
93
- const cached = this.snippetCache.get(meta.slug);
180
+ const cached = this.options.cache ? this.snippetCache.get(meta.slug) : undefined;
94
181
  if (cached) {
95
182
  return cached;
96
183
  }
97
-
98
184
  const promise = this.fetchSnippet(meta);
99
- this.snippetCache.set(meta.slug, promise);
185
+ if (this.options.cache) {
186
+ this.snippetCache.set(meta.slug, promise);
187
+ }
100
188
  return promise;
101
189
  }
102
190
 
103
191
  private async fetchSnippet(meta: SnippetMeta): Promise<Snippet> {
104
- const snippetPath = this.resolveSnippetPath(meta);
105
- const response = await this.fetcher(snippetPath);
106
- if (!response.ok) {
107
- throw new Error(`Failed to fetch snippet '${meta.slug}' (status ${response.status})`);
192
+ const url = this.resolveSnippetPath(meta.path);
193
+ let raw: string;
194
+ try {
195
+ raw = await this.options.fetch(url);
196
+ } catch (error) {
197
+ throw new ManifestLoadError(`Failed to fetch snippet at '${url}'.`, error);
108
198
  }
109
199
 
110
- const rawContent = await response.text();
111
- const parsed = matter(rawContent);
112
- const frontMatter = sanitizeFrontMatter(parsed.data ?? {});
113
- const mergedMeta = mergeFrontMatter(meta, frontMatter.meta);
200
+ const frontMatter = this.options.frontMatter ? parseFrontMatter(raw) : undefined;
201
+ const body = frontMatter?.content ?? raw;
202
+ const html = await this.options.render(body);
114
203
 
115
- const html = await this.markdownRenderer(parsed.content);
116
-
117
- const baseExtra = mergedMeta.extra ?? {};
118
- return {
119
- ...mergedMeta,
120
- extra: {
121
- ...baseExtra,
122
- ...frontMatter.extra
123
- },
124
- markdown: parsed.content,
125
- html
204
+ const merged: SnippetMeta = {
205
+ ...meta,
206
+ ...pickMeta(frontMatter?.meta),
207
+ extra: mergeExtra(meta.extra, frontMatter?.extra)
126
208
  };
127
- }
128
209
 
129
- private loadManifest(): Promise<SnippetMeta[]> {
130
- if (!this.manifestPromise) {
131
- this.manifestPromise = this.resolveManifest();
210
+ if (frontMatter?.slug) {
211
+ try {
212
+ const normalizedFrontSlug = normalizeSlug(frontMatter.slug);
213
+ if (normalizedFrontSlug !== meta.slug && this.options.verbose) {
214
+ console.warn(
215
+ `Front-matter slug '${frontMatter.slug}' (normalized: '${normalizedFrontSlug}') differs from manifest slug '${meta.slug}'.`
216
+ );
217
+ }
218
+ } catch (error) {
219
+ if (this.options.verbose) {
220
+ console.warn(`Failed to normalize front-matter slug '${frontMatter.slug}':`, error);
221
+ }
222
+ }
223
+ }
224
+
225
+ const snippet: Snippet = { ...merged, html, raw: body, markdown: body };
226
+ if (merged.tags) {
227
+ snippet.tags = [...merged.tags];
132
228
  }
133
- return this.manifestPromise;
229
+ return snippet;
134
230
  }
135
231
 
136
- private async resolveManifest(): Promise<SnippetMeta[]> {
137
- if (Array.isArray(this.manifestSource)) {
138
- return this.manifestSource;
232
+ private resolveSnippetPath(path: string): string {
233
+ if (HTTP_PATTERN.test(path)) {
234
+ return normalizeForwardSlashes(path);
139
235
  }
140
236
 
141
- if (typeof this.manifestSource === "function") {
142
- const result = await this.manifestSource();
143
- return Array.isArray(result) ? result : Promise.reject(new Error("Manifest function must return an array"));
237
+ const base = this.options.base ?? this.inferredBase ?? "";
238
+
239
+ if (path.startsWith("/")) {
240
+ if (base) {
241
+ return joinPaths(base, path);
242
+ }
243
+ return normalizeForwardSlashes(path);
144
244
  }
145
245
 
146
- const raw = await this.fetchText(this.manifestSource);
147
- const manifest = JSON.parse(raw);
148
- if (!Array.isArray(manifest)) {
149
- throw new Error("Manifest must be an array");
246
+ if (base) {
247
+ return joinPaths(base, path);
150
248
  }
151
- return manifest;
249
+ return normalizeForwardSlashes(path);
152
250
  }
251
+ }
153
252
 
154
- private async fetchText(path: string): Promise<string> {
155
- const response = await this.fetcher(path);
156
- if (!response.ok) {
157
- throw new Error(`Failed to fetch manifest from ${path} (status ${response.status})`);
158
- }
159
- return response.text();
253
+ function parseManifest(raw: string, source: string): SnippetMeta[] {
254
+ let parsed: unknown;
255
+ try {
256
+ parsed = JSON.parse(raw);
257
+ } catch (error) {
258
+ throw new ManifestLoadError(`Manifest at '${source}' is not valid JSON.`, error);
259
+ }
260
+
261
+ if (!Array.isArray(parsed)) {
262
+ throw new ManifestLoadError(`Manifest at '${source}' must be a JSON array.`);
160
263
  }
264
+
265
+ return parsed as SnippetMeta[];
161
266
  }
162
267
 
163
- function sanitizeFrontMatter(data: Record<string, unknown>) {
164
- const meta: Partial<SnippetMeta> = {};
165
- const extra: Record<string, unknown> = {};
268
+ function normalizeManifestEntry(entry: SnippetMeta): SnippetMeta {
269
+ if (!entry.slug) {
270
+ throw new ManifestLoadError("Manifest entry is missing required 'slug' property.");
271
+ }
272
+ if (!entry.path) {
273
+ throw new ManifestLoadError(`Manifest entry for '${entry.slug}' is missing required 'path'.`);
274
+ }
275
+ const normalized: SnippetMeta = {
276
+ slug: entry.slug,
277
+ title: entry.title,
278
+ type: entry.type,
279
+ order: entry.order,
280
+ tags: entry.tags ? [...entry.tags] : undefined,
281
+ path: normalizeForwardSlashes(entry.path),
282
+ group: entry.group ?? null,
283
+ draft: entry.draft,
284
+ extra: cloneRecord(entry.extra)
285
+ };
286
+ return normalized;
287
+ }
166
288
 
167
- for (const [key, value] of Object.entries(data)) {
168
- if (OPTIONAL_FIELDS.includes(key as keyof SnippetMeta)) {
169
- if (key === "tags") {
170
- meta.tags = normalizeTags(value);
171
- } else {
172
- (meta as Record<string, unknown>)[key] = value;
173
- }
174
- } else {
175
- extra[key] = value;
289
+ function deriveBaseFromManifest(manifest: string): string | undefined {
290
+ if (HTTP_PATTERN.test(manifest)) {
291
+ try {
292
+ const url = new URL(manifest);
293
+ url.hash = "";
294
+ url.search = "";
295
+ const path = url.pathname;
296
+ url.pathname = path.replace(/[^/]*$/, "");
297
+ return url.toString();
298
+ } catch (error) {
299
+ console.warn(`Unable to derive base from manifest '${manifest}':`, error);
300
+ return undefined;
176
301
  }
177
302
  }
178
303
 
179
- return { meta, extra };
304
+ const sanitized = manifest.replace(/[?#].*$/, "");
305
+ const index = sanitized.lastIndexOf("/");
306
+ if (index === -1) {
307
+ return undefined;
308
+ }
309
+ return sanitized.slice(0, index + 1);
180
310
  }
181
311
 
182
- function normalizeTags(value: unknown): string[] | undefined {
183
- if (!value) {
184
- return undefined;
312
+ function joinPaths(base: string, relative: string): string {
313
+ if (HTTP_PATTERN.test(base)) {
314
+ return new URL(relative, base).toString();
185
315
  }
186
- if (Array.isArray(value)) {
187
- return value.map((tag) => String(tag));
316
+ const leading = base.endsWith("/") ? base : `${base}/`;
317
+ const trimmed = relative.startsWith("/") ? relative.slice(1) : relative;
318
+ return normalizeForwardSlashes(`${leading}${trimmed}`);
319
+ }
320
+
321
+ function normalizeForwardSlashes(value: string): string {
322
+ if (HTTP_PATTERN.test(value)) {
323
+ try {
324
+ const url = new URL(value);
325
+ url.pathname = url.pathname.replace(/\/{2,}/g, "/");
326
+ return url.toString();
327
+ } catch {
328
+ // fall back to manual normalization below
329
+ }
188
330
  }
189
- if (typeof value === "string") {
190
- return value
191
- .split(",")
192
- .map((tag) => tag.trim())
193
- .filter(Boolean);
331
+
332
+ if (value.startsWith("//")) {
333
+ return `//${value.slice(2).replace(/\/{2,}/g, "/")}`;
194
334
  }
195
- return undefined;
335
+
336
+ if (value.startsWith("/")) {
337
+ return `/${value
338
+ .slice(1)
339
+ .replace(/\/{2,}/g, "/")}`;
340
+ }
341
+
342
+ return value.replace(/\/{2,}/g, "/");
196
343
  }
197
344
 
198
- function mergeFrontMatter(base: SnippetMeta, overrides: Partial<SnippetMeta>): SnippetMeta {
199
- const merged: SnippetMeta = { ...base };
200
- for (const field of OPTIONAL_FIELDS) {
201
- const overrideValue = overrides[field];
202
- if (overrideValue !== undefined && merged[field] === undefined) {
203
- merged[field] = overrideValue as never;
204
- }
345
+ function mergeExtra(
346
+ base: Record<string, unknown> | undefined,
347
+ overrides: Record<string, unknown> | undefined
348
+ ): Record<string, unknown> | undefined {
349
+ if (!base && !overrides) {
350
+ return undefined;
351
+ }
352
+ return { ...(base ?? {}), ...(overrides ?? {}) };
353
+ }
354
+
355
+ function cloneRecord<T extends Record<string, unknown> | undefined>(value: T): T {
356
+ if (!value) {
357
+ return value;
205
358
  }
206
- return merged;
359
+ return { ...value } as T;
207
360
  }
208
361
 
209
- async function defaultFetch(input: string): Promise<ResponseLike> {
362
+ function cloneMeta(meta: SnippetMeta): SnippetMeta {
363
+ return {
364
+ ...meta,
365
+ tags: meta.tags ? [...meta.tags] : undefined,
366
+ extra: cloneRecord(meta.extra)
367
+ };
368
+ }
369
+
370
+ async function defaultFetch(url: string): Promise<SnippetFetcherResult> {
210
371
  const runtimeFetch = (globalThis as typeof globalThis & { fetch?: typeof fetch }).fetch;
211
372
  if (!runtimeFetch) {
212
- throw new Error("No global fetch implementation found. Provide a custom fetcher.");
373
+ throw new ManifestLoadError("No global fetch implementation is available. Provide a custom fetch function.");
374
+ }
375
+ return runtimeFetch(url);
376
+ }
377
+
378
+ async function resolveResponseText(response: ResponseLike, url: string): Promise<string> {
379
+ if (!response.ok) {
380
+ throw new ManifestLoadError(`Request to '${url}' failed with status ${response.status}.`);
381
+ }
382
+ return response.text();
383
+ }
384
+
385
+ function pickMeta(meta?: Partial<SnippetMeta>): Partial<SnippetMeta> {
386
+ if (!meta) {
387
+ return {};
388
+ }
389
+ const result: Partial<SnippetMeta> = {};
390
+ if (meta.title !== undefined) {
391
+ result.title = meta.title;
392
+ }
393
+ if (meta.type !== undefined) {
394
+ result.type = meta.type;
395
+ }
396
+ if (meta.order !== undefined) {
397
+ result.order = meta.order;
398
+ }
399
+ if (meta.tags !== undefined) {
400
+ result.tags = [...meta.tags];
401
+ }
402
+ if (meta.group !== undefined) {
403
+ result.group = meta.group;
404
+ }
405
+ if (meta.draft !== undefined) {
406
+ result.draft = meta.draft;
407
+ }
408
+ if (meta.path !== undefined) {
409
+ result.path = meta.path;
213
410
  }
214
- return runtimeFetch(input);
411
+ return result;
215
412
  }
package/src/types.ts CHANGED
@@ -1,26 +1,26 @@
1
1
  export interface SnippetMeta {
2
2
  slug: string;
3
3
  title?: string;
4
- order?: number | null;
5
4
  type?: string;
5
+ order?: number;
6
6
  tags?: string[];
7
- draft?: boolean;
8
7
  path: string;
9
- group: string;
8
+ group?: string | null;
9
+ draft?: boolean;
10
10
  extra?: Record<string, unknown>;
11
11
  }
12
12
 
13
13
  export interface Snippet extends SnippetMeta {
14
- markdown: string;
15
14
  html: string;
15
+ raw?: string;
16
+ markdown?: string;
16
17
  }
17
18
 
18
- export type SnippetFilter = (snippet: SnippetMeta) => boolean;
19
-
20
- export interface ListOptions {
21
- filter?: SnippetFilter;
22
- limit?: number;
23
- offset?: number;
19
+ export interface SnippetSearchFilter {
20
+ type?: string;
21
+ group?: string;
22
+ tags?: string[];
23
+ tagsMode?: "any" | "all";
24
24
  }
25
25
 
26
26
  export type ManifestSource =
@@ -28,19 +28,22 @@ export type ManifestSource =
28
28
  | SnippetMeta[]
29
29
  | (() => Promise<SnippetMeta[]> | SnippetMeta[]);
30
30
 
31
- export interface SnippetClientOptions {
32
- /** Where to load the manifest; defaults to `/snippets-index.json`. */
33
- manifest: ManifestSource;
34
- /** Custom fetch to use in Node environments. Falls back to global `fetch`. */
35
- fetcher?: (input: string) => Promise<ResponseLike>;
36
- /** Allows overriding markdown → HTML rendering. Defaults to `marked`. */
37
- markdownRenderer?: (markdown: string) => Promise<string> | string;
38
- /** Customize how snippet URLs are resolved; defaults to the manifest `path`. */
39
- resolveSnippetPath?: (meta: SnippetMeta) => string;
40
- }
41
-
42
31
  export interface ResponseLike {
43
32
  ok: boolean;
44
33
  status: number;
45
34
  text(): Promise<string>;
46
35
  }
36
+
37
+ export type SnippetFetcherResult = string | ResponseLike;
38
+
39
+ export type SnippetFetcher = (url: string) => Promise<SnippetFetcherResult>;
40
+
41
+ export interface SnippetClientOptions {
42
+ manifest: ManifestSource;
43
+ base?: string;
44
+ fetch?: SnippetFetcher;
45
+ frontMatter?: boolean;
46
+ cache?: boolean;
47
+ verbose?: boolean;
48
+ render?: (markdown: string) => string | Promise<string>;
49
+ }
package/tsup.config.ts CHANGED
@@ -4,16 +4,19 @@ export default defineConfig([
4
4
  {
5
5
  entry: {
6
6
  index: "src/index.ts",
7
- slug: "src/slug.ts"
7
+ slug: "src/slug.ts",
8
+ inline: "src/inline.ts",
9
+ "angular/index": "src/angular/index.ts"
8
10
  },
9
- format: ["esm", "cjs"],
11
+ format: ["esm"],
10
12
  dts: true,
11
13
  sourcemap: true,
12
14
  clean: true,
13
15
  target: "es2020",
14
- outExtension({ format }) {
16
+ external: ["@angular/core", "@angular/common", "@angular/router", "@angular/platform-browser", "rxjs"],
17
+ outExtension() {
15
18
  return {
16
- js: format === "cjs" ? ".cjs" : ".mjs"
19
+ js: ".js"
17
20
  };
18
21
  }
19
22
  },
@@ -33,5 +36,24 @@ export default defineConfig([
33
36
  js: ".js"
34
37
  };
35
38
  }
39
+ },
40
+ {
41
+ entry: {
42
+ "mark-down-inline": "src/inline.ts"
43
+ },
44
+ format: ["iife"],
45
+ globalName: "markDownInline",
46
+ dts: false,
47
+ sourcemap: true,
48
+ clean: false,
49
+ platform: "browser",
50
+ minify: false,
51
+ target: "es2018",
52
+ splitting: false,
53
+ outExtension() {
54
+ return {
55
+ js: ".umd.js"
56
+ };
57
+ }
36
58
  }
37
59
  ]);