@slidev/parser 52.15.1 → 52.16.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/core.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as TimesplitOutput, i as TimesplitInput, n as parseRangeString, o as parseTimesplits, r as parseTimeString, t as parseAspectRatio } from "./utils-Bas9O4Gd.mjs";
1
+ import { a as TimesplitOutput, i as TimesplitInput, n as parseRangeString, o as parseTimesplits, r as parseTimeString, t as parseAspectRatio } from "./utils-DpcPp-I0.mjs";
2
2
  import { FontOptions, ResolvedFontOptions, SlidevConfig, SlidevDetectedFeatures, SlidevMarkdown, SlidevPreparserExtension, SlidevThemeMeta, SourceSlideInfo } from "@slidev/types";
3
3
 
4
4
  //#region src/config.d.ts
package/dist/core.mjs CHANGED
@@ -1,7 +1,6 @@
1
- import { i as parseTimeString, n as parseRangeString, r as parseTimesplits, t as parseAspectRatio } from "./utils-CmVawvVO.mjs";
1
+ import { parseAspectRatio, parseRangeString, parseTimeString, parseTimesplits } from "./utils.mjs";
2
2
  import { ensurePrefix, toArray, uniq } from "@antfu/utils";
3
3
  import YAML from "yaml";
4
-
5
4
  //#region src/config.ts
