@rohal12/twee-ts 1.18.1 → 1.18.2
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/bin/twee-ts.js +132 -21
- package/dist/bin/twee-ts.js.map +1 -1
- package/dist/index.cjs +131 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +132 -21
- package/dist/index.js.map +1 -1
- package/dist/{paths-CUv1qfz-.js → paths-BuTAg2av.js} +141 -23
- package/dist/paths-BuTAg2av.js.map +1 -0
- package/dist/plugins/rollup.js +1 -1
- package/dist/plugins/vite.js +28 -10
- package/dist/plugins/vite.js.map +1 -1
- package/package.json +1 -1
- package/dist/paths-CUv1qfz-.js.map +0 -1
package/dist/bin/twee-ts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { parseArgs } from "node:util";
|
|
3
|
-
import { accessSync, chmodSync, constants, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
|
|
3
|
+
import { accessSync, chmodSync, constants, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
|
|
4
4
|
import * as nodePath from "node:path";
|
|
5
5
|
import { basename, dirname, extname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
@@ -1555,6 +1555,89 @@ function stripSetupFunction(chunk) {
|
|
|
1555
1555
|
for (const match of chunk.matchAll(SETUP_FUNCTION_PROPERTY)) lastIndex = match.index;
|
|
1556
1556
|
return lastIndex === -1 ? void 0 : chunk.slice(0, lastIndex) + "}";
|
|
1557
1557
|
}
|
|
1558
|
+
/** Index after the whitespace and `//` or block comments starting at `i`. */
|
|
1559
|
+
function skipTrivia(text, i) {
|
|
1560
|
+
let pos = i;
|
|
1561
|
+
for (;;) {
|
|
1562
|
+
const ch = text[pos];
|
|
1563
|
+
if (ch !== void 0 && /\s/.test(ch)) pos++;
|
|
1564
|
+
else if (text.startsWith("//", pos)) {
|
|
1565
|
+
const newline = text.indexOf("\n", pos);
|
|
1566
|
+
pos = newline === -1 ? text.length : newline + 1;
|
|
1567
|
+
} else if (text.startsWith("/*", pos)) {
|
|
1568
|
+
const close = text.indexOf("*/", pos + 2);
|
|
1569
|
+
pos = close === -1 ? text.length : close + 2;
|
|
1570
|
+
} else return pos;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
/** Index after the quoted string starting at `i` (a quote character); template literals nest `${…}`. */
|
|
1574
|
+
function skipString(text, i) {
|
|
1575
|
+
const quote = text[i];
|
|
1576
|
+
let pos = i + 1;
|
|
1577
|
+
while (pos < text.length) {
|
|
1578
|
+
const ch = text[pos];
|
|
1579
|
+
if (ch === "\\") pos += 2;
|
|
1580
|
+
else if (ch === quote) return pos + 1;
|
|
1581
|
+
else if (quote === "`" && ch === "$" && text[pos + 1] === "{") {
|
|
1582
|
+
const close = findClosingBrace(text, pos + 1);
|
|
1583
|
+
if (close === -1) return text.length;
|
|
1584
|
+
pos = close + 1;
|
|
1585
|
+
} else pos++;
|
|
1586
|
+
}
|
|
1587
|
+
return text.length;
|
|
1588
|
+
}
|
|
1589
|
+
function startsComment(text, pos) {
|
|
1590
|
+
return text[pos] === "/" && (text[pos + 1] === "/" || text[pos + 1] === "*");
|
|
1591
|
+
}
|
|
1592
|
+
function startsString(text, pos) {
|
|
1593
|
+
const ch = text[pos];
|
|
1594
|
+
return ch === "\"" || ch === "'" || ch === "`";
|
|
1595
|
+
}
|
|
1596
|
+
/** Index of the `}` matching the `{` at `open`, skipping comments and strings; -1 when unbalanced. */
|
|
1597
|
+
function findClosingBrace(text, open) {
|
|
1598
|
+
let depth = 0;
|
|
1599
|
+
let pos = open;
|
|
1600
|
+
while (pos < text.length) if (startsComment(text, pos)) pos = skipTrivia(text, pos);
|
|
1601
|
+
else if (startsString(text, pos)) pos = skipString(text, pos);
|
|
1602
|
+
else {
|
|
1603
|
+
if (text[pos] === "{") depth++;
|
|
1604
|
+
else if (text[pos] === "}" && --depth === 0) return pos;
|
|
1605
|
+
pos++;
|
|
1606
|
+
}
|
|
1607
|
+
return -1;
|
|
1608
|
+
}
|
|
1609
|
+
const STORY_FORMAT = "storyFormat";
|
|
1610
|
+
const IDENTIFIER_CHAR = /[\w$]/;
|
|
1611
|
+
/**
|
|
1612
|
+
* Locate the object literal passed to `storyFormat(` as `[start, end)` offsets, ignoring comments and
|
|
1613
|
+
* strings outside it and never evaluating anything. Without such a call, the first `{` outside comments
|
|
1614
|
+
* and strings starts the object. When its closing brace cannot be matched (a Harlowe setup function can
|
|
1615
|
+
* hold regular expression literals), the last `}` of the file ends it.
|
|
1616
|
+
*/
|
|
1617
|
+
function locateFormatObject(text) {
|
|
1618
|
+
let start = -1;
|
|
1619
|
+
let firstBrace = -1;
|
|
1620
|
+
let pos = 0;
|
|
1621
|
+
while (pos < text.length && start === -1) if (startsComment(text, pos)) pos = skipTrivia(text, pos);
|
|
1622
|
+
else if (startsString(text, pos)) pos = skipString(text, pos);
|
|
1623
|
+
else if (text[pos] === "{") {
|
|
1624
|
+
if (firstBrace === -1) firstBrace = pos;
|
|
1625
|
+
pos++;
|
|
1626
|
+
} else if (text.startsWith(STORY_FORMAT, pos) && !IDENTIFIER_CHAR.test(text[pos - 1] ?? "")) {
|
|
1627
|
+
const paren = skipTrivia(text, pos + 11);
|
|
1628
|
+
const brace = text[paren] === "(" ? skipTrivia(text, paren + 1) : -1;
|
|
1629
|
+
if (text[brace] === "{") start = brace;
|
|
1630
|
+
pos += 11;
|
|
1631
|
+
} else pos++;
|
|
1632
|
+
if (start === -1) start = firstBrace;
|
|
1633
|
+
if (start === -1) return void 0;
|
|
1634
|
+
const close = findClosingBrace(text, start);
|
|
1635
|
+
const end = close === -1 ? text.lastIndexOf("}") : close;
|
|
1636
|
+
return end < start ? void 0 : {
|
|
1637
|
+
start,
|
|
1638
|
+
end: end + 1
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1558
1641
|
/**
|
|
1559
1642
|
* Parse the format object at `start`–`end` of `text`: strict JSON first (fast), then the
|
|
1560
1643
|
* JavaScript literal subset formats use. Error positions point into `text`.
|
|
@@ -1625,15 +1708,14 @@ function toFormatJSON(raw) {
|
|
|
1625
1708
|
* `harlowe-3` directory or a direct download.
|
|
1626
1709
|
*/
|
|
1627
1710
|
function decodeFormatJSON(source) {
|
|
1628
|
-
const
|
|
1629
|
-
|
|
1630
|
-
if (first === -1 || last < first) return {
|
|
1711
|
+
const located = locateFormatObject(source);
|
|
1712
|
+
if (located === void 0) return {
|
|
1631
1713
|
ok: false,
|
|
1632
1714
|
reason: "Could not find Twine 2 style story format JSON chunk."
|
|
1633
1715
|
};
|
|
1634
|
-
const parsed = parseFormatObject(source,
|
|
1716
|
+
const parsed = parseFormatObject(source, located.start, located.end);
|
|
1635
1717
|
if (parsed.ok) return toFormatJSON(parsed.value);
|
|
1636
|
-
const stripped = stripSetupFunction(source.slice(
|
|
1718
|
+
const stripped = stripSetupFunction(source.slice(located.start, located.end));
|
|
1637
1719
|
const retried = stripped === void 0 ? void 0 : parseFormatObject(stripped, 0, stripped.length);
|
|
1638
1720
|
if (retried?.ok) return toFormatJSON(retried.value);
|
|
1639
1721
|
return {
|
|
@@ -1920,16 +2002,17 @@ function readFormatSource(format, diagnostics) {
|
|
|
1920
2002
|
/**
|
|
1921
2003
|
* Writes `data` to `path` so that a reader sees either the previous file or the complete new
|
|
1922
2004
|
* one, never a partly written file. The data goes to a temporary file in the same folder,
|
|
1923
|
-
* which is then renamed over `path`. When `path` is a symlink, the file it
|
|
1924
|
-
* replaced, and an existing file keeps its permissions.
|
|
2005
|
+
* which is then renamed over `path`. When `path` is a symlink (even a dangling one, or a chain of links), the file it
|
|
2006
|
+
* finally points to is replaced or created and the link stays, and an existing file keeps its permissions.
|
|
1925
2007
|
*
|
|
1926
2008
|
* On failure (a full disk, a missing folder) the temporary file is removed, `path` is left
|
|
1927
2009
|
* as it was, and the error is thrown with `path` in its message and its original `code`.
|
|
1928
2010
|
*/
|
|
1929
2011
|
function writeFileAtomic(path, data) {
|
|
1930
|
-
|
|
1931
|
-
const temp = join(dirname(target), `.${basename(target)}.${process.pid}-${randomBytes(6).toString("hex")}.tmp`);
|
|
2012
|
+
let temp;
|
|
1932
2013
|
try {
|
|
2014
|
+
const target = resolveWriteTarget(path);
|
|
2015
|
+
temp = join(dirname(target), `.${basename(target)}.${process.pid}-${randomBytes(6).toString("hex")}.tmp`);
|
|
1933
2016
|
writeFileSync(temp, data, {
|
|
1934
2017
|
encoding: "utf-8",
|
|
1935
2018
|
flag: "wx"
|
|
@@ -1937,17 +2020,25 @@ function writeFileAtomic(path, data) {
|
|
|
1937
2020
|
keepPermissions(target, temp);
|
|
1938
2021
|
renameSync(temp, target);
|
|
1939
2022
|
} catch (e) {
|
|
1940
|
-
rmSync(temp, { force: true });
|
|
2023
|
+
if (temp !== void 0) rmSync(temp, { force: true });
|
|
1941
2024
|
throw writeError(path, e);
|
|
1942
2025
|
}
|
|
1943
2026
|
}
|
|
1944
|
-
/**
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
2027
|
+
/** Links followed before a path counts as a cycle (Linux stops at 40 as well). */
|
|
2028
|
+
const MAX_LINK_DEPTH = 40;
|
|
2029
|
+
/**
|
|
2030
|
+
* The file a symlink at `path` finally points to, or `path` itself when it is no link. Links are
|
|
2031
|
+
* followed one by one, so a link whose target does not exist yet (absolute, relative, or a chain
|
|
2032
|
+
* of them) still resolves to the file that should be created. A cycle or an unreadable link throws.
|
|
2033
|
+
*/
|
|
2034
|
+
function resolveWriteTarget(path) {
|
|
2035
|
+
let current = path;
|
|
2036
|
+
for (let depth = 0; depth <= MAX_LINK_DEPTH; depth++) {
|
|
2037
|
+
const stat = lstatSync(current, { throwIfNoEntry: false });
|
|
2038
|
+
if (stat === void 0 || !stat.isSymbolicLink()) return current;
|
|
2039
|
+
current = resolve(dirname(current), readlinkSync(current));
|
|
1950
2040
|
}
|
|
2041
|
+
throw Object.assign(/* @__PURE__ */ new Error(`ELOOP: too many levels of symbolic links, ${path}`), { code: "ELOOP" });
|
|
1951
2042
|
}
|
|
1952
2043
|
/**
|
|
1953
2044
|
* Gives `temp` the permission bits of `target`, when `target` exists. Best effort: on a file
|
|
@@ -2289,6 +2380,16 @@ async function resolveFormatUrls(request, urls, options = {}) {
|
|
|
2289
2380
|
}
|
|
2290
2381
|
if (lastError) throw lastError;
|
|
2291
2382
|
}
|
|
2383
|
+
/**
|
|
2384
|
+
* Find the best format among the cached copies of direct format URLs, without network access.
|
|
2385
|
+
* Matching follows {@link selectFormatCandidate}, so `allowOlder` admits a same-major older version.
|
|
2386
|
+
* Each URL keeps its own copy; the shared name and version downloads are never consulted.
|
|
2387
|
+
*
|
|
2388
|
+
* Internal, for format resolution; not part of the public API.
|
|
2389
|
+
*/
|
|
2390
|
+
function findCachedUrlFormat(request, urls, options = {}) {
|
|
2391
|
+
return selectFormatCandidate(request, urls.flatMap((url) => getCachedDirectFormat(url) ?? []), (f) => f, options);
|
|
2392
|
+
}
|
|
2292
2393
|
/** Whether `info` is a format the request accepts. */
|
|
2293
2394
|
function answers(request, info) {
|
|
2294
2395
|
return selectFormatCandidate(request, [info], (f) => f) !== void 0;
|
|
@@ -2524,8 +2625,8 @@ function findLocalFormat(all, pruned, request) {
|
|
|
2524
2625
|
* then format indices (unless `noRemote`). Names and IDs match the same way in each (see
|
|
2525
2626
|
* `selectFormatCandidate`), so a request that a local format answers never reaches the cache or the
|
|
2526
2627
|
* network. Local formats that cannot be used are reported as warnings. When a StoryData request
|
|
2527
|
-
* names a version that none of them has, an older version of
|
|
2528
|
-
*
|
|
2628
|
+
* names a version that none of them has, an older version of the same format and major version
|
|
2629
|
+
* is used with a warning, from local formats, the project's cached format URLs, or the shared cache. Anything else is an error diagnostic,
|
|
2529
2630
|
* and the result is `undefined`.
|
|
2530
2631
|
*/
|
|
2531
2632
|
async function resolveStoryFormat(request, options, diagnostics) {
|
|
@@ -2563,7 +2664,7 @@ async function resolveStoryFormat(request, options, diagnostics) {
|
|
|
2563
2664
|
if (remote) return remote;
|
|
2564
2665
|
}
|
|
2565
2666
|
if (request.kind === "name") {
|
|
2566
|
-
const older = selectFormatCandidate(request, rankedTwine2Formats(pruned), (f) => f, { allowOlder: true }) ?? findCachedFormat(request, { allowOlder: true });
|
|
2667
|
+
const older = selectFormatCandidate(request, rankedTwine2Formats(pruned), (f) => f, { allowOlder: true }) ?? findCachedUrlFormat(request, options.formatUrls ?? [], { allowOlder: true }) ?? findCachedFormat(request, { allowOlder: true });
|
|
2567
2668
|
if (older) {
|
|
2568
2669
|
diagnostics.push({
|
|
2569
2670
|
level: "warning",
|
|
@@ -6411,8 +6512,16 @@ const isLetter = (c) => c !== void 0 && /^[A-Za-z]$/.test(c);
|
|
|
6411
6512
|
* a full HTML tokenizer; it needs no more than the tags and where they end.
|
|
6412
6513
|
*/
|
|
6413
6514
|
function scanHeadTags(html) {
|
|
6515
|
+
const { closingHead, bodyStart } = scanTags$1(html);
|
|
6516
|
+
return {
|
|
6517
|
+
closingHead,
|
|
6518
|
+
bodyStart
|
|
6519
|
+
};
|
|
6520
|
+
}
|
|
6521
|
+
function scanTags$1(html) {
|
|
6414
6522
|
let closingHead;
|
|
6415
6523
|
let bodyStart;
|
|
6524
|
+
let headStartEnd;
|
|
6416
6525
|
let i = 0;
|
|
6417
6526
|
while (i < html.length) {
|
|
6418
6527
|
const lt = html.indexOf("<", i);
|
|
@@ -6431,13 +6540,15 @@ function scanHeadTags(html) {
|
|
|
6431
6540
|
} else if (isLetter(next)) {
|
|
6432
6541
|
const { name, end } = readTag(html, lt + 1);
|
|
6433
6542
|
if (name === "body" && bodyStart === void 0) bodyStart = lt;
|
|
6543
|
+
if (name === "head" && headStartEnd === void 0) headStartEnd = end;
|
|
6434
6544
|
i = RAW_TEXT_ELEMENTS.has(name) ? rawTextEnd(html, name, end) : end;
|
|
6435
6545
|
} else i = lt + 1;
|
|
6436
6546
|
if (closingHead !== void 0 && bodyStart !== void 0) break;
|
|
6437
6547
|
}
|
|
6438
6548
|
return {
|
|
6439
6549
|
closingHead,
|
|
6440
|
-
bodyStart
|
|
6550
|
+
bodyStart,
|
|
6551
|
+
headStartEnd
|
|
6441
6552
|
};
|
|
6442
6553
|
}
|
|
6443
6554
|
/** Read a tag whose name starts at `from`: its lowercase name, and the offset after its `>` (or the end). */
|