Fix bin/publish: copy docs.dist from project root

Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

111
node_modules/@prettier/plugin-php/src/clean.mjs generated vendored Executable file
View File

@@ -0,0 +1,111 @@
import { printNumber, normalizeMagicMethodName } from "./util.mjs";
const ignoredProperties = new Set([
"loc",
"range",
"raw",
"comments",
"leadingComments",
"trailingComments",
"parenthesizedExpression",
"parent",
"prev",
"start",
"end",
"tokens",
"errors",
"extra",
]);
/**
* This function takes the existing ast node and a copy, by reference
* We use it for testing, so that we can compare pre-post versions of the AST,
* excluding things we don't care about (like node location, case that will be
* changed by the printer, etc.)
*/
function clean(node, newObj) {
if (node.kind === "string") {
// TODO if options are available in this method, replace with
// newObj.isDoubleQuote = !useSingleQuote(node, options);
delete newObj.isDoubleQuote;
}
if (["array", "list"].includes(node.kind)) {
// TODO if options are available in this method, assign instead of delete
delete newObj.shortForm;
}
if (node.kind === "inline") {
if (node.value.includes("___PSEUDO_INLINE_PLACEHOLDER___")) {
return null;
}
newObj.value = newObj.value.replace(/\n/g, "");
}
// continue ((2)); -> continue 2;
// continue 1; -> continue;
if ((node.kind === "continue" || node.kind === "break") && node.level) {
const { level } = newObj;
if (level.kind === "number") {
newObj.level = level.value === "1" ? null : level;
}
}
// if () {{ }} -> if () {}
if (node.kind === "block") {
if (node.children.length === 1 && node.children[0].kind === "block") {
while (newObj.children[0].kind === "block") {
newObj.children = newObj.children[0].children;
}
}
}
// Normalize numbers
if (node.kind === "number") {
newObj.value = printNumber(node.value);
}
const statements = ["foreach", "for", "if", "while", "do"];
if (statements.includes(node.kind)) {
if (node.body && node.body.kind !== "block") {
newObj.body = {
kind: "block",
children: [newObj.body],
};
} else {
newObj.body = newObj.body ? newObj.body : null;
}
if (node.alternate && node.alternate.kind !== "block") {
newObj.alternate = {
kind: "block",
children: [newObj.alternate],
};
} else {
newObj.alternate = newObj.alternate ? newObj.alternate : null;
}
}
if (node.kind === "usegroup" && typeof node.name === "string") {
newObj.name = newObj.name.replace(/^\\/, "");
}
if (node.kind === "useitem") {
newObj.name = newObj.name.replace(/^\\/, "");
}
if (node.kind === "method" && node.name.kind === "identifier") {
newObj.name.name = normalizeMagicMethodName(newObj.name.name);
}
if (node.kind === "noop") {
return null;
}
}
clean.ignoredProperties = ignoredProperties;
export default clean;

1044
node_modules/@prettier/plugin-php/src/comments.mjs generated vendored Executable file

File diff suppressed because it is too large Load Diff

173
node_modules/@prettier/plugin-php/src/index.mjs generated vendored Executable file
View File