6
5
  const RE_QUOTED_STRING = /^(['"]).*\1$/;
7
6
  const RE_FILENAME_STEM = /([^\\/]+?)(?:\.\w+)?$/;
@@ -192,7 +191,6 @@ function resolveDrawings(options = {}, filepath) {
192
191
  syncAll
193
192
  };
194
193
  }
195
-
196
194
  //#endregion
197
195
  //#region src/core.ts
198
196
  const RE_FRONTMATTER = /^---.*\r?\n([\s\S]*?)---/;
@@ -206,6 +204,22 @@ const RE_MERMAID_CODEBLOCK = /^```mermaid/m;
206
204
  const RE_HEADING = /^(#+) (.*)$/m;
207
205
  const RE_LEADING_BACKTICKS = /^\s*`+/;
208
206
  const RE_CRLF = /\r?\n/g;
207
+ function advanceHtmlCommentState(line, inHtmlComment) {
208
+ let cursor = 0;
209
+ while (cursor < line.length) if (inHtmlComment) {
210
+ const end = line.indexOf("-->", cursor);
211
+ if (end < 0) return true;
212
+ inHtmlComment = false;
213
+ cursor = end + 3;
214
+ } else {
215
+ const start = line.indexOf("<!--", cursor);
216
+ if (start < 0) return false;
217
+ const end = line.indexOf("-->", start + 4);
218
+ if (end < 0) return true;
219
+ cursor = end + 3;
220
+ }
221
+ return inHtmlComment;
222
+ }
209
223
  function stringify(data) {
210
224
  return `${data.slides.map(stringifySlide).join("\n").trim()}\n`;
211
225
  }
@@ -321,6 +335,7 @@ async function parse(markdown, filepath, extensions, options = {}) {
321
335
  const slides = [];
322
336
  let start = 0;
323
337
  let contentStart = 0;
338
+ let inHtmlComment = false;
324
339
  async function slice(end) {
325
340
  if (start === end) return;
326
341
  const slide = {
@@ -351,7 +366,12 @@ async function parse(markdown, filepath, extensions, options = {}) {
351
366
  for (const e of extensions) if (e.transformRawLines) await e.transformRawLines(lines);
352
367
  }
353
368
  for (let i = 0; i < lines.length; i++) {
354
- const line = lines[i].trimEnd();
369
+ const rawLine = lines[i];
370
+ const line = rawLine.trimEnd();
371
+ if (inHtmlComment) {
372
+ inHtmlComment = advanceHtmlCommentState(rawLine, true);
373
+ continue;
374
+ }
355
375
  if (line.startsWith("---")) {
356
376
  await slice(i);
357
377
  const next = lines[i + 1];
@@ -365,7 +385,7 @@ async function parse(markdown, filepath, extensions, options = {}) {
365
385
  let j = i + 1;
366
386
  for (; j < lines.length; j++) if (lines[j].startsWith(codeBlockLevel)) break;
367
387
  if (j !== lines.length) i = j;
368
- }
388
+ } else inHtmlComment = advanceHtmlCommentState(rawLine, false);
369
389
  }
370
390
  if (start <= lines.length - 1) await slice(lines.length);
371
391
  return {
@@ -379,6 +399,7 @@ function parseSync(markdown, filepath, options = {}) {
379
399
  const slides = [];
380
400
  let start = 0;
381
401
  let contentStart = 0;
402
+ let inHtmlComment = false;
382
403
  function slice(end) {
383
404
  if (start === end) return;
384
405
  const slide = {
@@ -394,7 +415,12 @@ function parseSync(markdown, filepath, options = {}) {
394
415
  contentStart = end + 1;
395
416
  }
396
417
  for (let i = 0; i < lines.length; i++) {
397
- const line = lines[i].trimEnd();
418
+ const rawLine = lines[i];
419
+ const line = rawLine.trimEnd();
420
+ if (inHtmlComment) {
421
+ inHtmlComment = advanceHtmlCommentState(rawLine, true);
422
+ continue;
423
+ }
398
424
  if (line.startsWith("---")) {
399
425
  slice(i);
400
426
  const next = lines[i + 1];
@@ -408,7 +434,7 @@ function parseSync(markdown, filepath, options = {}) {
408
434
  let j = i + 1;
409
435
  for (; j < lines.length; j++) if (lines[j].startsWith(codeBlockLevel)) break;
410
436
  if (j !== lines.length) i = j;
411
- }
437
+ } else inHtmlComment = advanceHtmlCommentState(rawLine, false);
412
438
  }
413
439
  if (start <= lines.length - 1) slice(lines.length);
414
440
  return {
@@ -442,13 +468,12 @@ function scanMonacoReferencedMods(md) {
442
468
  };
443
469
  }
444
470
  function hash(str) {
445
- let hash$1 = 0;
471
+ let hash = 0;
446
472
  for (let i = 0; i < str.length; i++) {
447
- hash$1 = (hash$1 << 5) - hash$1 + str.charCodeAt(i);
448
- hash$1 |= 0;
473
+ hash = (hash << 5) - hash + str.charCodeAt(i);
474
+ hash |= 0;
449
475
  }
450
- return hash$1.toString(36).slice(0, 12);
476
+ return hash.toString(36).slice(0, 12);
451
477
  }
452
-
453
478
  //#endregion
454
- export { detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig };
479
+ export { detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig };
package/dist/fs.d.mts CHANGED
@@ -1,15 +1,19 @@
1
- import { a as TimesplitOutput, i as TimesplitInput, n as parseRangeString, o as parseTimesplits, r as parseTimeString, t as parseAspectRatio } from "./utils-Bas9O4Gd.mjs";
1
+ import { a as TimesplitOutput, i as TimesplitInput, n as parseRangeString, o as parseTimesplits, r as parseTimeString, t as parseAspectRatio } from "./utils-DpcPp-I0.mjs";
2
2
  import { SlidevParserOptions, detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseSlide, parseSync, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig } from "./core.mjs";
3
3
  import { PreparserExtensionLoader, SlidevData, SlidevMarkdown } from "@slidev/types";
4
4
 
5
5
  //#region src/fs.d.ts
6
6
  declare function injectPreparserExtensionLoader(fn: PreparserExtensionLoader): void;
7
+ interface LoadRootsInfo {
8
+ roots: string[];
9
+ userRoot: string;
10
+ }
7
11
  /**
8
12
  * Slidev data without config and themeMeta,
9
13
  * because config and themeMeta depends on the theme to be loaded.
10
14
  */
11
15
  type LoadedSlidevData = Omit<SlidevData, 'config' | 'themeMeta'>;
12
- declare function load(userRoot: string, filepath: string, sources?: Record<string, string> | ((path: string) => Promise<string>), mode?: string): Promise<LoadedSlidevData>;
16
+ declare function load(options: LoadRootsInfo, filepath: string, sources?: Record<string, string> | ((path: string) => Promise<string>), mode?: string): Promise<LoadedSlidevData>;
13
17
  declare function save(markdown: SlidevMarkdown): Promise<string>;
14
18
  //#endregion
15
- export { LoadedSlidevData, SlidevParserOptions, TimesplitInput, TimesplitOutput, detectFeatures, extractImagesUsage, getDefaultConfig, injectPreparserExtensionLoader, load, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, save, stringify, stringifySlide, verifyConfig };
19
+ export { LoadRootsInfo, LoadedSlidevData, SlidevParserOptions, TimesplitInput, TimesplitOutput, detectFeatures, extractImagesUsage, getDefaultConfig, injectPreparserExtensionLoader, load, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, save, stringify, stringifySlide, verifyConfig };
package/dist/fs.mjs CHANGED
@@ -1,11 +1,10 @@
1
- import { i as parseTimeString, n as parseRangeString, r as parseTimesplits, t as parseAspectRatio } from "./utils-CmVawvVO.mjs";
1
+ import { parseAspectRatio, parseRangeString, parseTimeString, parseTimesplits } from "./utils.mjs";
2
2
  import { detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseSlide, parseSync, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig } from "./core.mjs";
3
3
  import { slash } from "@antfu/utils";
4
4
  import YAML from "yaml";
5
5
  import { existsSync } from "node:fs";
6
6
  import { readFile, writeFile } from "node:fs/promises";
7
7
  import { dirname, resolve } from "node:path";
8
-
9
8
  //#region src/fs.ts
10
9
  const RE_FRONTMATTER_START = /^---(?:[^-].*)?$/;
11
10
  const RE_BLANK_LINE = /^\s*$/;
@@ -15,7 +14,7 @@ let preparserExtensionLoader = null;
15
14
  function injectPreparserExtensionLoader(fn) {
16
15
  preparserExtensionLoader = fn;
17
16
  }
18
- async function load(userRoot, filepath, sources = {}, mode) {
17
+ async function load(options, filepath, sources = {}, mode) {
19
18
  const loadSource = typeof sources === "function" ? sources : async (path) => sources[path] ?? readFile(path, "utf-8");
20
19
  const markdown = await loadSource(filepath);
21
20
  let extensions;
@@ -28,12 +27,12 @@ async function load(userRoot, filepath, sources = {}, mode) {
28
27
  hm = lines.slice(1, hEnd).join("\n");
29
28
  }
30
29
  const o = YAML.parse(hm) ?? {};
31
- extensions = await preparserExtensionLoader(o, filepath, mode);
30
+ extensions = await preparserExtensionLoader(options.roots, o, filepath, mode);
32
31
  }
33
32
  const markdownFiles = {};
34
33
  const watchFiles = {};
35
34
  const slides = [];
36
- async function loadMarkdown(path, range$1, frontmatterOverride, importers) {
35
+ async function loadMarkdown(path, range, frontmatterOverride, importers) {
37
36
  let md = markdownFiles[path];
38
37
  if (!md) {
39
38
  md = await parse(await loadSource(path), path, extensions);
@@ -41,7 +40,7 @@ async function load(userRoot, filepath, sources = {}, mode) {
41
40
  watchFiles[path] = /* @__PURE__ */ new Set();
42
41
  }
43
42
  const directImporter = importers?.at(-1);
44
- for (const index of parseRangeString(md.slides.length, range$1)) {
43
+ for (const index of parseRangeString(md.slides.length, range)) {
45
44
  const subSlide = md.slides[index - 1];
46
45
  try {
47
46
  await loadSlide(md, subSlide, frontmatterOverride, importers);
@@ -61,7 +60,7 @@ async function load(userRoot, filepath, sources = {}, mode) {
61
60
  if (slide.frontmatter.disabled || slide.frontmatter.hide) return;
62
61
  if (slide.frontmatter.src) {
63
62
  const [rawPath, rangeRaw] = slide.frontmatter.src.split("#");
64
- const path = slash(rawPath.startsWith("/") ? resolve(userRoot, rawPath.substring(1)) : resolve(dirname(slide.filepath), rawPath));
63
+ const path = slash(rawPath.startsWith("/") ? resolve(options.userRoot, rawPath.substring(1)) : resolve(dirname(slide.filepath), rawPath));
65
64
  frontmatterOverride = {
66
65
  ...slide.frontmatter,
67
66
  ...frontmatterOverride
@@ -107,6 +106,5 @@ async function save(markdown) {
107
106
  await writeFile(markdown.filepath, fileContent, "utf-8");
108
107
  return fileContent;
109
108
  }
110
-
111
109
  //#endregion
112
- export { detectFeatures, extractImagesUsage, getDefaultConfig, injectPreparserExtensionLoader, load, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, save, stringify, stringifySlide, verifyConfig };
110
+ export { detectFeatures, extractImagesUsage, getDefaultConfig, injectPreparserExtensionLoader, load, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, save, stringify, stringifySlide, verifyConfig };
package/dist/index.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { a as TimesplitOutput, i as TimesplitInput, n as parseRangeString, o as parseTimesplits, r as parseTimeString, t as parseAspectRatio } from "./utils-Bas9O4Gd.mjs";
1
+ import { a as TimesplitOutput, i as TimesplitInput, n as parseRangeString, o as parseTimesplits, r as parseTimeString, t as parseAspectRatio } from "./utils-DpcPp-I0.mjs";
2
2
  import { SlidevParserOptions, detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseSlide, parseSync, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig } from "./core.mjs";
3
3
  export { SlidevParserOptions, TimesplitInput, TimesplitOutput, detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig };
package/dist/index.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import { i as parseTimeString, n as parseRangeString, r as parseTimesplits, t as parseAspectRatio } from "./utils-CmVawvVO.mjs";
1
+ import { parseAspectRatio, parseRangeString, parseTimeString, parseTimesplits } from "./utils.mjs";
2
2
  import { detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseSlide, parseSync, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig } from "./core.mjs";
3
-
4
- export { detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig };
3
+ export { detectFeatures, extractImagesUsage, getDefaultConfig, parse, parseAspectRatio, parseRangeString, parseSlide, parseSync, parseTimeString, parseTimesplits, prettify, prettifySlide, resolveConfig, resolveFonts, stringify, stringifySlide, verifyConfig };
package/dist/utils.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as TimesplitOutput, i as TimesplitInput, n as parseRangeString, o as parseTimesplits, r as parseTimeString, t as parseAspectRatio } from "./utils-Bas9O4Gd.mjs";
1
+ import { a as TimesplitOutput, i as TimesplitInput, n as parseRangeString, o as parseTimesplits, r as parseTimeString, t as parseAspectRatio } from "./utils-DpcPp-I0.mjs";
2
2
  export { TimesplitInput, TimesplitOutput, parseAspectRatio, parseRangeString, parseTimeString, parseTimesplits };
package/dist/utils.mjs CHANGED
@@ -1,3 +1,144 @@
1
- import { i as parseTimeString, n as parseRangeString, r as parseTimesplits, t as parseAspectRatio } from "./utils-CmVawvVO.mjs";
2
-
3
- export { parseAspectRatio, parseRangeString, parseTimeString, parseTimesplits };
1
+ import { isNumber, range, uniq } from "@antfu/utils";
2
+ //#region src/timesplit/timestring.ts
3
+ /**
4
+ * Parse timestamp into seconds
5
+ *
6
+ * Accepts:
7
+ * - 10:50.1
8
+ * - 10s
9
+ * - 5m
10
+ * - 3min
11
+ * - 3mins 5secs
12
+ * - 10.5m3s
13
+ * - +10s
14
+ * - 1h10m30s
15
+ * - 1h4s
16
+ * - 1:1:1
17
+ */
18
+ const RE_ALPHA = /[a-z]/i;
19
+ function parseTimeString(timestamp) {
20
+ if (typeof timestamp === "number") return {
21
+ seconds: timestamp,
22
+ relative: false
23
+ };
24
+ const relative = timestamp.startsWith("+");
25
+ if (relative) timestamp = timestamp.slice(1);
26
+ let seconds = 0;
27
+ if (timestamp.includes(":")) {
28
+ const parts = timestamp.split(":").map(Number);
29
+ let h = 0;
30
+ let m = 0;
31
+ let s = 0;
32
+ if (parts.length === 3) {
33
+ h = parts[0];
34
+ m = parts[1];
35
+ s = parts[2];
36
+ } else if (parts.length === 2) {
37
+ m = parts[0];
38
+ s = parts[1];
39
+ } else if (parts.length === 1) s = parts[0];
40
+ else throw new TypeError("Invalid timestamp format");
41
+ if (Number.isNaN(h) || Number.isNaN(m) || Number.isNaN(s)) throw new TypeError("Invalid timestamp format");
42
+ seconds = (h || 0) * 3600 + (m || 0) * 60 + (s || 0);
43
+ } else if (!RE_ALPHA.test(timestamp)) seconds = Number(timestamp);
44
+ else {
45
+ const unitMap = {
46
+ s: 1,
47
+ sec: 1,
48
+ secs: 1,
49
+ m: 60,
50
+ min: 60,
51
+ mins: 60,
52
+ h: 3600,
53
+ hr: 3600,
54
+ hrs: 3600,
55
+ hour: 3600,
56
+ hours: 3600,
57
+ day: 86400,
58
+ days: 86400,
59
+ week: 604800,
60
+ weeks: 604800,
61
+ month: 2629746,
62
+ months: 2629746,
63
+ year: 31556952,
64
+ years: 31556952
65
+ };
66
+ const regex = /([\d.]+)([a-z]+)/gi;
67
+ const matches = timestamp.matchAll(regex);
68
+ if (matches) for (const match of matches) {
69
+ const value = Number(match[1]);
70
+ if (Number.isNaN(value)) throw new TypeError(`Invalid timestamp value: ${match[1]}`);
71
+ const unit = match[2].toLowerCase();
72
+ if (!(unit in unitMap)) throw new TypeError(`Invalid timestamp unit: ${unit}`);
73
+ seconds += value * unitMap[unit];
74
+ }
75
+ const remaining = timestamp.replace(regex, "").trim();
76
+ if (remaining) throw new TypeError(`Unknown timestamp remaining: ${remaining}`);
77
+ }
78
+ return {
79
+ seconds,
80
+ relative
81
+ };
82
+ }
83
+ //#endregion
84
+ //#region src/timesplit/timesplit.ts
85
+ function parseTimesplits(inputs) {
86
+ let ts = 0;
87
+ const outputs = [];
88
+ let current = {
89
+ timestampStart: ts,
90
+ timestampEnd: ts,
91
+ noStart: 0,
92
+ noEnd: 0,
93
+ title: "[start]"
94
+ };
95
+ outputs.push(current);
96
+ for (const input of inputs) {
97
+ const time = parseTimeString(input.timesplit);
98
+ const end = time.relative ? ts + time.seconds : time.seconds;
99
+ if (end < ts) throw new Error(`Timesplit end ${end} is before start ${ts}`);
100
+ current.timestampEnd = end;
101
+ current.noEnd = input.no;
102
+ if (input.title) current.title = input.title;
103
+ ts = end;
104
+ current = {
105
+ timestampStart: end,
106
+ timestampEnd: end,
107
+ noStart: input.no,
108
+ noEnd: input.no
109
+ };
110
+ outputs.push(current);
111
+ }
112
+ return outputs;
113
+ }
114
+ //#endregion
115
+ //#region src/utils.ts
116
+ const RE_ASPECT_RATIO_SEPARATOR = /[:/x|]/;
117
+ /**
118
+ * 1,3-5,8 => [1, 3, 4, 5, 8]
119
+ */
120
+ function parseRangeString(total, rangeStr) {
121
+ if (!rangeStr || rangeStr === "all" || rangeStr === "*") return range(1, total + 1);
122
+ if (rangeStr === "none") return [];
123
+ const indexes = [];
124
+ for (const part of rangeStr.split(/[,;]/g)) if (!part.includes("-")) indexes.push(+part);
125
+ else {
126
+ const [start, end] = part.split("-", 2);
127
+ indexes.push(...range(+start, !end ? total + 1 : +end + 1));
128
+ }
129
+ return uniq(indexes).filter((i) => i <= total).sort((a, b) => a - b);
130
+ }
131
+ /**
132
+ * Accepts `16/9` `1:1` `3x4`
133
+ */
134
+ function parseAspectRatio(str) {
135
+ if (isNumber(str)) return str;
136
+ if (!Number.isNaN(+str)) return +str;
137
+ const [wStr = "", hStr = ""] = str.split(RE_ASPECT_RATIO_SEPARATOR);
138
+ const w = Number.parseFloat(wStr.trim());
139
+ const h = Number.parseFloat(hStr.trim());
140
+ if (Number.isNaN(w) || Number.isNaN(h) || h === 0) throw new Error(`Invalid aspect ratio "${str}"`);
141
+ return w / h;
142
+ }
143
+ //#endregion
144
+ export { parseAspectRatio, parseRangeString, parseTimeString, parseTimesplits };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slidev/parser",
3
- "version": "52.15.1",
3
+ "version": "52.16.0",
4
4
  "description": "Markdown parser for Slidev",
5
5
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
6
6
  "license": "MIT",
@@ -30,8 +30,11 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@antfu/utils": "^9.3.0",
33
- "yaml": "^2.8.3",
34
- "@slidev/types": "52.15.1"
33
+ "yaml": "^2.9.0",
34
+ "@slidev/types": "52.16.0"
35
+ },
36
+ "devDependencies": {
37
+ "tsdown-stale-guard": "^0.1.2"
35
38
  },
36
39
  "scripts": {
37
40
  "build": "tsdown",
@@ -1,148 +0,0 @@
1
- import { isNumber, range, uniq } from "@antfu/utils";
2
-
3
- //#region src/timesplit/timestring.ts
4
- /**
5
- * Parse timestamp into seconds
6
- *
7
- * Accepts:
8
- * - 10:50.1
9
- * - 10s
10
- * - 5m
11
- * - 3min
12
- * - 3mins 5secs
13
- * - 10.5m3s
14
- * - +10s
15
- * - 1h10m30s
16
- * - 1h4s
17
- * - 1:1:1
18
- */
19
- const RE_ALPHA = /[a-z]/i;
20
- function parseTimeString(timestamp) {
21
- if (typeof timestamp === "number") return {
22
- seconds: timestamp,
23
- relative: false
24
- };
25
- const relative = timestamp.startsWith("+");
26
- if (relative) timestamp = timestamp.slice(1);
27
- let seconds = 0;
28
- if (timestamp.includes(":")) {
29
- const parts = timestamp.split(":").map(Number);
30
- let h = 0;
31
- let m = 0;
32
- let s = 0;
33
- if (parts.length === 3) {
34
- h = parts[0];
35
- m = parts[1];
36
- s = parts[2];
37
- } else if (parts.length === 2) {
38
- m = parts[0];
39
- s = parts[1];
40
- } else if (parts.length === 1) s = parts[0];
41
- else throw new TypeError("Invalid timestamp format");
42
- if (Number.isNaN(h) || Number.isNaN(m) || Number.isNaN(s)) throw new TypeError("Invalid timestamp format");
43
- seconds = (h || 0) * 3600 + (m || 0) * 60 + (s || 0);
44
- } else if (!RE_ALPHA.test(timestamp)) seconds = Number(timestamp);
45
- else {
46
- const unitMap = {
47
- s: 1,
48
- sec: 1,
49
- secs: 1,
50
- m: 60,
51
- min: 60,
52
- mins: 60,
53
- h: 3600,
54
- hr: 3600,
55
- hrs: 3600,
56
- hour: 3600,
57
- hours: 3600,
58
- day: 86400,
59
- days: 86400,
60
- week: 604800,
61
- weeks: 604800,
62
- month: 2629746,
63
- months: 2629746,
64
- year: 31556952,
65
- years: 31556952
66
- };
67
- const regex = /([\d.]+)([a-z]+)/gi;
68
- const matches = timestamp.matchAll(regex);
69
- if (matches) for (const match of matches) {
70
- const value = Number(match[1]);
71
- if (Number.isNaN(value)) throw new TypeError(`Invalid timestamp value: ${match[1]}`);
72
- const unit = match[2].toLowerCase();
73
- if (!(unit in unitMap)) throw new TypeError(`Invalid timestamp unit: ${unit}`);
74
- seconds += value * unitMap[unit];
75
- }
76
- const remaining = timestamp.replace(regex, "").trim();
77
- if (remaining) throw new TypeError(`Unknown timestamp remaining: ${remaining}`);
78
- }
79
- return {
80
- seconds,
81
- relative
82
- };
83
- }
84
-
85
- //#endregion
86
- //#region src/timesplit/timesplit.ts
87
- function parseTimesplits(inputs) {
88
- let ts = 0;
89
- const outputs = [];
90
- let current = {
91
- timestampStart: ts,
92
- timestampEnd: ts,
93
- noStart: 0,
94
- noEnd: 0,
95
- title: "[start]"
96
- };
97
- outputs.push(current);
98
- for (const input of inputs) {
99
- const time = parseTimeString(input.timesplit);
100
- const end = time.relative ? ts + time.seconds : time.seconds;
101
- if (end < ts) throw new Error(`Timesplit end ${end} is before start ${ts}`);
102
- current.timestampEnd = end;
103
- current.noEnd = input.no;
104
- if (input.title) current.title = input.title;
105
- ts = end;
106
- current = {
107
- timestampStart: end,
108
- timestampEnd: end,
109
- noStart: input.no,
110
- noEnd: input.no
111
- };
112
- outputs.push(current);
113
- }
114
- return outputs;
115
- }
116
-
117
- //#endregion
118
- //#region src/utils.ts
119
- const RE_ASPECT_RATIO_SEPARATOR = /[:/x|]/;
120
- /**
121
- * 1,3-5,8 => [1, 3, 4, 5, 8]
122
- */
123
- function parseRangeString(total, rangeStr) {
124
- if (!rangeStr || rangeStr === "all" || rangeStr === "*") return range(1, total + 1);
125
- if (rangeStr === "none") return [];
126
- const indexes = [];
127
- for (const part of rangeStr.split(/[,;]/g)) if (!part.includes("-")) indexes.push(+part);
128
- else {
129
- const [start, end] = part.split("-", 2);
130
- indexes.push(...range(+start, !end ? total + 1 : +end + 1));
131
- }
132
- return uniq(indexes).filter((i) => i <= total).sort((a, b) => a - b);
133
- }
134
- /**
135
- * Accepts `16/9` `1:1` `3x4`
136
- */
137
- function parseAspectRatio(str) {
138
- if (isNumber(str)) return str;
139
- if (!Number.isNaN(+str)) return +str;
140
- const [wStr = "", hStr = ""] = str.split(RE_ASPECT_RATIO_SEPARATOR);
141
- const w = Number.parseFloat(wStr.trim());
142
- const h = Number.parseFloat(hStr.trim());
143
- if (Number.isNaN(w) || Number.isNaN(h) || h === 0) throw new Error(`Invalid aspect ratio "${str}"`);
144
- return w / h;
145
- }
146
-
147
- //#endregion
148
- export { parseTimeString as i, parseRangeString as n, parseTimesplits as r, parseAspectRatio as t };