@rohal12/twee-ts 1.5.0 → 1.6.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.
@@ -255,8 +255,21 @@ function quote(s) {
255
255
  }
256
256
 
257
257
  // src/story.ts
258
+ var passageIndex = /* @__PURE__ */ new WeakMap();
259
+ function getIndex(story) {
260
+ let index = passageIndex.get(story);
261
+ if (!index) {
262
+ index = /* @__PURE__ */ new Map();
263
+ for (let i = 0; i < story.passages.length; i++) {
264
+ const passage = story.passages[i];
265
+ if (passage) index.set(passage.name, i);
266
+ }
267
+ passageIndex.set(story, index);
268
+ }
269
+ return index;
270
+ }
258
271
  function createStory() {
259
- return {
272
+ const story = {
260
273
  name: "",
261
274
  ifid: "",
262
275
  passages: [],
@@ -272,16 +285,17 @@ function createStory() {
272
285
  zoom: 1
273
286
  }
274
287
  };
288
+ passageIndex.set(story, /* @__PURE__ */ new Map());
289
+ return story;
275
290
  }
276
291
  function storyHas(story, name) {
277
- return story.passages.some((p) => p.name === name);
278
- }
279
- function storyIndex(story, name) {
280
- return story.passages.findIndex((p) => p.name === name);
292
+ return getIndex(story).has(name);
281
293
  }
282
294
  function storyAppend(story, p, diagnostics) {
283
- const i = storyIndex(story, p.name);
295
+ const index = getIndex(story);
296
+ const i = index.get(p.name) ?? -1;
284
297
  if (i === -1) {
298
+ index.set(p.name, story.passages.length);
285
299
  story.passages.push(p);
286
300
  } else {
287
301
  diagnostics.push({
@@ -292,9 +306,15 @@ function storyAppend(story, p, diagnostics) {
292
306
  }
293
307
  }
294
308
  function storyPrepend(story, p, diagnostics) {
295
- const i = storyIndex(story, p.name);
309
+ const index = getIndex(story);
310
+ const i = index.get(p.name) ?? -1;
296
311
  if (i === -1) {
297
312
  story.passages.unshift(p);
313
+ index.clear();
314
+ for (let j = 0; j < story.passages.length; j++) {
315
+ const passage = story.passages[j];
316
+ if (passage) index.set(passage.name, j);
317
+ }
298
318
  } else {
299
319
  diagnostics.push({
300
320
  level: "warning",
@@ -785,6 +805,7 @@ function readFormatSource(format) {
785
805
 
786
806
  // src/loader.ts
787
807
  import { basename } from "path";
808
+ import { statSync as statSync3 } from "fs";
788
809
 
789
810
  // src/lexer.ts
790
811
  var EOF = -1;
@@ -1292,54 +1313,182 @@ function loadInlineSources(story, sources, opts, diagnostics) {
1292
1313
  }
1293
1314
  }
1294
1315
  }
1295
- function loadTwee(story, filename, opts, diagnostics) {
1316
+ function parseTweeFile(filename, opts) {
1296
1317
  const source = readUTF8(filename);
1297
1318
  const result = parseTwee(source, {
1298
1319
  filename,
1299
1320
  trim: opts.trim ?? true,
1300
1321
  twee2Compat: opts.twee2Compat ?? false
1301
1322
  });
1302
- diagnostics.push(...result.diagnostics);
1303
- for (const p of result.passages) {
1304
- storyAdd(story, p, diagnostics);
1305
- }
1323
+ return { passages: result.passages, diagnostics: [...result.diagnostics] };
1306
1324
  }
1307
- function loadTagged(story, tag, filename, diagnostics) {
1325
+ function parseTaggedFile(tag, filename) {
1308
1326
  const source = readUTF8(filename);
1309
- storyAdd(story, { name: basename(filename), tags: [tag], text: source }, diagnostics);
1327
+ return { passages: [{ name: basename(filename), tags: [tag], text: source }], diagnostics: [] };
1310
1328
  }
1311
- function loadMedia(story, tag, filename, diagnostics) {
1329
+ function parseMediaFile(tag, filename) {
1312
1330
  const source = readBase64(filename);
1313
1331
  const name = baseNameWithoutExt(filename);
1314
- storyAdd(
1315
- story,
1316
- {
1317
- name,
1318
- tags: [tag],
1319
- text: `data:${mediaTypeFromFilename(filename)};base64,${source}`
1320
- },
1321
- diagnostics
1322
- );
1332
+ return {
1333
+ passages: [{ name, tags: [tag], text: `data:${mediaTypeFromFilename(filename)};base64,${source}` }],
1334
+ diagnostics: []
1335
+ };
1323
1336
  }
1324
- function loadFont(story, filename, diagnostics) {
1337
+ function parseFontFile(filename) {
1325
1338
  const source = readBase64(filename);
1326
1339
  const name = basename(filename);
1327
1340
  const family = baseNameWithoutExt(filename);
1328
1341
  const ext = normalizedFileExt(filename);
1329
1342
  const mediaType = mediaTypeFromExt(ext);
1330
1343
  const hint = fontFormatHint(ext);
1331
- storyAdd(
1332
- story,
1333
- {
1334
- name,
1335
- tags: ["stylesheet"],
1336
- text: `@font-face {
1344
+ return {
1345
+ passages: [
1346
+ {
1347
+ name,
1348
+ tags: ["stylesheet"],
1349
+ text: `@font-face {
1337
1350
  font-family: "${family}";
1338
1351
  src: url("data:${mediaType};base64,${source}") format("${hint}");
1339
1352
  }`
1340
- },
1341
- diagnostics
1342
- );
1353
+ }
1354
+ ],
1355
+ diagnostics: []
1356
+ };
1357
+ }
1358
+ function parseFile(filename, opts) {
1359
+ const ext = normalizedFileExt(filename);
1360
+ switch (ext) {
1361
+ case "tw":
1362
+ case "twee":
1363
+ return parseTweeFile(filename, opts);
1364
+ case "tw2":
1365
+ case "twee2":
1366
+ return parseTweeFile(filename, { ...opts, twee2Compat: true });
1367
+ case "css":
1368
+ return parseTaggedFile("stylesheet", filename);
1369
+ case "js":
1370
+ return parseTaggedFile("script", filename);
1371
+ case "otf":
1372
+ case "ttf":
1373
+ case "woff":
1374
+ case "woff2":
1375
+ return parseFontFile(filename);
1376
+ case "gif":
1377
+ case "jpeg":
1378
+ case "jpg":
1379
+ case "png":
1380
+ case "svg":
1381
+ case "tif":
1382
+ case "tiff":
1383
+ case "webp":
1384
+ return parseMediaFile("Twine.image", filename);
1385
+ case "aac":
1386
+ case "flac":
1387
+ case "m4a":
1388
+ case "mp3":
1389
+ case "oga":
1390
+ case "ogg":
1391
+ case "opus":
1392
+ case "wav":
1393
+ case "wave":
1394
+ case "weba":
1395
+ return parseMediaFile("Twine.audio", filename);
1396
+ case "mp4":
1397
+ case "ogv":
1398
+ case "webm":
1399
+ return parseMediaFile("Twine.video", filename);
1400
+ case "vtt":
1401
+ return parseMediaFile("Twine.vtt", filename);
1402
+ default:
1403
+ return void 0;
1404
+ }
1405
+ }
1406
+ function loadTwee(story, filename, opts, diagnostics) {
1407
+ const result = parseTweeFile(filename, opts);
1408
+ diagnostics.push(...result.diagnostics);
1409
+ for (const p of result.passages) {
1410
+ storyAdd(story, p, diagnostics);
1411
+ }
1412
+ }
1413
+ function loadTagged(story, tag, filename, diagnostics) {
1414
+ const result = parseTaggedFile(tag, filename);
1415
+ for (const p of result.passages) {
1416
+ storyAdd(story, p, diagnostics);
1417
+ }
1418
+ }
1419
+ function loadMedia(story, tag, filename, diagnostics) {
1420
+ const result = parseMediaFile(tag, filename);
1421
+ for (const p of result.passages) {
1422
+ storyAdd(story, p, diagnostics);
1423
+ }
1424
+ }
1425
+ function loadFont(story, filename, diagnostics) {
1426
+ const result = parseFontFile(filename);
1427
+ for (const p of result.passages) {
1428
+ storyAdd(story, p, diagnostics);
1429
+ }
1430
+ }
1431
+ function loadSourcesCached(story, filenames, opts, diagnostics, processedFiles, cache, changedFiles) {
1432
+ const currentFiles = /* @__PURE__ */ new Set();
1433
+ for (const filename of filenames) {
1434
+ if (processedFiles.has(filename)) {
1435
+ diagnostics.push({ level: "warning", message: `load ${filename}: Skipping duplicate.` });
1436
+ continue;
1437
+ }
1438
+ currentFiles.add(filename);
1439
+ const cached = cache.get(filename);
1440
+ if (changedFiles && cached && !changedFiles.has(filename)) {
1441
+ diagnostics.push(...cached.diagnostics);
1442
+ for (const p of cached.passages) {
1443
+ storyAdd(story, p, diagnostics);
1444
+ }
1445
+ processedFiles.add(filename);
1446
+ continue;
1447
+ }
1448
+ let mtimeMs;
1449
+ try {
1450
+ mtimeMs = statSync3(filename).mtimeMs;
1451
+ } catch {
1452
+ continue;
1453
+ }
1454
+ if (cached && cached.mtimeMs === mtimeMs) {
1455
+ diagnostics.push(...cached.diagnostics);
1456
+ for (const p of cached.passages) {
1457
+ storyAdd(story, p, diagnostics);
1458
+ }
1459
+ processedFiles.add(filename);
1460
+ continue;
1461
+ }
1462
+ try {
1463
+ const result = parseFile(filename, opts);
1464
+ if (!result) continue;
1465
+ cache.set(filename, {
1466
+ mtimeMs,
1467
+ passages: result.passages,
1468
+ diagnostics: result.diagnostics
1469
+ });
1470
+ diagnostics.push(...result.diagnostics);
1471
+ for (const p of result.passages) {
1472
+ storyAdd(story, p, diagnostics);
1473
+ }
1474
+ } catch (e) {
1475
+ diagnostics.push({
1476
+ level: "error",
1477
+ message: `load ${filename}: ${e instanceof Error ? e.message : String(e)}`,
1478
+ file: filename
1479
+ });
1480
+ continue;
1481
+ }
1482
+ processedFiles.add(filename);
1483
+ }
1484
+ for (const key of cache.keys()) {
1485
+ if (!currentFiles.has(key)) {
1486
+ cache.delete(key);
1487
+ }
1488
+ }
1489
+ if (story.name !== "" && !story.passages.some((p) => p.name === "StoryTitle")) {
1490
+ storyPrepend(story, { name: "StoryTitle", tags: [], text: story.name }, diagnostics);
1491
+ }
1343
1492
  }
1344
1493
 
1345
1494
  // src/version.ts
@@ -1608,7 +1757,7 @@ function modifyHead(html, modulePaths, headFile, diagnostics) {
1608
1757
  }
1609
1758
 
1610
1759
  // src/remote-formats.ts
1611
- import { mkdirSync, readFileSync as readFileSync3, writeFileSync, existsSync, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
1760
+ import { mkdirSync, readFileSync as readFileSync3, writeFileSync, existsSync, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
1612
1761
  import { join as join4 } from "path";
1613
1762
  import { homedir as homedir2 } from "os";
1614
1763
  var DEFAULT_SFA_INDICES = [
@@ -1818,7 +1967,7 @@ var TweeTsError = class extends Error {
1818
1967
  async function compile(options) {
1819
1968
  return buildOutput(options);
1820
1969
  }
1821
- async function buildOutput(options) {
1970
+ async function buildOutput(options, cache, changedFiles) {
1822
1971
  const diagnostics = [];
1823
1972
  const outputMode = options.outputMode ?? "html";
1824
1973
  const trim = options.trim ?? true;
@@ -1839,7 +1988,11 @@ async function buildOutput(options) {
1839
1988
  const sourceFilenames = getFilenames(filePaths);
1840
1989
  const story = createStory();
1841
1990
  const processedFiles = /* @__PURE__ */ new Set();
1842
- loadSources(story, sourceFilenames, { trim, twee2Compat }, diagnostics, processedFiles);
1991
+ if (cache) {
1992
+ loadSourcesCached(story, sourceFilenames, { trim, twee2Compat }, diagnostics, processedFiles, cache, changedFiles);
1993
+ } else {
1994
+ loadSources(story, sourceFilenames, { trim, twee2Compat }, diagnostics, processedFiles);
1995
+ }
1843
1996
  loadInlineSources(story, inlineSources, { trim, twee2Compat }, diagnostics);
1844
1997
  if (options.tagAliases) {
1845
1998
  story.passages = applyTagAliases(story.passages, options.tagAliases);
@@ -1980,4 +2133,4 @@ function storyToJSON(story) {
1980
2133
  export {
1981
2134
  compile
1982
2135
  };
1983
- //# sourceMappingURL=chunk-KI4QMLEN.js.map
2136
+ //# sourceMappingURL=chunk-HXL3JC4L.js.map