참고소스 수정본
This commit is contained in:
198
참고/playwright-main/packages/isomorphic/stringUtils.ts
Normal file
198
참고/playwright-main/packages/isomorphic/stringUtils.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// NOTE: this function should not be used to escape any selectors.
|
||||
export function escapeWithQuotes(text: string, char: string = '\'') {
|
||||
const stringified = JSON.stringify(text);
|
||||
const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, '"');
|
||||
if (char === '\'')
|
||||
return char + escapedText.replace(/[']/g, '\\\'') + char;
|
||||
if (char === '"')
|
||||
return char + escapedText.replace(/["]/g, '\\"') + char;
|
||||
if (char === '`')
|
||||
return char + escapedText.replace(/[`]/g, '\\`') + char;
|
||||
throw new Error('Invalid escape char');
|
||||
}
|
||||
|
||||
export function escapeTemplateString(text: string): string {
|
||||
return text
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/`/g, '\\`')
|
||||
.replace(/\$\{/g, '\\${');
|
||||
}
|
||||
|
||||
export function isString(obj: any): obj is string {
|
||||
return typeof obj === 'string' || obj instanceof String;
|
||||
}
|
||||
|
||||
export function toTitleCase(name: string) {
|
||||
return name.charAt(0).toUpperCase() + name.substring(1);
|
||||
}
|
||||
|
||||
export function toSnakeCase(name: string): string {
|
||||
// E.g. ignoreHTTPSErrors => ignore_https_errors.
|
||||
return name.replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/([A-Z])([A-Z][a-z])/g, '$1_$2').toLowerCase();
|
||||
}
|
||||
|
||||
export function formatObject(value: any, indent = ' ', mode: 'multiline' | 'oneline' = 'multiline'): string {
|
||||
if (typeof value === 'string')
|
||||
return escapeWithQuotes(value, '\'');
|
||||
if (Array.isArray(value))
|
||||
return `[${value.map(o => formatObject(o)).join(', ')}]`;
|
||||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value).filter(key => key !== 'timeout' && value[key] !== undefined).sort();
|
||||
if (!keys.length)
|
||||
return '{}';
|
||||
const tokens: string[] = [];
|
||||
for (const key of keys)
|
||||
tokens.push(`${key}: ${formatObject(value[key])}`);
|
||||
if (mode === 'multiline')
|
||||
return `{\n${tokens.map(t => indent + t).join(`,\n`)}\n}`;
|
||||
return `{ ${tokens.join(', ')} }`;
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
export function formatObjectOrVoid(value: any, indent = ' '): string {
|
||||
const result = formatObject(value, indent);
|
||||
return result === '{}' ? '' : result;
|
||||
}
|
||||
|
||||
export function quoteCSSAttributeValue(text: string): string {
|
||||
return `"${text.replace(/["\\]/g, char => '\\' + char)}"`;
|
||||
}
|
||||
|
||||
let normalizedWhitespaceCache: Map<string, string> | undefined;
|
||||
|
||||
export function cacheNormalizedWhitespaces() {
|
||||
normalizedWhitespaceCache = new Map();
|
||||
}
|
||||
|
||||
export function normalizeWhiteSpace(text: string): string {
|
||||
let result = normalizedWhitespaceCache?.get(text);
|
||||
if (result === undefined) {
|
||||
result = text.replace(/[\u200b\u00ad]/g, '').trim().replace(/\s+/g, ' ');
|
||||
normalizedWhitespaceCache?.set(text, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function normalizeEscapedRegexQuotes(source: string) {
|
||||
// This function reverses the effect of escapeRegexForSelector below.
|
||||
// Odd number of backslashes followed by the quote -> remove unneeded backslash.
|
||||
return source.replace(/(^|[^\\])(\\\\)*\\(['"`])/g, '$1$2$3');
|
||||
}
|
||||
|
||||
function escapeRegexForSelector(re: RegExp): string {
|
||||
// Unicode mode does not allow "identity character escapes", so we do not escape and
|
||||
// hope that it does not contain quotes and/or >> signs.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Character_escape
|
||||
// TODO: rework RE usages in internal selectors away from literal representation to json, e.g. {source,flags}.
|
||||
if (re.unicode || (re as any).unicodeSets)
|
||||
return String(re);
|
||||
// Even number of backslashes followed by the quote -> insert a backslash.
|
||||
return String(re).replace(/(^|[^\\])(\\\\)*(["'`])/g, '$1$2\\$3').replace(/>>/g, '\\>\\>');
|
||||
}
|
||||
|
||||
export function escapeForTextSelector(text: string | RegExp, exact: boolean): string {
|
||||
if (typeof text !== 'string')
|
||||
return escapeRegexForSelector(text);
|
||||
return `${JSON.stringify(text)}${exact ? 's' : 'i'}`;
|
||||
}
|
||||
|
||||
export function escapeForAttributeSelector(value: string | RegExp, exact: boolean): string {
|
||||
if (typeof value !== 'string')
|
||||
return escapeRegexForSelector(value);
|
||||
// TODO: this should actually be
|
||||
// cssEscape(value).replace(/\\ /g, ' ')
|
||||
// However, our attribute selectors do not conform to CSS parsing spec,
|
||||
// so we escape them differently.
|
||||
return `"${value.replace(/\\/g, '\\\\').replace(/["]/g, '\\"')}"${exact ? 's' : 'i'}`;
|
||||
}
|
||||
|
||||
export function trimString(input: string, cap: number, suffix: string = ''): string {
|
||||
if (input.length <= cap)
|
||||
return input;
|
||||
const chars = [...input];
|
||||
if (chars.length > cap)
|
||||
return chars.slice(0, cap - suffix.length).join('') + suffix;
|
||||
return chars.join('');
|
||||
}
|
||||
|
||||
export function trimStringWithEllipsis(input: string, cap: number): string {
|
||||
return trimString(input, cap, '\u2026');
|
||||
}
|
||||
|
||||
export function escapeRegExp(s: string) {
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
}
|
||||
|
||||
const escaped = { '&': '&', '<': '<', '>': '>', '"': '"', '\'': ''' };
|
||||
export function escapeHTMLAttribute(s: string): string {
|
||||
return s.replace(/[&<>"']/ug, char => (escaped as any)[char]);
|
||||
}
|
||||
export function escapeHTML(s: string): string {
|
||||
return s.replace(/[&<]/ug, char => (escaped as any)[char]);
|
||||
}
|
||||
|
||||
export function longestCommonSubstring(s1: string, s2: string): string {
|
||||
const n = s1.length;
|
||||
const m = s2.length;
|
||||
let maxLen = 0;
|
||||
let endingIndex = 0;
|
||||
|
||||
// Initialize a 2D array with zeros
|
||||
const dp = Array(n + 1)
|
||||
.fill(null)
|
||||
.map(() => Array(m + 1).fill(0));
|
||||
|
||||
// Build the dp table
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let j = 1; j <= m; j++) {
|
||||
if (s1[i - 1] === s2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + 1;
|
||||
|
||||
if (dp[i][j] > maxLen) {
|
||||
maxLen = dp[i][j];
|
||||
endingIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the longest common substring
|
||||
return s1.slice(endingIndex - maxLen, endingIndex);
|
||||
}
|
||||
|
||||
export function parseRegex(regex: string): RegExp {
|
||||
if (regex[0] !== '/')
|
||||
throw new Error(`Invalid regex, must start with '/': ${regex}`);
|
||||
const lastSlash = regex.lastIndexOf('/');
|
||||
if (lastSlash <= 0)
|
||||
throw new Error(`Invalid regex, must end with '/' followed by optional flags: ${regex}`);
|
||||
const source = regex.slice(1, lastSlash);
|
||||
const flags = regex.slice(lastSlash + 1);
|
||||
return new RegExp(source, flags);
|
||||
}
|
||||
|
||||
// Semicolons removed from [[\]()#;?] to avoid polynomial backtracking
|
||||
// when both that group and (?:;...)* can match runs of semicolons.
|
||||
// \d{1,4} relaxed to \d{0,4} so empty params (e.g. ESC[;H) still match.
|
||||
export const ansiRegex = new RegExp('([\\u001B\\u009B][[\\]()#?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{0,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))', 'g');
|
||||
export function stripAnsiEscapes(str: string): string {
|
||||
return str.replace(ansiRegex, '');
|
||||
}
|
||||
Reference in New Issue
Block a user