@@ -0,0 +1,173 @@
import { doc } from "prettier";
import LINGUIST_LANGUAGES_PHP from "linguist-languages/data/PHP";
import LINGUIST_LANGUAGES_HTML_PHP from "linguist-languages/data/HTML_2b_PHP";
import parse from "./parser.mjs";
import print from "./printer.mjs";
import clean from "./clean.mjs";
import options from "./options.mjs";
import {
handleOwnLineComment,
handleEndOfLineComment,
handleRemainingComment,
getCommentChildNodes,
canAttachComment,
isBlockComment,
} from "./comments.mjs";
import { hasPragma, insertPragma } from "./pragma.mjs";
import { locStart, locEnd } from "./loc.mjs";
const { join, hardline } = doc.builders;
function createLanguage(linguistData, { extend, override }) {
const language = {};
for (const key in linguistData) {
const newKey = key === "languageId" ? "linguistLanguageId" : key;
language[newKey] = linguistData[key];
}
if (extend) {
for (const key in extend) {
language[key] = (language[key] || []).concat(extend[key]);
}
}
for (const key in override) {
language[key] = override[key];
}
return language;
}
const languages = [
createLanguage(LINGUIST_LANGUAGES_PHP, {
override: {
parsers: ["php"],
vscodeLanguageIds: ["php"],
},
}),
createLanguage(LINGUIST_LANGUAGES_HTML_PHP, {
override: {
parsers: ["php"],
vscodeLanguageIds: ["php"],
},
}),
];
const parsers = {
php: {
parse,
astFormat: "php",
locStart,
locEnd,
hasPragma,
},
};
const ignoredKeys = new Set([
"kind",
"loc",
"errors",
"extra",
"comments",
"leadingComments",
"enclosingNode",
"precedingNode",
"followingNode",
]);
function getVisitorKeys(node, nonTraversableKeys) {
return Object.keys(node).filter(
(key) => !nonTraversableKeys.has(key) && !ignoredKeys.has(key)
);
}
const printers = {
php: {
print,
getVisitorKeys,
insertPragma,
massageAstNode: clean,
getCommentChildNodes,
canAttachComment,
isBlockComment,
handleComments: {
ownLine: handleOwnLineComment,
endOfLine: handleEndOfLineComment,
remaining: handleRemainingComment,
},
willPrintOwnComments(path) {
const { node } = path;
return node && node.kind === "noop";
},
printComment(path) {
const comment = path.node;
switch (comment.kind) {
case "commentblock": {
// for now, don't touch single line block comments
if (!comment.value.includes("\n")) {
return comment.value;
}
const lines = comment.value.split("\n");
// if this is a block comment, handle indentation
if (
lines
.slice(1, lines.length - 1)
.every((line) => line.trim()[0] === "*")
) {
return join(
hardline,
lines.map(
(line, index) =>
(index > 0 ? " " : "") +
(index < lines.length - 1 ? line.trim() : line.trimLeft())
)
);
}
// otherwise we can't be sure about indentation, so just print as is
return comment.value;
}
case "commentline": {
return comment.value.trimRight();
}
/* c8 ignore next 2 */
default:
throw new Error(`Not a comment: ${JSON.stringify(comment)}`);
}
},
hasPrettierIgnore(path) {
const isSimpleIgnore = (comment) =>
comment.value.includes("prettier-ignore") &&
!comment.value.includes("prettier-ignore-start") &&
!comment.value.includes("prettier-ignore-end");
const { node, parent: parentNode } = path;
return (
(node &&
node.kind !== "classconstant" &&
node.comments &&
node.comments.length > 0 &&
node.comments.some(isSimpleIgnore)) ||
// For proper formatting, the classconstant ignore formatting should
// run on the "constant" child
(node &&
node.kind === "constant" &&
parentNode &&
parentNode.kind === "classconstant" &&
parentNode.comments &&
parentNode.comments.length > 0 &&
parentNode.comments.some(isSimpleIgnore))
);
},
},
};
const defaultOptions = {
tabWidth: 4,
};
export { languages, printers, parsers, options, defaultOptions };

4
node_modules/@prettier/plugin-php/src/loc.mjs generated vendored Executable file
View File

@@ -0,0 +1,4 @@
const loc = (prop) => (node) => node.loc?.[prop]?.offset;
export const locStart = loc("start");
export const locEnd = loc("end");

250
node_modules/@prettier/plugin-php/src/needs-parens.mjs generated vendored Executable file
View File

