@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import * as nodePath from "node:path";
|
|
3
3
|
import { basename, dirname, extname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
@@ -1344,6 +1344,89 @@ function stripSetupFunction(chunk) {
|
|
|
1344
1344
|
for (const match of chunk.matchAll(SETUP_FUNCTION_PROPERTY)) lastIndex = match.index;
|
|
1345
1345
|
return lastIndex === -1 ? void 0 : chunk.slice(0, lastIndex) + "}";
|
|
1346
1346
|
}
|
|
1347
|
+
/** Index after the whitespace and `//` or block comments starting at `i`. */
|
|
1348
|
+
function skipTrivia(text, i) {
|
|
1349
|
+
let pos = i;
|
|
1350
|
+
for (;;) {
|
|
1351
|
+
const ch = text[pos];
|
|
1352
|
+
if (ch !== void 0 && /\s/.test(ch)) pos++;
|
|
1353
|
+
else if (text.startsWith("//", pos)) {
|
|
1354
|
+
const newline = text.indexOf("\n", pos);
|
|
1355
|
+
pos = newline === -1 ? text.length : newline + 1;
|
|
1356
|
+
} else if (text.startsWith("/*", pos)) {
|
|
1357
|
+
const close = text.indexOf("*/", pos + 2);
|
|
1358
|
+
pos = close === -1 ? text.length : close + 2;
|
|
1359
|
+
} else return pos;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
/** Index after the quoted string starting at `i` (a quote character); template literals nest `${…}`. */
|
|
1363
|
+
function skipString(text, i) {
|
|
1364
|
+
const quote = text[i];
|
|
1365
|
+
let pos = i + 1;
|
|
1366
|
+
while (pos < text.length) {
|
|
1367
|
+
const ch = text[pos];
|
|
1368
|
+
if (ch === "\\") pos += 2;
|
|
1369
|
+
else if (ch === quote) return pos + 1;
|
|
1370
|
+
else if (quote === "`" && ch === "$" && text[pos + 1] === "{") {
|
|
1371
|
+
const close = findClosingBrace(text, pos + 1);
|
|
1372
|
+
if (close === -1) return text.length;
|
|
1373
|
+
pos = close + 1;
|
|
1374
|
+
} else pos++;
|
|
1375
|
+
}
|
|
1376
|
+
return text.length;
|
|
1377
|
+
}
|
|
1378
|
+
function startsComment(text, pos) {
|
|
1379
|
+
return text[pos] === "/" && (text[pos + 1] === "/" || text[pos + 1] === "*");
|
|
1380
|
+
}
|
|
1381
|
+
function startsString(text, pos) {
|
|
1382
|
+
const ch = text[pos];
|
|
1383
|
+
return ch === "\"" || ch === "'" || ch === "`";
|
|
1384
|
+
}
|
|
1385
|
+
/** Index of the `}` matching the `{` at `open`, skipping comments and strings; -1 when unbalanced. */
|
|
1386
|
+
function findClosingBrace(text, open) {
|
|
1387
|
+
let depth = 0;
|
|
1388
|
+
let pos = open;
|
|
1389
|
+
while (pos < text.length) if (startsComment(text, pos)) pos = skipTrivia(text, pos);
|
|
1390
|
+
else if (startsString(text, pos)) pos = skipString(text, pos);
|
|
1391
|
+
else {
|
|
1392
|
+
if (text[pos] === "{") depth++;
|
|
1393
|
+
else if (text[pos] === "}" && --depth === 0) return pos;
|
|
1394
|
+
pos++;
|
|
1395
|
+
}
|
|
1396
|
+
return -1;
|
|
1397
|
+
}
|
|
1398
|
+
const STORY_FORMAT = "storyFormat";
|
|
1399
|
+
const IDENTIFIER_CHAR = /[\w$]/;
|
|
1400
|
+
/**
|
|
1401
|
+
* Locate the object literal passed to `storyFormat(` as `[start, end)` offsets, ignoring comments and
|
|
1402
|
+
* strings outside it and never evaluating anything. Without such a call, the first `{` outside comments
|
|
1403
|
+
* and strings starts the object. When its closing brace cannot be matched (a Harlowe setup function can
|
|
1404
|
+
* hold regular expression literals), the last `}` of the file ends it.
|
|
1405
|
+
*/
|
|
1406
|
+
function locateFormatObject(text) {
|
|
1407
|
+
let start = -1;
|
|
1408
|
+
let firstBrace = -1;
|
|
1409
|
+
let pos = 0;
|
|
1410
|
+
while (pos < text.length && start === -1) if (startsComment(text, pos)) pos = skipTrivia(text, pos);
|
|
1411
|
+
else if (startsString(text, pos)) pos = skipString(text, pos);
|
|
1412
|
+
else if (text[pos] === "{") {
|
|
1413
|
+
if (firstBrace === -1) firstBrace = pos;
|
|
1414
|
+
pos++;
|
|
1415
|
+
} else if (text.startsWith(STORY_FORMAT, pos) && !IDENTIFIER_CHAR.test(text[pos - 1] ?? "")) {
|
|
1416
|
+
const paren = skipTrivia(text, pos + 11);
|
|
1417
|
+
const brace = text[paren] === "(" ? skipTrivia(text, paren + 1) : -1;
|
|
1418
|
+
if (text[brace] === "{") start = brace;
|
|
1419
|
+
pos += 11;
|
|
1420
|
+
} else pos++;
|
|
1421
|
+
if (start === -1) start = firstBrace;
|
|
1422
|
+
if (start === -1) return void 0;
|
|
1423
|
+
const close = findClosingBrace(text, start);
|
|
1424
|
+
const end = close === -1 ? text.lastIndexOf("}") : close;
|
|
1425
|
+
return end < start ? void 0 : {
|
|
1426
|
+
start,
|
|
1427
|
+
end: end + 1
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1347
1430
|
/**
|
|
1348
1431
|
* Parse the format object at `start`–`end` of `text`: strict JSON first (fast), then the
|
|
1349
1432
|
* JavaScript literal subset formats use. Error positions point into `text`.
|
|
@@ -1414,15 +1497,14 @@ function toFormatJSON(raw) {
|
|
|
1414
1497
|
* `harlowe-3` directory or a direct download.
|
|
1415
1498
|
*/
|
|
1416
1499
|
function decodeFormatJSON(source) {
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
if (first === -1 || last < first) return {
|
|
1500
|
+
const located = locateFormatObject(source);
|
|
1501
|
+
if (located === void 0) return {
|
|
1420
1502
|
ok: false,
|
|
1421
1503
|
reason: "Could not find Twine 2 style story format JSON chunk."
|
|
1422
1504
|
};
|
|
1423
|
-
const parsed = parseFormatObject(source,
|
|
1505
|
+
const parsed = parseFormatObject(source, located.start, located.end);
|
|
1424
1506
|
if (parsed.ok) return toFormatJSON(parsed.value);
|
|
1425
|
-
const stripped = stripSetupFunction(source.slice(
|
|
1507
|
+
const stripped = stripSetupFunction(source.slice(located.start, located.end));
|
|
1426
1508
|
const retried = stripped === void 0 ? void 0 : parseFormatObject(stripped, 0, stripped.length);
|
|
1427
1509
|
if (retried?.ok) return toFormatJSON(retried.value);
|
|
1428
1510
|
return {
|
|
@@ -1709,16 +1791,17 @@ function readFormatSource(format, diagnostics) {
|
|
|
1709
1791
|
/**
|
|
1710
1792
|
* Writes `data` to `path` so that a reader sees either the previous file or the complete new
|
|
1711
1793
|
* one, never a partly written file. The data goes to a temporary file in the same folder,
|
|
1712
|
-
* which is then renamed over `path`. When `path` is a symlink, the file it
|
|
1713
|
-
* replaced, and an existing file keeps its permissions.
|
|
1794
|
+
* which is then renamed over `path`. When `path` is a symlink (even a dangling one, or a chain of links), the file it
|
|
1795
|
+
* finally points to is replaced or created and the link stays, and an existing file keeps its permissions.
|
|
1714
1796
|
*
|
|
1715
1797
|
* On failure (a full disk, a missing folder) the temporary file is removed, `path` is left
|
|
1716
1798
|
* as it was, and the error is thrown with `path` in its message and its original `code`.
|
|
1717
1799
|
*/
|
|
1718
1800
|
function writeFileAtomic(path, data) {
|
|
1719
|
-
|
|
1720
|
-
const temp = join(dirname(target), `.${basename(target)}.${process.pid}-${randomBytes(6).toString("hex")}.tmp`);
|
|
1801
|
+
let temp;
|
|
1721
1802
|
try {
|
|
1803
|
+
const target = resolveWriteTarget(path);
|
|
1804
|
+
temp = join(dirname(target), `.${basename(target)}.${process.pid}-${randomBytes(6).toString("hex")}.tmp`);
|
|
1722
1805
|
writeFileSync(temp, data, {
|
|
1723
1806
|
encoding: "utf-8",
|
|
1724
1807
|
flag: "wx"
|
|
@@ -1726,17 +1809,25 @@ function writeFileAtomic(path, data) {
|
|
|
1726
1809
|
keepPermissions(target, temp);
|
|
1727
1810
|
renameSync(temp, target);
|
|
1728
1811
|
} catch (e) {
|
|
1729
|
-
rmSync(temp, { force: true });
|
|
1812
|
+
if (temp !== void 0) rmSync(temp, { force: true });
|
|
1730
1813
|
throw writeError(path, e);
|
|
1731
1814
|
}
|
|
1732
1815
|
}
|
|
1733
|
-
/**
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1816
|
+
/** Links followed before a path counts as a cycle (Linux stops at 40 as well). */
|
|
1817
|
+
const MAX_LINK_DEPTH = 40;
|
|
1818
|
+
/**
|
|
1819
|
+
* The file a symlink at `path` finally points to, or `path` itself when it is no link. Links are
|
|
1820
|
+
* followed one by one, so a link whose target does not exist yet (absolute, relative, or a chain
|
|
1821
|
+
* of them) still resolves to the file that should be created. A cycle or an unreadable link throws.
|
|
1822
|
+
*/
|
|
1823
|
+
function resolveWriteTarget(path) {
|
|
1824
|
+
let current = path;
|
|
1825
|
+
for (let depth = 0; depth <= MAX_LINK_DEPTH; depth++) {
|
|
1826
|
+
const stat = lstatSync(current, { throwIfNoEntry: false });
|
|
1827
|
+
if (stat === void 0 || !stat.isSymbolicLink()) return current;
|
|
1828
|
+
current = resolve(dirname(current), readlinkSync(current));
|
|
1739
1829
|
}
|
|
1830
|
+
throw Object.assign(/* @__PURE__ */ new Error(`ELOOP: too many levels of symbolic links, ${path}`), { code: "ELOOP" });
|
|
1740
1831
|
}
|
|
1741
1832
|
/**
|
|
1742
1833
|
* Gives `temp` the permission bits of `target`, when `target` exists. Best effort: on a file
|
|
@@ -2078,6 +2169,16 @@ async function resolveFormatUrls(request, urls, options = {}) {
|
|
|
2078
2169
|
}
|
|
2079
2170
|
if (lastError) throw lastError;
|
|
2080
2171
|
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Find the best format among the cached copies of direct format URLs, without network access.
|
|
2174
|
+
* Matching follows {@link selectFormatCandidate}, so `allowOlder` admits a same-major older version.
|
|
2175
|
+
* Each URL keeps its own copy; the shared name and version downloads are never consulted.
|
|
2176
|
+
*
|
|
2177
|
+
* Internal, for format resolution; not part of the public API.
|
|
2178
|
+
*/
|
|
2179
|
+
function findCachedUrlFormat(request, urls, options = {}) {
|
|
2180
|
+
return selectFormatCandidate(request, urls.flatMap((url) => getCachedDirectFormat(url) ?? []), (f) => f, options);
|
|
2181
|
+
}
|
|
2081
2182
|
/** Whether `info` is a format the request accepts. */
|
|
2082
2183
|
function answers(request, info) {
|
|
2083
2184
|
return selectFormatCandidate(request, [info], (f) => f) !== void 0;
|
|
@@ -2213,8 +2314,8 @@ function findLocalFormat(all, pruned, request) {
|
|
|
2213
2314
|
* then format indices (unless `noRemote`). Names and IDs match the same way in each (see
|
|
2214
2315
|
* `selectFormatCandidate`), so a request that a local format answers never reaches the cache or the
|
|
2215
2316
|
* network. Local formats that cannot be used are reported as warnings. When a StoryData request
|
|
2216
|
-
* names a version that none of them has, an older version of
|
|
2217
|
-
*
|
|
2317
|
+
* names a version that none of them has, an older version of the same format and major version
|
|
2318
|
+
* is used with a warning, from local formats, the project's cached format URLs, or the shared cache. Anything else is an error diagnostic,
|
|
2218
2319
|
* and the result is `undefined`.
|
|
2219
2320
|
*/
|
|
2220
2321
|
async function resolveStoryFormat(request, options, diagnostics) {
|
|
@@ -2252,7 +2353,7 @@ async function resolveStoryFormat(request, options, diagnostics) {
|
|
|
2252
2353
|
if (remote) return remote;
|
|
2253
2354
|
}
|
|
2254
2355
|
if (request.kind === "name") {
|
|
2255
|
-
const older = selectFormatCandidate(request, rankedTwine2Formats(pruned), (f) => f, { allowOlder: true }) ?? findCachedFormat(request, { allowOlder: true });
|
|
2356
|
+
const older = selectFormatCandidate(request, rankedTwine2Formats(pruned), (f) => f, { allowOlder: true }) ?? findCachedUrlFormat(request, options.formatUrls ?? [], { allowOlder: true }) ?? findCachedFormat(request, { allowOlder: true });
|
|
2256
2357
|
if (older) {
|
|
2257
2358
|
diagnostics.push({
|
|
2258
2359
|
level: "warning",
|
|
@@ -6100,8 +6201,23 @@ const isLetter = (c) => c !== void 0 && /^[A-Za-z]$/.test(c);
|
|
|
6100
6201
|
* a full HTML tokenizer; it needs no more than the tags and where they end.
|
|
6101
6202
|
*/
|
|
6102
6203
|
function scanHeadTags(html) {
|
|
6204
|
+
const { closingHead, bodyStart } = scanTags(html);
|
|
6205
|
+
return {
|
|
6206
|
+
closingHead,
|
|
6207
|
+
bodyStart
|
|
6208
|
+
};
|
|
6209
|
+
}
|
|
6210
|
+
/**
|
|
6211
|
+
* The offset just after the first real head start tag, where content goes to come first in the head; found like
|
|
6212
|
+
* the tags of `scanHeadTags()`, so not one in a comment, a script or an attribute value. `undefined` without one.
|
|
6213
|
+
*/
|
|
6214
|
+
function findHeadStartEnd(html) {
|
|
6215
|
+
return scanTags(html).headStartEnd;
|
|
6216
|
+
}
|
|
6217
|
+
function scanTags(html) {
|
|
6103
6218
|
let closingHead;
|
|
6104
6219
|
let bodyStart;
|
|
6220
|
+
let headStartEnd;
|
|
6105
6221
|
let i = 0;
|
|
6106
6222
|
while (i < html.length) {
|
|
6107
6223
|
const lt = html.indexOf("<", i);
|
|
@@ -6120,13 +6236,15 @@ function scanHeadTags(html) {
|
|
|
6120
6236
|
} else if (isLetter(next)) {
|
|
6121
6237
|
const { name, end } = readTag(html, lt + 1);
|
|
6122
6238
|
if (name === "body" && bodyStart === void 0) bodyStart = lt;
|
|
6239
|
+
if (name === "head" && headStartEnd === void 0) headStartEnd = end;
|
|
6123
6240
|
i = RAW_TEXT_ELEMENTS.has(name) ? rawTextEnd(html, name, end) : end;
|
|
6124
6241
|
} else i = lt + 1;
|
|
6125
6242
|
if (closingHead !== void 0 && bodyStart !== void 0) break;
|
|
6126
6243
|
}
|
|
6127
6244
|
return {
|
|
6128
6245
|
closingHead,
|
|
6129
|
-
bodyStart
|
|
6246
|
+
bodyStart,
|
|
6247
|
+
headStartEnd
|
|
6130
6248
|
};
|
|
6131
6249
|
}
|
|
6132
6250
|
/** Read a tag whose name starts at `from`: its lowercase name, and the offset after its `>` (or the end). */
|
|
@@ -6921,6 +7039,6 @@ function isViteConfigTemp(file) {
|
|
|
6921
7039
|
return /\.timestamp-\d+-[0-9a-f]*\.mjs$/.test(file);
|
|
6922
7040
|
}
|
|
6923
7041
|
//#endregion
|
|
6924
|
-
export { toPosix as a, splitDiagnostics as c,
|
|
7042
|
+
export { toPosix as a, splitDiagnostics as c, getFilenames as d, isExcluded as f, mediaTypeFromFilename as g, walkedEntry as h, outputLocations as i, compileForOutputFile as l, realPathOf as m, isInside as n, fatalError as o, outputPaths as p, isViteConfigTemp as r, formatDiagnostic as s, createOutputRecord as t, findHeadStartEnd as u };
|
|
6925
7043
|
|
|
6926
|
-
//# sourceMappingURL=paths-
|
|
7044
|
+
//# sourceMappingURL=paths-BuTAg2av.js.map
|