limina 0.1.3 → 0.2.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/LICENSE.md +535 -794
- package/bin/limina.js +5 -9
- package/checker-host-process.js +49 -0
- package/chunks/dep-BOg2f5FD.js +42 -0
- package/chunks/{dep-BizOzD0V.js → dep-D63Sc9OL.js} +999 -176
- package/chunks/dep-gI8bWpox.js +1092 -0
- package/cli.js +39713 -39849
- package/flow-renderer-process.js +1 -1
- package/index.d.ts +209 -50
- package/index.js +2 -2
- package/package.json +20 -15
- package/schemas/tsconfig-schema.json +4 -1
- package/chunks/dep-BT-sPH_d.js +0 -103
- package/chunks/dep-CDo4z58I.js +0 -191
- package/chunks/dep-Tb3jeYdU.js +0 -240
package/chunks/dep-CDo4z58I.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { a as __toESM, t as require_picocolors } from "./dep-BT-sPH_d.js";
|
|
2
|
-
//#region ../../node_modules/.pnpm/publint@0.3.17/node_modules/publint/src/shared/utils.js
|
|
3
|
-
/**
|
|
4
|
-
* @param {string[]} path
|
|
5
|
-
* @returns {string}
|
|
6
|
-
*/
|
|
7
|
-
function formatMessagePath(path) {
|
|
8
|
-
let formatted = "pkg";
|
|
9
|
-
for (const part of path) if (/^\d+$/.test(part)) formatted += `[${part}]`;
|
|
10
|
-
else if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(part)) formatted += `["${part}"]`;
|
|
11
|
-
else formatted += "." + part;
|
|
12
|
-
return formatted;
|
|
13
|
-
}
|
|
14
|
-
/** @type {import('../utils.d.ts').getPkgPathValue} */
|
|
15
|
-
function getPkgPathValue(pkg, path) {
|
|
16
|
-
let v = pkg;
|
|
17
|
-
for (const p of path) v = v[p];
|
|
18
|
-
return v;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* @param {string} str
|
|
22
|
-
* @param {string} search
|
|
23
|
-
* @param {string} replace
|
|
24
|
-
*/
|
|
25
|
-
function replaceLast(str, search, replace) {
|
|
26
|
-
const index = str.lastIndexOf(search);
|
|
27
|
-
if (index === -1) return str;
|
|
28
|
-
return str.slice(0, index) + replace + str.slice(index + search.length);
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
//#region ../../node_modules/.pnpm/publint@0.3.17/node_modules/publint/src/shared/message.js
|
|
32
|
-
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
33
|
-
const EMPTY_MSG = "<empty>";
|
|
34
|
-
/** @type {import('../utils.d.ts').formatMessage} */
|
|
35
|
-
function formatMessage(m, pkg, opts = {}) {
|
|
36
|
-
const h = getHighlighter(opts.color);
|
|
37
|
-
/** @param {string[]} path */
|
|
38
|
-
const pv = (path) => {
|
|
39
|
-
try {
|
|
40
|
-
const str = String(getPkgPathValue(pkg, path));
|
|
41
|
-
return str === "" ? EMPTY_MSG : str;
|
|
42
|
-
} catch {
|
|
43
|
-
return "undefined";
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
switch (m.code) {
|
|
47
|
-
case "IMPLICIT_INDEX_JS_INVALID_FORMAT": return `index.js should be ${m.args.expectFormat} but it is ${m.args.actualFormat}`;
|
|
48
|
-
case "FILE_INVALID_FORMAT": {
|
|
49
|
-
const relativePath = m.args.actualFilePath ?? pv(m.path);
|
|
50
|
-
let start = "";
|
|
51
|
-
if (opts.reference || m.path[0] === "name") start = h.bold(relativePath);
|
|
52
|
-
else {
|
|
53
|
-
const is = pv(m.path).includes("*") ? "matches" : "is";
|
|
54
|
-
start = `${h.bold(formatMessagePath(m.path))} ${is} ${h.bold(relativePath)} and`;
|
|
55
|
-
}
|
|
56
|
-
return `${start} is written in ${h.warn(m.args.actualFormat)}, but is interpreted as ${h.warn(m.args.expectFormat)}. Consider using the ${h.warn(m.args.expectExtension)} extension, e.g. ${h.bold(replaceLast(relativePath, ".js", m.args.expectExtension))}`;
|
|
57
|
-
}
|
|
58
|
-
case "FILE_INVALID_EXPLICIT_FORMAT": {
|
|
59
|
-
const relativePath = m.args.actualFilePath ?? pv(m.path);
|
|
60
|
-
let start = "";
|
|
61
|
-
if (opts.reference || m.path[0] === "name") start = h.bold(relativePath);
|
|
62
|
-
else {
|
|
63
|
-
const is = pv(m.path).includes("*") ? "matches" : "is";
|
|
64
|
-
start = `${h.bold(formatMessagePath(m.path))} ${is} ${h.bold(relativePath)} and`;
|
|
65
|
-
}
|
|
66
|
-
return `${start} ends with the ${h.warn(m.args.actualExtension)} extension, but the code is written in ${h.warn(m.args.actualFormat)}. Consider using the ${h.warn(m.args.expectExtension)} extension, e.g. ${h.bold(replaceLast(relativePath, m.args.actualExtension, m.args.expectExtension))}`;
|
|
67
|
-
}
|
|
68
|
-
case "FILE_INVALID_JSX_EXTENSION": {
|
|
69
|
-
const relativePath = m.args.globbedFilePath ?? pv(m.path);
|
|
70
|
-
let start = "";
|
|
71
|
-
if (opts.reference || m.path[0] === "name") start = h.bold(relativePath);
|
|
72
|
-
else {
|
|
73
|
-
const is = m.args.globbedFilePath ? "matches" : "is";
|
|
74
|
-
start = `${h.bold(formatMessagePath(m.path))} ${is} ${h.bold(relativePath)} and`;
|
|
75
|
-
}
|
|
76
|
-
return `${start} uses an invalid ${h.bold(m.args.actualExtension)} extension. You don't need to split ESM and CJS formats for JSX. You should write a single file in ESM with the ${h.bold(".jsx")} extension instead, e.g. ${h.bold(replaceLast(pv(m.path), m.args.actualExtension, ".jsx"))}`;
|
|
77
|
-
}
|
|
78
|
-
case "FILE_DOES_NOT_EXIST": if (opts.reference) return `File does not exist`;
|
|
79
|
-
else {
|
|
80
|
-
let value = pv(m.path);
|
|
81
|
-
if (value === EMPTY_MSG && m.path[0] === "main") value += " (implies index.js)";
|
|
82
|
-
return `${h.bold(formatMessagePath(m.path))} is ${h.bold(value)} but the file does not exist.`;
|
|
83
|
-
}
|
|
84
|
-
case "FILE_NOT_PUBLISHED": if (opts.reference) return `File is not published. Is it specified in ${h.bold("pkg.files")}?`;
|
|
85
|
-
else return `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} but the file is not published. Is it specified in ${h.bold("pkg.files")}?`;
|
|
86
|
-
case "HAS_ESM_MAIN_BUT_NO_EXPORTS": return `${h.bold("pkg.main")} is an ESM file, but it is usually better to use ${h.bold("pkg.exports")} instead. If you don't support Node.js 12.6 and below, you can also remove ${h.bold("pkg.main")}. (This will be a breaking change)`;
|
|
87
|
-
case "HAS_MODULE_BUT_NO_EXPORTS": return `${h.bold("pkg.module")} is used to output ESM, but ${h.bold("pkg.exports")} is not defined. As Node.js doesn't read ${h.bold("pkg.module")}, the ESM output may be skipped. Consider adding ${h.bold("pkg.exports")} to export the ESM output. ${h.bold("pkg.module")} can usually be removed alongside too. (This will be a breaking change)`;
|
|
88
|
-
case "MODULE_SHOULD_BE_ESM":
|
|
89
|
-
case "EXPORTS_MODULE_SHOULD_BE_ESM":
|
|
90
|
-
case "IMPORTS_MODULE_SHOULD_BE_ESM": return `${opts.reference ? "Should" : `${h.bold(formatMessagePath(m.path))} should`} be ESM, but the code is written in CJS.`;
|
|
91
|
-
case "EXPORTS_GLOB_NO_MATCHED_FILES":
|
|
92
|
-
case "IMPORTS_GLOB_NO_MATCHED_FILES": return `${opts.reference ? "Does" : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} but does`} not match any files.`;
|
|
93
|
-
case "EXPORTS_GLOB_NO_DEPRECATED_SUBPATH_MAPPING":
|
|
94
|
-
case "IMPORTS_GLOB_NO_DEPRECATED_SUBPATH_MAPPING": return `${h.bold(formatMessagePath(m.path))} maps to a path that ends with ${h.bold("/")} which is a removed feature. Use ${h.bold(formatMessagePath(m.args.expectPath))}: "${h.bold(m.args.expectValue)}" instead.`;
|
|
95
|
-
case "EXPORTS_TYPES_SHOULD_BE_FIRST": return `${opts.reference ? "Should" : `${h.bold(formatMessagePath(m.path))} should`} be the first in the object as conditions are order-sensitive so it can be resolved by TypeScript.`;
|
|
96
|
-
case "EXPORTS_MODULE_SHOULD_PRECEDE_REQUIRE":
|
|
97
|
-
case "IMPORTS_MODULE_SHOULD_PRECEDE_REQUIRE": return `${opts.reference ? "Should" : `${h.bold(formatMessagePath(m.path))} should`} come before the "require" condition so it can take precedence when used by a bundler.`;
|
|
98
|
-
case "EXPORTS_DEFAULT_SHOULD_BE_LAST":
|
|
99
|
-
case "IMPORTS_DEFAULT_SHOULD_BE_LAST": return `${opts.reference ? "Should" : `${h.bold(formatMessagePath(m.path))} should`} be the last in the object so it doesn't take precedence over the keys following it.`;
|
|
100
|
-
case "EXPORTS_VALUE_INVALID":
|
|
101
|
-
case "IMPORTS_VALUE_INVALID": return `${opts.reference ? h.bold(pv(m.path)) : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} but`} is invalid as it does not start with "${h.bold("./")}". Use ${h.bold(m.args.suggestValue)} instead.`;
|
|
102
|
-
case "EXPORTS_FALLBACK_ARRAY_USE":
|
|
103
|
-
case "IMPORTS_FALLBACK_ARRAY_USE": return `${opts.reference ? "The value" : h.bold(formatMessagePath(m.path))} uses fallback arrays which is not recommended. It picks the first value that can be parsed and does not have a use case in Node.js currently. It also works differently in some tools and may face inconsistent behaviors.`;
|
|
104
|
-
case "EXPORTS_MISSING_ROOT_ENTRYPOINT": {
|
|
105
|
-
const mainField = m.args.mainFields[0];
|
|
106
|
-
return `${opts.reference ? "The root entrypoint is missing" : `${h.bold(formatMessagePath(m.path))} is missing the root entrypoint export`}, which is defined in ${h.bold("pkg." + mainField)}. Environments that support the ${h.bold("\"exports\"")} field will ignore ${h.bold("pkg." + mainField)} as ${h.bold("\"exports\"")} takes the highest priority. Consider adding ${h.bold(formatMessagePath(m.path.concat(".")))}: "${h.bold(pv([mainField]))}".`;
|
|
107
|
-
}
|
|
108
|
-
case "USE_EXPORTS_BROWSER": return `${h.bold("pkg.browser")} with a string value can be refactored to use ${h.bold("pkg.exports")} and the ${h.bold("\"browser\"")} condition to declare browser-specific exports. e.g. ${h.bold("pkg.exports[\".\"].browser")}: "${h.bold(pv(m.path))}". (This will be a breaking change)`;
|
|
109
|
-
case "USE_EXPORTS_OR_IMPORTS_BROWSER": return `${h.bold("pkg.browser")} with an object value can be refactored to use ${h.bold("pkg.exports")}/${h.bold("pkg.imports")} and the ${h.bold("\"browser\"")} condition to declare browser-specific exports. (This will be a breaking change)`;
|
|
110
|
-
case "USE_FILES": return `The package ${h.bold("publishes internal tests or config files")}. You can use ${h.bold("pkg.files")} to only publish certain files and save user bandwidth.`;
|
|
111
|
-
case "USE_TYPE": return `The package does not specify the ${h.bold("\"type\"")} field. Node.js may attempt to detect the package type causing a small performance hit. Consider adding ${h.bold("\"type\"")}: "${h.bold("commonjs")}".`;
|
|
112
|
-
case "USE_LICENSE": return `The package does not specify the ${h.bold("\"license\"")} field but a license file was detected at ${h.bold(m.args.licenseFilePath)}. Consider adding a ${h.bold("\"license\"")} field so it's displayed on npm.`;
|
|
113
|
-
case "TYPES_NOT_EXPORTED": {
|
|
114
|
-
const typesFilePath = exportsRel(m.args.typesFilePath);
|
|
115
|
-
const start = opts.reference ? "The" : h.bold(formatMessagePath(m.path));
|
|
116
|
-
if (m.args.actualExtension && m.args.expectExtension) return `${start} types is not exported. Consider adding ${h.bold(formatMessagePath(m.path) + ".types")} to be compatible with TypeScript's ${h.bold("\"moduleResolution\": \"bundler\"")} compiler option. Note that you cannot use "${h.bold(typesFilePath)}" because it has a mismatching format. Instead, you can duplicate the file and use the ${h.bold(m.args.expectExtension)} extension, e.g. ${h.bold(formatMessagePath(m.path) + ".types")}: "${h.bold(replaceLast(typesFilePath, m.args.actualExtension, m.args.expectExtension))}"`;
|
|
117
|
-
else return `${start} types is not exported. Consider adding ${h.bold(formatMessagePath(m.path) + ".types")}: "${h.bold(typesFilePath)}" to be compatible with TypeScript's ${h.bold("\"moduleResolution\": \"bundler\"")} compiler option.`;
|
|
118
|
-
}
|
|
119
|
-
case "EXPORTS_TYPES_INVALID_FORMAT": {
|
|
120
|
-
let additionalMessage = "";
|
|
121
|
-
if (m.args.expectFormat === "ESM" && m.args.actualFormat === "CJS") additionalMessage = `This causes the types to be ambiguous when default importing the package due to its implied interop. `;
|
|
122
|
-
else if (m.args.expectFormat === "CJS" && m.args.actualFormat === "ESM") additionalMessage = `This causes the types to only work when dynamically importing the package, even though the package exports CJS. `;
|
|
123
|
-
return `${opts.reference ? "The" : h.bold(formatMessagePath(m.path))} types is interpreted as ${m.args.actualFormat} when resolving with the "${h.bold(m.args.condition)}" condition. ` + additionalMessage + `Consider splitting out two ${h.bold("\"types\"")} conditions for ${h.bold("\"import\"")} and ${h.bold("\"require\"")}, and use the ${h.warn(m.args.expectExtension)} extension, e.g. ${h.bold(formatMessagePath(m.args.expectPath))}: "${h.bold(replaceLast(pv(m.path), m.args.actualExtension, m.args.expectExtension))}"`;
|
|
124
|
-
}
|
|
125
|
-
case "FIELD_INVALID_VALUE_TYPE": {
|
|
126
|
-
let expectStr = m.args.expectTypes[0];
|
|
127
|
-
for (let i = 1; i < m.args.expectTypes.length; i++) if (i === m.args.expectTypes.length - 1) expectStr += ` or ${m.args.expectTypes[i]}`;
|
|
128
|
-
else expectStr += `, ${m.args.expectTypes[i]}`;
|
|
129
|
-
return `${opts.reference ? "The field value has" : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} which is`} an invalid ${h.bold(m.args.actualType)} type. Expected a ${h.bold(expectStr)} type instead.`;
|
|
130
|
-
}
|
|
131
|
-
case "EXPORTS_VALUE_CONFLICTS_WITH_BROWSER": return `${opts.reference ? `${h.bold(pv(m.path))} matches` : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} which also matches`} ${h.bold(formatMessagePath(m.args.browserPath))}: "${h.bold(pv(m.args.browserPath))}", which overrides the path when building the library with the "${h.bold(m.args.browserishCondition)}" condition. This is usually unintentional and may cause build issues. Consider using a different file name for ${h.bold(pv(m.path))}.`;
|
|
132
|
-
case "DEPRECATED_FIELD_JSNEXT": return `${h.bold(formatMessagePath(m.path))} is deprecated. ${h.bold("pkg.module")} should be used instead.`;
|
|
133
|
-
case "INVALID_REPOSITORY_VALUE": switch (m.args.type) {
|
|
134
|
-
case "invalid-string-shorthand": return `${opts.reference ? "The field value" : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} which`} isn't a valid shorthand value supported by npm. Consider using an object that references a repository.`;
|
|
135
|
-
case "invalid-git-url": return `${opts.reference ? "The field value" : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} which`} isn't a valid git URL. A valid git URL is usually in the form of "${h.bold("git+https://example.com/user/repo.git")}".`;
|
|
136
|
-
case "deprecated-github-git-protocol": return `${opts.reference ? "The field value" : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} which`} uses the git:// protocol that is deprecated by GitHub due to security concerns. Consider replacing the protocol with https://.`;
|
|
137
|
-
case "shorthand-git-sites": return `${opts.reference ? "The field value" : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} but`} could be a full git URL like "${h.bold(m.args.suggestValue)}".`;
|
|
138
|
-
}
|
|
139
|
-
case "LOCAL_DEPENDENCY": if (opts.reference) return `This dependency references a local package that will likely not work when installed by end-users.`;
|
|
140
|
-
else return `The "${h.bold(m.path[m.path.length - 1])}" dependency references "${h.bold(pv(m.path))}" that will likely not work when installed by end-users.`;
|
|
141
|
-
case "BIN_FILE_NOT_EXECUTABLE": return `${opts.reference ? "This bin file" : `${h.bold(formatMessagePath(m.path))} is ${h.bold(pv(m.path))} but the file`} is not executable. It should start with a shebang, e.g. ${h.bold("#!/usr/bin/env node")}.`;
|
|
142
|
-
case "IMPORTS_KEY_INVALID": return `${opts.reference ? `The imports key is invalid as it` : `${h.bold(formatMessagePath(m.path))} is invalid as the imports key`} does not start with "${h.bold("#")}". Use ${h.bold(m.args.suggestKey)} instead.`;
|
|
143
|
-
case "CJS_WITH_ESMODULE_DEFAULT_EXPORT": return `${m.path[0] === "name" && m.args.filePath ? h.bold(m.args.filePath) : opts.reference ? "This file" : `${h.bold(formatMessagePath(m.path))}`} sets both ${h.bold("exports.__esModule = true")} and ${h.bold("exports.default")}. This pattern breaks default imports when importing from an ESM-interpreted file in some bundlers. Consider using ESM syntax instead of CommonJS with ${h.bold("__esModule")} or avoid the default export.`;
|
|
144
|
-
default: return;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
/** @type { import('picocolors/types.js').Colors | undefined } */
|
|
148
|
-
let _picocolorsWithForcedColor;
|
|
149
|
-
/**
|
|
150
|
-
* @param {import('../utils.js').FormatMessageOptions['color']} color
|
|
151
|
-
*/
|
|
152
|
-
function getHighlighter(color) {
|
|
153
|
-
/** @type {(s: string) => string} */
|
|
154
|
-
let bold;
|
|
155
|
-
/** @type {(s: string) => string} */
|
|
156
|
-
let warn;
|
|
157
|
-
switch (color) {
|
|
158
|
-
case "html":
|
|
159
|
-
bold = (s) => `<strong>${s}</strong>`;
|
|
160
|
-
warn = (s) => `<strong>${s}</strong>`;
|
|
161
|
-
break;
|
|
162
|
-
case true:
|
|
163
|
-
_picocolorsWithForcedColor ??= import_picocolors.default.createColors(true);
|
|
164
|
-
bold = (s) => _picocolorsWithForcedColor.bold(s);
|
|
165
|
-
warn = (s) => _picocolorsWithForcedColor.yellow(s);
|
|
166
|
-
break;
|
|
167
|
-
case false:
|
|
168
|
-
bold = (s) => s;
|
|
169
|
-
warn = (s) => s;
|
|
170
|
-
break;
|
|
171
|
-
default:
|
|
172
|
-
bold = (s) => import_picocolors.default.bold(s);
|
|
173
|
-
warn = (s) => import_picocolors.default.yellow(s);
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
return {
|
|
177
|
-
bold,
|
|
178
|
-
warn
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Make sure s is an `"exports"` compatible relative path
|
|
183
|
-
* @param {string} s
|
|
184
|
-
*/
|
|
185
|
-
function exportsRel(s) {
|
|
186
|
-
if (s[0] === ".") return s;
|
|
187
|
-
if (s[0] === "/") return "." + s;
|
|
188
|
-
return "./" + s;
|
|
189
|
-
}
|
|
190
|
-
//#endregion
|
|
191
|
-
export { formatMessage, formatMessagePath, getPkgPathValue };
|
package/chunks/dep-Tb3jeYdU.js
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
//#region src/flow/render-model.ts
|
|
2
|
-
const ANSI_RESET = "\x1B[0m";
|
|
3
|
-
const ANSI_GREEN = "\x1B[32m";
|
|
4
|
-
const ANSI_RED = "\x1B[31m";
|
|
5
|
-
const ANSI_YELLOW = "\x1B[33m";
|
|
6
|
-
const DEFAULT_TERMINAL_COLUMNS = 80;
|
|
7
|
-
const TERMINAL_FRAME_MARGIN_LINES = 1;
|
|
8
|
-
const TERMINAL_FRAME_CONTEXT_LINES = 6;
|
|
9
|
-
const OMITTED_LINES_MARKER = "│ ...";
|
|
10
|
-
const ANSI_ESCAPE = String.fromCodePoint(27);
|
|
11
|
-
const ANSI_PATTERN = new RegExp(String.raw`${ANSI_ESCAPE}\[[\d:;<=>?]*[\u0020-\u002F]*[\u0040-\u007E]`, "gu");
|
|
12
|
-
const SPINNER_FRAMES = [
|
|
13
|
-
"⠋",
|
|
14
|
-
"⠙",
|
|
15
|
-
"⠹",
|
|
16
|
-
"⠸",
|
|
17
|
-
"⠼",
|
|
18
|
-
"⠴",
|
|
19
|
-
"⠦",
|
|
20
|
-
"⠧",
|
|
21
|
-
"⠇",
|
|
22
|
-
"⠏"
|
|
23
|
-
];
|
|
24
|
-
const FLOW_SYMBOL_BY_STATUS = {
|
|
25
|
-
fail: "✕",
|
|
26
|
-
info: "│",
|
|
27
|
-
pass: "◆",
|
|
28
|
-
planned: "◇",
|
|
29
|
-
skip: "◇",
|
|
30
|
-
start: "◇",
|
|
31
|
-
warn: "▲"
|
|
32
|
-
};
|
|
33
|
-
function colorInteractiveSymbol(status, symbol) {
|
|
34
|
-
if (status === "pass") return `${ANSI_GREEN}${symbol}${ANSI_RESET}`;
|
|
35
|
-
if (status === "fail") return `${ANSI_RED}${symbol}${ANSI_RESET}`;
|
|
36
|
-
if (status === "warn") return `${ANSI_YELLOW}${symbol}${ANSI_RESET}`;
|
|
37
|
-
return symbol;
|
|
38
|
-
}
|
|
39
|
-
function formatElapsedTime(milliseconds) {
|
|
40
|
-
if (milliseconds < 1e3) return `${Math.round(milliseconds)}ms`;
|
|
41
|
-
return `${(milliseconds / 1e3).toFixed(2)}s`;
|
|
42
|
-
}
|
|
43
|
-
function formatMessageWithElapsed(message, elapsedTimeMs) {
|
|
44
|
-
return typeof elapsedTimeMs === "number" ? `${message} (${formatElapsedTime(elapsedTimeMs)})` : message;
|
|
45
|
-
}
|
|
46
|
-
function indentMessage(message, depth) {
|
|
47
|
-
if (depth <= 0) return message;
|
|
48
|
-
return `${" ".repeat(depth)}${message}`;
|
|
49
|
-
}
|
|
50
|
-
function formatInteractiveLine(status, message, depth, spinnerFrameIndex) {
|
|
51
|
-
const renderedMessage = indentMessage(message, depth);
|
|
52
|
-
return `${colorInteractiveSymbol(status, status === "start" ? SPINNER_FRAMES[spinnerFrameIndex % SPINNER_FRAMES.length] : FLOW_SYMBOL_BY_STATUS[status])} ${renderedMessage}`;
|
|
53
|
-
}
|
|
54
|
-
function toTreeFlowStatus(status) {
|
|
55
|
-
switch (status) {
|
|
56
|
-
case "failed": return "fail";
|
|
57
|
-
case "passed": return "pass";
|
|
58
|
-
case "planned": return "planned";
|
|
59
|
-
case "running": return "start";
|
|
60
|
-
case "skipped": return "skip";
|
|
61
|
-
}
|
|
62
|
-
throw new Error(`Unsupported flow tree node status: ${status}`);
|
|
63
|
-
}
|
|
64
|
-
function isTreeNodeTerminal(node) {
|
|
65
|
-
return node.status === "failed" || node.status === "passed" || node.status === "skipped";
|
|
66
|
-
}
|
|
67
|
-
function areTreeNodeDescendantsTerminal(node) {
|
|
68
|
-
return node.children.every((child) => isTreeNodeTerminal(child) && areTreeNodeDescendantsTerminal(child));
|
|
69
|
-
}
|
|
70
|
-
function renderTreeNodeLine(node, spinnerFrameIndex) {
|
|
71
|
-
const elapsedTimeMs = isTreeNodeTerminal(node) && areTreeNodeDescendantsTerminal(node) ? node.elapsedTimeMs : void 0;
|
|
72
|
-
return formatInteractiveLine(toTreeFlowStatus(node.status), formatMessageWithElapsed(node.message, elapsedTimeMs), node.depth, spinnerFrameIndex);
|
|
73
|
-
}
|
|
74
|
-
function renderTreeNodeLines(node, spinnerFrameIndex) {
|
|
75
|
-
return [renderTreeNodeLine(node, spinnerFrameIndex), ...node.children.flatMap((child) => renderTreeNodeLines(child, spinnerFrameIndex))];
|
|
76
|
-
}
|
|
77
|
-
function renderCompactTreeNodeLines(node, spinnerFrameIndex) {
|
|
78
|
-
return [renderTreeNodeLine(node, spinnerFrameIndex), ...node.children.map((child) => renderTreeNodeLine(child, spinnerFrameIndex))];
|
|
79
|
-
}
|
|
80
|
-
function renderSnapshotLines(snapshot, spinnerFrameIndex) {
|
|
81
|
-
const lines = snapshot.entries.flatMap((entry) => {
|
|
82
|
-
if (entry.kind === "line") return [entry.line];
|
|
83
|
-
if (entry.kind === "flow-line") return [formatInteractiveLine(entry.status, formatMessageWithElapsed(entry.message, entry.elapsedTimeMs), entry.depth, spinnerFrameIndex)];
|
|
84
|
-
return snapshot.treeRoots.flatMap((root) => renderTreeNodeLines(root, spinnerFrameIndex));
|
|
85
|
-
});
|
|
86
|
-
return snapshot.outroMessage ? [...lines, `└ ${snapshot.outroMessage}`] : lines;
|
|
87
|
-
}
|
|
88
|
-
function renderCompactSnapshotLines(snapshot, spinnerFrameIndex) {
|
|
89
|
-
const flowLineDepths = snapshot.entries.filter((entry) => entry.kind === "flow-line").map((entry) => entry.depth);
|
|
90
|
-
const maxCompactFlowLineDepth = flowLineDepths.length > 0 ? Math.min(...flowLineDepths) + 1 : Number.POSITIVE_INFINITY;
|
|
91
|
-
const lines = snapshot.entries.flatMap((entry) => {
|
|
92
|
-
if (entry.kind === "line") return [entry.line];
|
|
93
|
-
if (entry.kind === "flow-line") {
|
|
94
|
-
if (entry.depth > maxCompactFlowLineDepth) return [];
|
|
95
|
-
return [formatInteractiveLine(entry.status, formatMessageWithElapsed(entry.message, entry.elapsedTimeMs), entry.depth, spinnerFrameIndex)];
|
|
96
|
-
}
|
|
97
|
-
return snapshot.treeRoots.flatMap((root) => renderCompactTreeNodeLines(root, spinnerFrameIndex));
|
|
98
|
-
});
|
|
99
|
-
return snapshot.outroMessage ? [...lines, `└ ${snapshot.outroMessage}`] : lines;
|
|
100
|
-
}
|
|
101
|
-
function stripControlSequences(text) {
|
|
102
|
-
return text.replaceAll(ANSI_PATTERN, "").replaceAll("\r", "");
|
|
103
|
-
}
|
|
104
|
-
function countRenderedTerminalRows(line, columns) {
|
|
105
|
-
const text = stripControlSequences(line);
|
|
106
|
-
let column = 0;
|
|
107
|
-
let rows = 1;
|
|
108
|
-
for (const char of text) {
|
|
109
|
-
if (char === "\n") {
|
|
110
|
-
rows += 1;
|
|
111
|
-
column = 0;
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
column += 1;
|
|
115
|
-
if (column >= columns) {
|
|
116
|
-
rows += 1;
|
|
117
|
-
column = 0;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return rows;
|
|
121
|
-
}
|
|
122
|
-
function countRenderedRows(lines, dimensions) {
|
|
123
|
-
const columns = Math.max(1, dimensions.columns ?? DEFAULT_TERMINAL_COLUMNS);
|
|
124
|
-
return lines.reduce((sum, line) => sum + countRenderedTerminalRows(line, columns), 0);
|
|
125
|
-
}
|
|
126
|
-
function fitsRenderedLines(lines, dimensions, options = {}) {
|
|
127
|
-
if (dimensions.rows === void 0) return true;
|
|
128
|
-
const contextLines = options.reserveContext && dimensions.rows > TERMINAL_FRAME_CONTEXT_LINES * 2 ? TERMINAL_FRAME_CONTEXT_LINES : 0;
|
|
129
|
-
const lineLimit = Math.max(1, dimensions.rows - TERMINAL_FRAME_MARGIN_LINES - contextLines);
|
|
130
|
-
return countRenderedRows(lines, dimensions) <= lineLimit;
|
|
131
|
-
}
|
|
132
|
-
function fitRenderedLinesToTerminal(lines, dimensions, options = {}) {
|
|
133
|
-
if (fitsRenderedLines(lines, dimensions) && !options.omittedLines) return lines;
|
|
134
|
-
if (dimensions.rows === void 0) return options.omittedLines ? addOmittedLinesMarker(lines) : lines;
|
|
135
|
-
const lineLimit = Math.max(1, dimensions.rows - TERMINAL_FRAME_MARGIN_LINES);
|
|
136
|
-
const columns = Math.max(1, dimensions.columns ?? DEFAULT_TERMINAL_COLUMNS);
|
|
137
|
-
const lastLine = lines.at(-1);
|
|
138
|
-
const shouldPreserveOutro = lastLine?.startsWith("└ ") ?? false;
|
|
139
|
-
const bodyLineCount = lines.length - (shouldPreserveOutro ? 1 : 0);
|
|
140
|
-
const bodyLines = lines.slice(0, bodyLineCount);
|
|
141
|
-
const ellipsisRows = countRenderedTerminalRows(OMITTED_LINES_MARKER, columns);
|
|
142
|
-
const reservedRows = shouldPreserveOutro && lastLine ? countRenderedTerminalRows(lastLine, columns) : 0;
|
|
143
|
-
const availableBodyRows = Math.max(0, lineLimit - reservedRows);
|
|
144
|
-
const bodyRows = countRenderedRows(bodyLines, { columns });
|
|
145
|
-
const shouldShowOmissionMarker = (options.omittedLines === true || bodyRows > availableBodyRows) && availableBodyRows >= ellipsisRows;
|
|
146
|
-
const fittedLines = [];
|
|
147
|
-
let remainingRows = availableBodyRows;
|
|
148
|
-
if (shouldShowOmissionMarker) remainingRows -= ellipsisRows;
|
|
149
|
-
for (let index = 0; index < bodyLineCount && remainingRows > 0; index++) {
|
|
150
|
-
const line = lines[index];
|
|
151
|
-
const rowCount = countRenderedTerminalRows(line, columns);
|
|
152
|
-
if (rowCount > remainingRows) break;
|
|
153
|
-
fittedLines.push(line);
|
|
154
|
-
remainingRows -= rowCount;
|
|
155
|
-
}
|
|
156
|
-
if (shouldShowOmissionMarker) fittedLines.push(OMITTED_LINES_MARKER);
|
|
157
|
-
if (shouldPreserveOutro && lastLine && reservedRows <= lineLimit) fittedLines.push(lastLine);
|
|
158
|
-
if (fittedLines.length > 0) return fittedLines;
|
|
159
|
-
return lines.slice(0, 1);
|
|
160
|
-
}
|
|
161
|
-
function addOmittedLinesMarker(lines) {
|
|
162
|
-
if (lines.includes(OMITTED_LINES_MARKER)) return lines;
|
|
163
|
-
const lastLine = lines.at(-1);
|
|
164
|
-
if (lastLine?.startsWith("└ ")) return [
|
|
165
|
-
...lines.slice(0, -1),
|
|
166
|
-
OMITTED_LINES_MARKER,
|
|
167
|
-
lastLine
|
|
168
|
-
];
|
|
169
|
-
return [...lines, OMITTED_LINES_MARKER];
|
|
170
|
-
}
|
|
171
|
-
function renderSnapshotLinesForTerminal(snapshot, spinnerFrameIndex, dimensions) {
|
|
172
|
-
const shouldPreferCompact = snapshot.compactMode === "check-flow" && snapshot.outroMessage !== void 0;
|
|
173
|
-
const fullLines = renderSnapshotLines(snapshot, spinnerFrameIndex);
|
|
174
|
-
if (!shouldPreferCompact && fitsRenderedLines(fullLines, dimensions, { reserveContext: true })) return fullLines;
|
|
175
|
-
const compactLines = renderCompactSnapshotLines(snapshot, spinnerFrameIndex);
|
|
176
|
-
return fitRenderedLinesToTerminal(compactLines, dimensions, { omittedLines: compactLines.length < fullLines.length });
|
|
177
|
-
}
|
|
178
|
-
function hasRunningSnapshotWork(snapshot) {
|
|
179
|
-
const hasRunningTreeNode = (node) => node.status === "running" || node.children.some(hasRunningTreeNode);
|
|
180
|
-
return snapshot.entries.some((entry) => entry.kind === "flow-line" && entry.status === "start") || snapshot.treeRoots.some(hasRunningTreeNode);
|
|
181
|
-
}
|
|
182
|
-
function toWritableText(chunk) {
|
|
183
|
-
if (chunk instanceof Uint8Array) return Buffer.from(chunk).toString();
|
|
184
|
-
return chunk;
|
|
185
|
-
}
|
|
186
|
-
//#endregion
|
|
187
|
-
//#region src/flow/terminal-frame.ts
|
|
188
|
-
var TerminalFrameTracker = class {
|
|
189
|
-
#column = 0;
|
|
190
|
-
#lineCount = 0;
|
|
191
|
-
#getColumns;
|
|
192
|
-
constructor(getColumns) {
|
|
193
|
-
this.#getColumns = getColumns;
|
|
194
|
-
}
|
|
195
|
-
get lineCount() {
|
|
196
|
-
return this.#lineCount;
|
|
197
|
-
}
|
|
198
|
-
record(chunk) {
|
|
199
|
-
const text = stripControlSequences(toWritableText(chunk));
|
|
200
|
-
const columns = Math.max(1, this.#getColumns());
|
|
201
|
-
for (const char of text) {
|
|
202
|
-
if (char === "\n") {
|
|
203
|
-
this.#lineCount += 1;
|
|
204
|
-
this.#column = 0;
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
this.#column += 1;
|
|
208
|
-
if (this.#column >= columns) {
|
|
209
|
-
this.#lineCount += 1;
|
|
210
|
-
this.#column = 0;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
reset() {
|
|
215
|
-
this.#lineCount = 0;
|
|
216
|
-
this.#column = 0;
|
|
217
|
-
}
|
|
218
|
-
setLineCount(lineCount) {
|
|
219
|
-
this.#lineCount = Math.max(0, lineCount);
|
|
220
|
-
this.#column = 0;
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
function patchWriteStream(stream, onWrite) {
|
|
224
|
-
if (typeof stream?.write !== "function") return;
|
|
225
|
-
const originalWrite = stream.write;
|
|
226
|
-
const patchedWrite = (...args) => {
|
|
227
|
-
onWrite(args[0]);
|
|
228
|
-
return writeWithFlowArgs(originalWrite, args);
|
|
229
|
-
};
|
|
230
|
-
stream.write = patchedWrite;
|
|
231
|
-
return () => {
|
|
232
|
-
stream.write = originalWrite;
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
function writeWithFlowArgs(write, args) {
|
|
236
|
-
if (typeof args[1] === "string") return write(args[0], args[1], args[2]);
|
|
237
|
-
return write(args[0], args[1]);
|
|
238
|
-
}
|
|
239
|
-
//#endregion
|
|
240
|
-
export { formatInteractiveLine as a, renderSnapshotLinesForTerminal as c, SPINNER_FRAMES as i, toTreeFlowStatus as l, patchWriteStream as n, formatMessageWithElapsed as o, writeWithFlowArgs as r, hasRunningSnapshotWork as s, TerminalFrameTracker as t, toWritableText as u };
|