@@ -0,0 +1,250 @@
import { getPrecedence, shouldFlatten, isBitwiseOperator } from "./util.mjs";
function needsParens(path, options) {
const { parent } = path;
if (!parent) {
return false;
}
const { key, node } = path;
if (
[
// No need parens for top level children of this nodes
"program",
"expressionstatement",
"namespace",
"declare",
"block",
// No need parens
"include",
"print",
"return",
"echo",
].includes(parent.kind)
) {
return false;
}
switch (node.kind) {
case "pre":
case "post":
if (parent.kind === "unary") {
return (
node.kind === "pre" &&
((node.type === "+" && parent.type === "+") ||
(node.type === "-" && parent.type === "-"))
);
}
// else fallthrough
case "unary":
switch (parent.kind) {
case "unary":
return (
node.type === parent.type &&
(node.type === "+" || node.type === "-")
);
case "propertylookup":
case "nullsafepropertylookup":
case "staticlookup":
case "offsetlookup":
case "call":
return key === "what";
case "bin":
return parent.type === "**" && key === "left";
default:
return false;
}
case "bin": {
switch (parent.kind) {
case "assign":
case "retif":
return ["and", "xor", "or"].includes(node.type);
case "silent":
case "cast":
// TODO: bug https://github.com/glayzzle/php-parser/issues/172
return node.parenthesizedExpression;
case "pre":
case "post":
case "unary":
return true;
case "call":
case "propertylookup":
case "nullsafepropertylookup":
case "staticlookup":
case "offsetlookup":
return key === "what";
case "bin": {
const po = parent.type;
const pp = getPrecedence(po);
const no = node.type;
const np = getPrecedence(no);
if (pp > np) {
return true;
}
if (po === "||" && no === "&&") {
return true;
}
if (pp === np && key === "right") {
return true;
}
if (pp === np && !shouldFlatten(po, no)) {
return true;
}
if (pp < np && no === "%") {
return po === "+" || po === "-";
}
// Add parenthesis when working with bitwise operators
// It's not stricly needed but helps with code understanding
if (isBitwiseOperator(po)) {
return true;
}
return false;
}
default:
return false;
}
}
case "propertylookup":
case "nullsafepropertylookup":
case "staticlookup": {
switch (parent.kind) {
case "call":
return key === "what" && node.parenthesizedExpression;
default:
return false;
}
}
case "clone":
case "new": {
const requiresParens =
node.kind === "clone" ||
(node.kind === "new" && options.phpVersion < 8.4);
switch (parent.kind) {
case "propertylookup":
case "nullsafepropertylookup":
case "staticlookup":
case "offsetlookup":
case "call":
return key === "what" && requiresParens;
default:
return false;
}
}
case "yield": {
switch (parent.kind) {
case "propertylookup":
case "nullsafepropertylookup":
case "staticlookup":
case "offsetlookup":
case "call":
return key === "what";
case "retif":
return key === "test";
default:
return !!(node.key || node.value);
}
}
case "assign": {
if (
parent.kind === "for" &&
(parent.init.includes(node) || parent.increment.includes(node))
) {
return false;
} else if (parent.kind === "assign") {
return false;
} else if (parent.kind === "static") {
return false;
} else if (
["if", "do", "while", "foreach", "switch"].includes(parent.kind)
) {
return false;
} else if (parent.kind === "silent") {
return false;
} else if (parent.kind === "call") {
return false;
}
return true;
}
case "retif":
switch (parent.kind) {
case "cast":
return true;
case "unary":
case "bin":
case "retif":
if (key === "test" && !parent.trueExpr) {
return false;
}
return true;
case "propertylookup":
case "nullsafepropertylookup":
case "staticlookup":
case "offsetlookup":
case "call":
return key === "what";
default:
return false;
}
case "closure":
switch (parent.kind) {
case "call":
return key === "what";
// https://github.com/prettier/plugin-php/issues/1675
case "propertylookup":
case "nullsafepropertylookup":
return true;
default:
return false;
}
case "silence":
case "cast":
// TODO: bug https://github.com/glayzzle/php-parser/issues/172
return node.parenthesizedExpression;
// else fallthrough
case "string":
case "array":
switch (parent.kind) {
case "propertylookup":
case "nullsafepropertylookup":
case "staticlookup":
case "offsetlookup":
case "call":
if (
["string", "array"].includes(node.kind) &&
parent.kind === "offsetlookup"
) {
return false;
}
return key === "what";
default:
return false;
}
case "print":
case "include":
return parent.kind === "bin";
}
return false;
}
export default needsParens;

151
node_modules/@prettier/plugin-php/src/options.mjs generated vendored Executable file
View File

