@escapenavigator/utils 1.10.143 → 1.10.145
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/quill-clipboard-matchers.d.ts +17 -0
- package/dist/quill-clipboard-matchers.js +22 -0
- package/dist/transform-text.js +12 -0
- package/package.json +3 -3
- package/dist/profile-logo-url.d.ts +0 -8
- package/dist/profile-logo-url.js +0 -38
- package/dist/profile-logo-url.test.d.ts +0 -1
- package/dist/profile-logo-url.test.js +0 -34
|
@@ -22,6 +22,23 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import Delta from 'quill-delta';
|
|
24
24
|
export type ClipboardMatcher = [number | string, (node: Node, delta: Delta) => Delta];
|
|
25
|
+
/**
|
|
26
|
+
* Убирает неразрывные пробелы из Quill-HTML: и литеральные сущности
|
|
27
|
+
* (` `/` `/` `), и сам символ U+00A0.
|
|
28
|
+
*
|
|
29
|
+
* ВАЖНО: не вешать на `onChange` контролируемого ReactQuill. Редактор
|
|
30
|
+
* сравнивает `value`-проп со своей внутренней сериализацией (она хранит
|
|
31
|
+
* ` `); если на каждый change подсовывать очищенное значение, оно
|
|
32
|
+
* никогда не совпадёт — ReactQuill бесконечно вызывает `setEditorContents`
|
|
33
|
+
* → `onChange` → `setState` и страница виснет («Maximum update depth»).
|
|
34
|
+
*
|
|
35
|
+
* Правильные места: (1) нормализация при ЗАГРУЗКЕ/сохранении (см.
|
|
36
|
+
* `normalizeIfTags`, `sanitizeQuillHtml`), (2) clipboard-matcher на вставку
|
|
37
|
+
* (`normalizeWhitespaceInDelta` ниже), (3) рендер письма (`transformText`).
|
|
38
|
+
* nbsp вреден тем, что запрещает перенос строки (абзацы рвутся посреди
|
|
39
|
+
* слова) и ломает разбор `{{#if X}}` (нужен обычный пробел после `#if`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function stripNonBreakingSpaces(html?: string | null): string;
|
|
25
42
|
/**
|
|
26
43
|
* Полный набор matcher-ов: и color-strip, и nbsp-normalize. Подключается там,
|
|
27
44
|
* где редактор не должен принимать цветную раскраску из буфера (waiver, AGB,
|
|
@@ -26,9 +26,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
};
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
28
|
exports.stripColorQuillModules = void 0;
|
|
29
|
+
exports.stripNonBreakingSpaces = stripNonBreakingSpaces;
|
|
29
30
|
exports.getStripColorClipboardMatchers = getStripColorClipboardMatchers;
|
|
30
31
|
exports.getNormalizeWhitespaceClipboardMatchers = getNormalizeWhitespaceClipboardMatchers;
|
|
31
32
|
const quill_delta_1 = __importDefault(require("quill-delta"));
|
|
33
|
+
/**
|
|
34
|
+
* Убирает неразрывные пробелы из Quill-HTML: и литеральные сущности
|
|
35
|
+
* (` `/` `/` `), и сам символ U+00A0.
|
|
36
|
+
*
|
|
37
|
+
* ВАЖНО: не вешать на `onChange` контролируемого ReactQuill. Редактор
|
|
38
|
+
* сравнивает `value`-проп со своей внутренней сериализацией (она хранит
|
|
39
|
+
* ` `); если на каждый change подсовывать очищенное значение, оно
|
|
40
|
+
* никогда не совпадёт — ReactQuill бесконечно вызывает `setEditorContents`
|
|
41
|
+
* → `onChange` → `setState` и страница виснет («Maximum update depth»).
|
|
42
|
+
*
|
|
43
|
+
* Правильные места: (1) нормализация при ЗАГРУЗКЕ/сохранении (см.
|
|
44
|
+
* `normalizeIfTags`, `sanitizeQuillHtml`), (2) clipboard-matcher на вставку
|
|
45
|
+
* (`normalizeWhitespaceInDelta` ниже), (3) рендер письма (`transformText`).
|
|
46
|
+
* nbsp вреден тем, что запрещает перенос строки (абзацы рвутся посреди
|
|
47
|
+
* слова) и ломает разбор `{{#if X}}` (нужен обычный пробел после `#if`).
|
|
48
|
+
*/
|
|
49
|
+
function stripNonBreakingSpaces(html) {
|
|
50
|
+
if (!html)
|
|
51
|
+
return html ?? '';
|
|
52
|
+
return html.replace(/ | | |\u00A0/gi, ' ');
|
|
53
|
+
}
|
|
32
54
|
const stripColorFromDelta = (_node, delta) => delta.compose(new quill_delta_1.default().retain(delta.length(), {
|
|
33
55
|
color: false,
|
|
34
56
|
background: false,
|
package/dist/transform-text.js
CHANGED
|
@@ -25,6 +25,18 @@ const BOOKING_BUTTON_STYLE = `
|
|
|
25
25
|
.trim();
|
|
26
26
|
const transformText = (text, obj) => {
|
|
27
27
|
let res = text || '';
|
|
28
|
+
// Quill-сохранённый HTML (особенно вставленный из Word/Docs или старого
|
|
29
|
+
// редактора) приходит с неразрывными пробелами (` `/U+00A0) между
|
|
30
|
+
// словами. Они ломают два места ниже:
|
|
31
|
+
// 1) разбор `{{#if X}}`-блоков — regex ждёт обычный `\s` после `#if`,
|
|
32
|
+
// а в тексте стоит литеральная сущность ` ` (`{{#if X}}`),
|
|
33
|
+
// из-за чего блок не матчится и теги печатаются в письмо как есть;
|
|
34
|
+
// 2) перенос строки — nbsp браузер/почтовый клиент не считают точкой
|
|
35
|
+
// переноса, поэтому длинные абзацы не переносятся и вылезают за
|
|
36
|
+
// контейнер (рвутся посреди слова).
|
|
37
|
+
// Нормализуем в обычный пробел — тот же контракт, что у `sanitizeQuillHtml`
|
|
38
|
+
// на фронте и у clipboard-matcher'ов редактора.
|
|
39
|
+
res = res.replace(/ | | |\u00A0/gi, ' ');
|
|
28
40
|
const bookingButtonRegex = /\{\{#manageBooking\}\}([\s\S]*?)\{\{\/manageBooking\}\}/g;
|
|
29
41
|
res = res.replace(bookingButtonRegex, (_match, buttonText) => {
|
|
30
42
|
const link = obj.bookingManagementLink;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@escapenavigator/utils",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.145",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"test": "jest"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@escapenavigator/types": "^1.10.
|
|
17
|
+
"@escapenavigator/types": "^1.10.141",
|
|
18
18
|
"axios": "^0.21.4",
|
|
19
19
|
"class-transformer": "^0.5.1",
|
|
20
20
|
"class-validator": "^0.13.2",
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"ts-jest": "^29.1.1",
|
|
29
29
|
"typescript": "^5.6"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "059625444023e0ba8978903b33ac18e9503f2f9d"
|
|
32
32
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Email clients (Gmail и др.) плохо переваривают WebP — PNG лежит рядом
|
|
3
|
-
* с тем же basename. В БД храним только WebP URL; при отправке письма
|
|
4
|
-
* подменяем расширение.
|
|
5
|
-
*/
|
|
6
|
-
export declare function profileLogoUrlForEmail(logoUrl: string | null | undefined): string;
|
|
7
|
-
/** TipTap/Maily doc: image-ноды с profile logo WebP → PNG перед send-render. */
|
|
8
|
-
export declare function replaceProfileLogoUrlsInDoc<T>(doc: T): T;
|
package/dist/profile-logo-url.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.profileLogoUrlForEmail = profileLogoUrlForEmail;
|
|
4
|
-
exports.replaceProfileLogoUrlsInDoc = replaceProfileLogoUrlsInDoc;
|
|
5
|
-
const PROFILE_LOGO_PATH = 'profile_logos/';
|
|
6
|
-
/**
|
|
7
|
-
* Email clients (Gmail и др.) плохо переваривают WebP — PNG лежит рядом
|
|
8
|
-
* с тем же basename. В БД храним только WebP URL; при отправке письма
|
|
9
|
-
* подменяем расширение.
|
|
10
|
-
*/
|
|
11
|
-
function profileLogoUrlForEmail(logoUrl) {
|
|
12
|
-
if (!logoUrl)
|
|
13
|
-
return logoUrl ?? '';
|
|
14
|
-
if (!logoUrl.includes(PROFILE_LOGO_PATH) || !/\.webp(\?.*)?$/i.test(logoUrl)) {
|
|
15
|
-
return logoUrl;
|
|
16
|
-
}
|
|
17
|
-
return logoUrl.replace(/\.webp(\?.*)?$/i, '.png$1');
|
|
18
|
-
}
|
|
19
|
-
/** TipTap/Maily doc: image-ноды с profile logo WebP → PNG перед send-render. */
|
|
20
|
-
function replaceProfileLogoUrlsInDoc(doc) {
|
|
21
|
-
if (!doc || typeof doc !== 'object')
|
|
22
|
-
return doc;
|
|
23
|
-
if (Array.isArray(doc)) {
|
|
24
|
-
return doc.map((item) => replaceProfileLogoUrlsInDoc(item));
|
|
25
|
-
}
|
|
26
|
-
const node = { ...doc };
|
|
27
|
-
if (node.type === 'image' && node.attrs && typeof node.attrs === 'object') {
|
|
28
|
-
const attrs = { ...node.attrs };
|
|
29
|
-
if (typeof attrs.src === 'string') {
|
|
30
|
-
attrs.src = profileLogoUrlForEmail(attrs.src);
|
|
31
|
-
}
|
|
32
|
-
node.attrs = attrs;
|
|
33
|
-
}
|
|
34
|
-
if (Array.isArray(node.content)) {
|
|
35
|
-
node.content = node.content.map((child) => replaceProfileLogoUrlsInDoc(child));
|
|
36
|
-
}
|
|
37
|
-
return node;
|
|
38
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const profile_logo_url_1 = require("./profile-logo-url");
|
|
4
|
-
describe('profileLogoUrlForEmail', () => {
|
|
5
|
-
it('replaces webp with png for profile logos', () => {
|
|
6
|
-
const url = 'https://escapenavigator-images.s3.eu-central-1.amazonaws.com/dev/profile_logos/123-imgABC.webp';
|
|
7
|
-
expect((0, profile_logo_url_1.profileLogoUrlForEmail)(url)).toBe('https://escapenavigator-images.s3.eu-central-1.amazonaws.com/dev/profile_logos/123-imgABC.png');
|
|
8
|
-
});
|
|
9
|
-
it('preserves query string', () => {
|
|
10
|
-
const url = 'https://cdn.example.com/profile_logos/x.webp?v=1';
|
|
11
|
-
expect((0, profile_logo_url_1.profileLogoUrlForEmail)(url)).toBe('https://cdn.example.com/profile_logos/x.png?v=1');
|
|
12
|
-
});
|
|
13
|
-
it('leaves non-profile and non-webp URLs unchanged', () => {
|
|
14
|
-
expect((0, profile_logo_url_1.profileLogoUrlForEmail)('https://cdn.example.com/inside/logo.png')).toBe('https://cdn.example.com/inside/logo.png');
|
|
15
|
-
expect((0, profile_logo_url_1.profileLogoUrlForEmail)(null)).toBe('');
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
describe('replaceProfileLogoUrlsInDoc', () => {
|
|
19
|
-
it('walks nested image nodes', () => {
|
|
20
|
-
const doc = {
|
|
21
|
-
type: 'doc',
|
|
22
|
-
content: [
|
|
23
|
-
{
|
|
24
|
-
type: 'image',
|
|
25
|
-
attrs: {
|
|
26
|
-
src: 'https://cdn.example.com/profile_logos/a.webp',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
};
|
|
31
|
-
const result = (0, profile_logo_url_1.replaceProfileLogoUrlsInDoc)(doc);
|
|
32
|
-
expect(result.content[0].attrs.src).toBe('https://cdn.example.com/profile_logos/a.png');
|
|
33
|
-
});
|
|
34
|
-
});
|