@@ -0,0 +1,151 @@
import fs from "fs";
import path from "path";
const CATEGORY_PHP = "PHP";
// prettier-ignore
const SUPPORTED_PHP_VERSIONS = [
5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6,
7.0, 7.1, 7.2, 7.3, 7.4,
8.0, 8.1, 8.2, 8.3, 8.4,
];
export const LATEST_SUPPORTED_PHP_VERSION = Math.max(...SUPPORTED_PHP_VERSIONS);
let getComposerError = "";
/**
* Detect the minimum PHP version from the composer.json file
* @return {number|null} The PHP version to use in the composer.json file, null when not found
*/
function getComposerPhpVersion() {
// Try to find composer.json
const currentDir = process.cwd();
let composerPath = null;
const directComposerPath = path.join(currentDir, "composer.json");
if (fs.existsSync(directComposerPath)) {
composerPath = directComposerPath;
}
if (!composerPath) {
let searchDir = path.dirname(currentDir);
while (searchDir !== path.parse(searchDir).root) {
const potentialComposerPath = path.join(searchDir, "composer.json");
if (fs.existsSync(potentialComposerPath)) {
composerPath = potentialComposerPath;
break;
}
searchDir = path.dirname(searchDir);
}
}
if (composerPath) {
try {
const fileContent = fs.readFileSync(composerPath, "utf8");
const composerJson = JSON.parse(fileContent);
if (composerJson.require && composerJson.require.php) {
// Check for a wildcard pattern like "7.*"
const wildcardMatch = composerJson.require.php.match(
/^(?:[^0-9]*)?([0-9]+)\.\*/
);
if (wildcardMatch) {
return parseFloat(`${wildcardMatch[1]}.0`);
}
// Extract version from composer semver format
const versionMatch = composerJson.require.php.match(
/^(?:[^0-9]*)?([0-9]+)\.([0-9]+)/
);
if (versionMatch) {
return parseFloat(`${versionMatch[1]}.${versionMatch[2]}`);
} else {
getComposerError = `Could not decode PHP version (${composerJson.require.php}})`;
return null;
}
}
} catch (e) {
getComposerError = `Error reading composer.json: ${e.message}`;
}
} else {
getComposerError = "Could not find composer.json";
}
return null;
}
export { getComposerPhpVersion };
/**
* Resolve the PHP version to a number based on the provided options.
*
*/
export function resolvePhpVersion(options) {
if (!options) {
return;
}
if (options.phpVersion === "auto") {
options.phpVersion =
getComposerPhpVersion() ?? LATEST_SUPPORTED_PHP_VERSION;
} else if (options.phpVersion === "composer") {
const v = getComposerPhpVersion();
if (v === null) {
throw new Error(
`Could not determine PHP version from composer; ${getComposerError}`
);
}
options.phpVersion = v;
} else {
options.phpVersion = parseFloat(options.phpVersion);
}
}
export default {
phpVersion: {
since: "0.13.0",
category: CATEGORY_PHP,
type: "choice",
default: "auto",
description: "Minimum target PHP version.",
choices: [
...SUPPORTED_PHP_VERSIONS.map((v) => ({ value: v.toFixed(1) })),
{
value: "composer",
description: "Use the PHP version defined in composer.json",
},
{
value: "auto",
description: `Try composer.json, else latest PHP Version (${LATEST_SUPPORTED_PHP_VERSION})`,
},
],
},
trailingCommaPHP: {
since: "0.0.0",
category: CATEGORY_PHP,
type: "boolean",
default: true,
description: "Print trailing commas wherever possible when multi-line.",
},
braceStyle: {
since: "0.10.0",
category: CATEGORY_PHP,
type: "choice",
default: "per-cs",
description:
"Print one space or newline for code blocks (classes and functions).",
choices: [
{ value: "psr-2", description: "(deprecated) Use per-cs" },
{ value: "per-cs", description: "Use the PER Coding Style brace style." },
{ value: "1tbs", description: "Use 1tbs brace style." },
],
},
singleQuote: {
since: "0.0.0",
category: CATEGORY_PHP,
type: "boolean",
default: false,
description: "Use single quotes instead of double quotes.",
},
};

67
node_modules/@prettier/plugin-php/src/parser.mjs generated vendored Executable file
View File

@@ -0,0 +1,67 @@
import engine from "php-parser";
import { LATEST_SUPPORTED_PHP_VERSION } from "./options.mjs";
import { resolvePhpVersion } from "./options.mjs";
function parse(text, opts) {
const inMarkdown = opts && opts.parentParser === "markdown";
if (!text && inMarkdown) {
return "";
}
resolvePhpVersion(opts);
// Todo https://github.com/glayzzle/php-parser/issues/170
text = text.replace(/\?>\n<\?/g, "?>\n___PSEUDO_INLINE_PLACEHOLDER___<?");
// initialize a new parser instance
const parser = new engine({
parser: {
extractDoc: true,
version: `${LATEST_SUPPORTED_PHP_VERSION}`,
},
ast: {
withPositions: true,
withSource: true,
},
});
const hasOpenPHPTag = text.indexOf("<?php") !== -1;
const parseAsEval = inMarkdown && !hasOpenPHPTag;
let ast;
try {
ast = parseAsEval ? parser.parseEval(text) : parser.parseCode(text);
} catch (err) {
if (err instanceof SyntaxError && "lineNumber" in err) {
err.loc = {
start: {
line: err.lineNumber,
column: err.columnNumber,
},
};
delete err.lineNumber;
delete err.columnNumber;
}
throw err;
}
ast.extra = {
parseAsEval,
};
// https://github.com/glayzzle/php-parser/issues/155
// currently inline comments include the line break at the end, we need to
// strip those out and update the end location for each comment manually
ast.comments.forEach((comment) => {
if (comment.value[comment.value.length - 1] === "\n") {
comment.value = comment.value.slice(0, -1);
comment.loc.end.offset = comment.loc.end.offset - 1;
}
});
return ast;
}
export default parse;

92
node_modules/@prettier/plugin-php/src/pragma.mjs generated vendored Executable file
View File

@@ -0,0 +1,92 @@
import { memoize } from "./util.mjs";
import parse from "./parser.mjs";
const reHasPragma = /@prettier|@format/;
const getPageLevelDocBlock = memoize((text) => {
const parsed = parse(text);
const [firstChild] = parsed.children;
const [firstDocBlock] = parsed.comments.filter(
(el) => el.kind === "commentblock"
);
if (
firstChild &&
firstDocBlock &&
firstDocBlock.loc.start.line < firstChild.loc.start.line
) {
return firstDocBlock;
}
});
function hasPragma(text) {
// fast path optimization - check if the pragma shows up in the file at all
if (!reHasPragma.test(text)) {
return false;
}
const pageLevelDocBlock = getPageLevelDocBlock(text);
if (pageLevelDocBlock) {
const { value } = pageLevelDocBlock;
return reHasPragma.test(value);
}
return false;
}
function injectPragma(docblock) {
let lines = docblock.split("\n");
if (lines.length === 1) {
// normalize to multiline for simplicity
const [, line] = /\/*\*\*(.*)\*\//.exec(lines[0]);
lines = ["/**", ` * ${line.trim()}`, " */"];
}
// find the first @pragma
// if there happens to be one on the opening line, just put it on the next line.
const pragmaIndex = lines.findIndex((line) => /@\S/.test(line)) || 1;
// not found => index == -1, which conveniently will splice 1 from the end.
lines.splice(pragmaIndex, 0, " * @format");
return lines.join("\n");
}
function insertPragma(text) {
const pageLevelDocBlock = getPageLevelDocBlock(text);
if (pageLevelDocBlock) {
const {
start: { offset: startOffset },
end: { offset: endOffset },
} = pageLevelDocBlock.loc;
const before = text.substring(0, startOffset);
const after = text.substring(endOffset);
return `${before}${injectPragma(pageLevelDocBlock.value, text)}${after}`;
}
const openTag = "<?php";
if (!text.startsWith(openTag)) {
// bail out
return text;
}
const splitAt = openTag.length;
const phpTag = text.substring(0, splitAt);
const after = text.substring(splitAt);
return `${phpTag}
/**
* @format
*/
${after}`;
}
export { hasPragma, insertPragma };

2899
node_modules/@prettier/plugin-php/src/printer.mjs generated vendored Executable file

File diff suppressed because it is too large Load Diff

743
node_modules/@prettier/plugin-php/src/util.mjs generated vendored Executable file
View File

@@ -0,0 +1,743 @@
import { util as prettierUtil } from "prettier";
import { locStart } from "./loc.mjs";
const { hasNewline, skipEverythingButNewLine, skipNewline } = prettierUtil;
function printNumber(rawNumber) {
return (
rawNumber
.toLowerCase()
// Remove unnecessary plus and zeroes from scientific notation.
.replace(/^([+-]?[\d.]+e)(?:\+|(-))?0*(\d)/, "$1$2$3")
// Remove unnecessary scientific notation (1e0).
.replace(/^([+-]?[\d.]+)e[+-]?0+$/, "$1")
// Make sure numbers always start with a digit.
.replace(/^([+-])?\./, "$10.")
// Remove extraneous trailing decimal zeroes.
.replace(/(\.\d+?)0+(?=e|$)/, "$1")
// Remove unnecessary .e notation
.replace(/\.(?=e)/, "")
);
}
// http://php.net/manual/en/language.operators.precedence.php
const PRECEDENCE = new Map(
[
["or"],
["xor"],
["and"],
[
"=",
"+=",
"-=",
"*=",
"**=",
"/=",
".=",
"%=",
"&=",
"|=",
"^=",
"<<=",
">>=",
],
["??"],
["||"],
["&&"],
["|"],
["^"],
["&"],
["==", "===", "!=", "!==", "<>", "<=>"],
["<", ">", "<=", ">="],
[">>", "<<"],
["+", "-", "."],
["*", "/", "%"],
["!"],
["instanceof"],
["++", "--", "~"],
["**"],
].flatMap((operators, index) =>
operators.map((operator) => [operator, index])
)
);
function getPrecedence(operator) {
return PRECEDENCE.get(operator);
}
const equalityOperators = ["==", "!=", "===", "!==", "<>", "<=>"];
const multiplicativeOperators = ["*", "/", "%"];
const bitshiftOperators = [">>", "<<"];
function isBitwiseOperator(operator) {
return (
!!bitshiftOperators[operator] ||
operator === "|" ||
operator === "^" ||
operator === "&"
);
}
function shouldFlatten(parentOp, nodeOp) {
if (getPrecedence(nodeOp) !== getPrecedence(parentOp)) {
return false;
}
// ** is right-associative
// x ** y ** z --> x ** (y ** z)
if (parentOp === "**") {
return false;
}
// x == y == z --> (x == y) == z
if (
equalityOperators.includes(parentOp) &&
equalityOperators.includes(nodeOp)
) {
return false;
}
// x * y % z --> (x * y) % z
if (
(nodeOp === "%" && multiplicativeOperators.includes(parentOp)) ||
(parentOp === "%" && multiplicativeOperators.includes(nodeOp))
) {
return false;
}
// x * y / z --> (x * y) / z
// x / y * z --> (x / y) * z
if (
nodeOp !== parentOp &&
multiplicativeOperators.includes(nodeOp) &&
multiplicativeOperators.includes(parentOp)
) {
return false;
}
// x << y << z --> (x << y) << z
if (
bitshiftOperators.includes(parentOp) &&
bitshiftOperators.includes(nodeOp)
) {
return false;
}
return true;
}
function nodeHasStatement(node) {
return [
"block",
"program",
"namespace",
"class",
"enum",
"interface",
"trait",
"traituse",
"declare",
].includes(node.kind);
}
function getBodyFirstChild({ body }) {
if (!body) {
return null;
}
if (body.kind === "block") {
body = body.children;
}
return body[0];
}
function getNodeListProperty(node) {
const body = node.children || node.body || node.adaptations;
return Array.isArray(body) ? body : null;
}
function getLast(arr) {
if (arr.length > 0) {
return arr[arr.length - 1];
}
return null;
}
function getPenultimate(arr) {
if (arr.length > 1) {
return arr[arr.length - 2];
}
return null;
}
function isFirstChildrenInlineNode(path) {
const { node } = path;
if (node.kind === "program") {
const children = getNodeListProperty(node);
if (!children || children.length === 0) {
return false;
}
return children[0].kind === "inline";
}
if (node.kind === "switch") {
if (!node.body) {
return false;
}
const children = getNodeListProperty(node.body);
if (children.length === 0) {
return false;
}
const [firstCase] = children;
if (!firstCase.body) {
return false;
}
const firstCaseChildren = getNodeListProperty(firstCase.body);
if (firstCaseChildren.length === 0) {
return false;
}
return firstCaseChildren[0].kind === "inline";
}
const firstChild = getBodyFirstChild(node);
if (!firstChild) {
return false;
}
return firstChild.kind === "inline";
}
function isDocNode(node) {
return (
node.kind === "nowdoc" ||
(node.kind === "encapsed" && node.type === "heredoc")
);
}
/**
* Heredoc/Nowdoc nodes need a trailing linebreak if they
* appear as function arguments or array elements
*/
function docShouldHaveTrailingNewline(path, recurse = 0) {
const node = path.getNode(recurse);
const parent = path.getNode(recurse + 1);
const parentParent = path.getNode(recurse + 2);
if (!parent) {
return false;
}
if (
(parentParent &&
["call", "new", "echo"].includes(parentParent.kind) &&
!["call", "array"].includes(parent.kind)) ||
parent.kind === "parameter"
) {
const lastIndex = parentParent.arguments.length - 1;
const index = parentParent.arguments.indexOf(parent);
return index !== lastIndex;
}
if (parentParent && parentParent.kind === "for") {
const initIndex = parentParent.init.indexOf(parent);
if (initIndex !== -1) {
return initIndex !== parentParent.init.length - 1;
}
const testIndex = parentParent.test.indexOf(parent);
if (testIndex !== -1) {
return testIndex !== parentParent.test.length - 1;
}
const incrementIndex = parentParent.increment.indexOf(parent);
if (incrementIndex !== -1) {
return incrementIndex !== parentParent.increment.length - 1;
}
}
if (parent.kind === "bin") {
return (
parent.left === node || docShouldHaveTrailingNewline(path, recurse + 1)
);
}
if (parent.kind === "case" && parent.test === node) {
return true;
}
if (parent.kind === "staticvariable") {
const lastIndex = parentParent.variables.length - 1;
const index = parentParent.variables.indexOf(parent);
return index !== lastIndex;
}
if (parent.kind === "entry") {
if (parent.key === node) {
return true;
}
const lastIndex = parentParent.items.length - 1;
const index = parentParent.items.indexOf(parent);
return index !== lastIndex;
}
if (["call", "new"].includes(parent.kind)) {
const lastIndex = parent.arguments.length - 1;
const index = parent.arguments.indexOf(node);
return index !== lastIndex;
}
if (parent.kind === "echo") {
const lastIndex = parent.expressions.length - 1;
const index = parent.expressions.indexOf(node);
return index !== lastIndex;
}
if (parent.kind === "array") {
const lastIndex = parent.items.length - 1;
const index = parent.items.indexOf(node);
return index !== lastIndex;
}
if (parent.kind === "retif") {
return docShouldHaveTrailingNewline(path, recurse + 1);
}
return false;
}
function lineShouldEndWithSemicolon(path) {
const { node, parent: parentNode } = path;
if (!parentNode) {
return false;
}
// for single line control structures written in a shortform (ie without a block),
// we need to make sure the single body node gets a semicolon
if (
["for", "foreach", "while", "do", "if", "switch"].includes(
parentNode.kind
) &&
node.kind !== "block" &&
node.kind !== "if" &&
(parentNode.body === node || parentNode.alternate === node)
) {
return true;
}
if (!nodeHasStatement(parentNode)) {
return false;
}
if (node.kind === "echo" && node.shortForm) {
return false;
}
if (node.kind === "traituse") {
return !node.adaptations;
}
if (node.kind === "method" && node.isAbstract) {
return true;
}
if (node.kind === "method") {
const { parent } = path;
if (parent && parent.kind === "interface") {
return true;
}
}
return [
"expressionstatement",
"do",
"usegroup",
"classconstant",
"propertystatement",
"traitprecedence",
"traitalias",
"goto",
"constantstatement",
"enumcase",
"global",
"static",
"echo",
"unset",
"return",
"break",
"continue",
"throw",
].includes(node.kind);
}
function fileShouldEndWithHardline(path) {
const { node } = path;
const isProgramNode = node.kind === "program";
const lastNode = node.children && getLast(node.children);
if (!isProgramNode) {
return false;
}
if (lastNode && ["halt", "inline"].includes(lastNode.kind)) {
return false;
}
if (
lastNode &&
(lastNode.kind === "declare" || lastNode.kind === "namespace")
) {
const lastNestedNode =
lastNode.children.length > 0 && getLast(lastNode.children);
if (lastNestedNode && ["halt", "inline"].includes(lastNestedNode.kind)) {
return false;
}
}
return true;
}
function maybeStripLeadingSlashFromUse(name) {
const nameWithoutLeadingSlash = name.replace(/^\\/, "");
if (nameWithoutLeadingSlash.indexOf("\\") !== -1) {
return nameWithoutLeadingSlash;
}
return name;
}
function hasDanglingComments(node) {
return (
node.comments &&
node.comments.some((comment) => !comment.leading && !comment.trailing)
);
}
function isLookupNode(node) {
return (
node.kind === "propertylookup" ||
node.kind === "nullsafepropertylookup" ||
node.kind === "staticlookup" ||
node.kind === "offsetlookup"
);
}
function shouldPrintHardLineAfterStartInControlStructure(path) {
const { node } = path;
if (["try", "catch"].includes(node.kind)) {
return false;
}
return isFirstChildrenInlineNode(path);
}
function shouldPrintHardLineBeforeEndInControlStructure(path) {
const { node } = path;
if (["try", "catch"].includes(node.kind)) {
return true;
}
if (node.kind === "switch") {
const children = getNodeListProperty(node.body);
if (children.length === 0) {
return true;
}
const lastCase = getLast(children);
if (!lastCase.body) {
return true;
}
const childrenInCase = getNodeListProperty(lastCase.body);
if (childrenInCase.length === 0) {
return true;
}
return childrenInCase[0].kind !== "inline";
}
return !isFirstChildrenInlineNode(path);
}
function getAlignment(text) {
const lines = text.split("\n");
const lastLine = lines.pop();
return lastLine.length - lastLine.trimLeft().length + 1;
}
function isProgramLikeNode(node) {
return ["program", "declare", "namespace"].includes(node.kind);
}
function isReferenceLikeNode(node) {
return [
"name",
"parentreference",
"selfreference",
"staticreference",
].includes(node.kind);
}
// Return `logical` value for `bin` node containing `||` or `&&` type otherwise return kind of node.
// Require for grouping logical and binary nodes in right way.
function getNodeKindIncludingLogical(node) {
if (node.kind === "bin" && ["||", "&&"].includes(node.type)) {
return "logical";
}
return node.kind;
}
/**
* Check if string can safely be converted from double to single quotes and vice-versa, i.e.
*
* - no embedded variables ("foo $bar")
* - no linebreaks
* - no special characters like \n, \t, ...
* - no octal/hex/unicode characters
*
* See https://php.net/manual/en/language.types.string.php#language.types.string.syntax.double
*/
function useDoubleQuote(node, options) {
if (node.isDoubleQuote === options.singleQuote) {
// We have a double quote and the user passed singleQuote:true, or the other way around.
const rawValue = node.raw.slice(node.raw[0] === "b" ? 2 : 1, -1);
const isComplex = rawValue.match(
/\\([$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u{([0-9a-fA-F]+)})|\r?\n|'|"|\$/
);
return node.isDoubleQuote ? isComplex : !isComplex;
}
return node.isDoubleQuote;
}
function hasEmptyBody(path, name = "body") {
const { node } = path;
return (
node[name] &&
node[name].children &&
node[name].children.length === 0 &&
(!node[name].comments || node[name].comments.length === 0)
);
}
function isNextLineEmptyAfterNamespace(text, node) {
let idx = locStart(node);
idx = skipEverythingButNewLine(text, idx);
idx = skipNewline(text, idx);
return hasNewline(text, idx);
}
function shouldPrintHardlineBeforeTrailingComma(lastElem) {
if (
lastElem.kind === "nowdoc" ||
(lastElem.kind === "encapsed" && lastElem.type === "heredoc")
) {
return true;
}
if (
lastElem.kind === "entry" &&
(lastElem.value.kind === "nowdoc" ||
(lastElem.value.kind === "encapsed" && lastElem.value.type === "heredoc"))
) {
return true;
}
return false;
}
function getAncestorCounter(path, typeOrTypes) {
const types = [].concat(typeOrTypes);
let counter = -1;
let ancestorNode;
while ((ancestorNode = path.getParentNode(++counter))) {
if (types.indexOf(ancestorNode.kind) !== -1) {
return counter;
}
}
return -1;
}
function getAncestorNode(path, typeOrTypes) {
const counter = getAncestorCounter(path, typeOrTypes);
return counter === -1 ? null : path.getParentNode(counter);
}
const magicMethods = [
"__construct",
"__destruct",
"__call",
"__callStatic",
"__get",
"__set",
"__isset",
"__unset",
"__sleep",
"__wakeup",
"__toString",
"__invoke",
"__set_state",
"__clone",
"__debugInfo",
];
const magicMethodsMap = new Map(
magicMethods.map((name) => [name.toLowerCase(), name])
);
function normalizeMagicMethodName(name) {
const loweredName = name.toLowerCase();
if (magicMethodsMap.has(loweredName)) {
return magicMethodsMap.get(loweredName);
}
return name;
}
/**
* @param {string[]} kindsArray
* @returns {(node: Node | Comment) => Boolean}
*/
function createTypeCheckFunction(kindsArray) {
const kinds = new Set(kindsArray);
return (node) => kinds.has(node?.kind);
}
const isSingleWordType = createTypeCheckFunction([
"variadicplaceholder",
"namedargument",
"nullkeyword",
"identifier",
"parameter",
"variable",
"variadic",
"boolean",
"literal",
"number",
"string",
"clone",
"cast",
]);
const isArrayExpression = createTypeCheckFunction(["array"]);
const isCallLikeExpression = createTypeCheckFunction([
"nullsafepropertylookup",
"propertylookup",
"staticlookup",
"offsetlookup",
"call",
"new",
]);
const isArrowFuncExpression = createTypeCheckFunction(["arrowfunc"]);
function getChainParts(node, prev = []) {
const parts = prev;
if (isCallLikeExpression(node)) {
parts.push(node);
}
if (!node.what) {
return parts;
}
return getChainParts(node.what, parts);
}
function isSimpleCallArgument(node, depth = 2) {
if (depth <= 0) {
return false;
}
const isChildSimple = (child) => isSimpleCallArgument(child, depth - 1);
if (isSingleWordType(node)) {
return true;
}
if (isArrayExpression(node)) {
return node.items.every((x) => x === null || isChildSimple(x));
}
if (isCallLikeExpression(node)) {
const parts = getChainParts(node);
parts.unshift();
return (
parts.length <= depth &&
parts.every((node) =>
isLookupNode(node)
? isChildSimple(node.offset)
: node.arguments.every(isChildSimple)
)
);
}
if (isArrowFuncExpression(node)) {
return (
node.arguments.length <= depth && node.arguments.every(isChildSimple)
);
}
return false;
}
function memoize(fn) {
const cache = new Map();
return (key) => {
if (!cache.has(key)) {
cache.set(key, fn(key));
}
return cache.get(key);
};
}
export {
printNumber,
getPrecedence,
isBitwiseOperator,
shouldFlatten,
nodeHasStatement,
getLast,
getPenultimate,
getBodyFirstChild,
lineShouldEndWithSemicolon,
fileShouldEndWithHardline,
maybeStripLeadingSlashFromUse,
hasDanglingComments,
docShouldHaveTrailingNewline,
isLookupNode,
isFirstChildrenInlineNode,
shouldPrintHardLineAfterStartInControlStructure,
shouldPrintHardLineBeforeEndInControlStructure,
getAlignment,
isProgramLikeNode,
isReferenceLikeNode,
getNodeKindIncludingLogical,
useDoubleQuote,
hasEmptyBody,
isNextLineEmptyAfterNamespace,
shouldPrintHardlineBeforeTrailingComma,
isDocNode,
getAncestorNode,
normalizeMagicMethodName,
isSimpleCallArgument,
memoize,
};