diff --git a/lib/util/emoji.spec.ts b/lib/util/emoji.spec.ts index b8b15a9660c913d9e971ed2d7656d91b42e1fab9..5c7083d7b284ce6edbf5c5419643b05cb3b6cc28 100644 --- a/lib/util/emoji.spec.ts +++ b/lib/util/emoji.spec.ts @@ -1,22 +1,87 @@ +import { fromCodepointToUnicode, fromHexcodeToCodepoint } from 'emojibase'; import { getName } from '../../test/util'; -import { setEmojiConfig, unemojify } from './emoji'; +import { emojify, setEmojiConfig, stripEmojis, unemojify } from './emoji'; describe(getName(), () => { - it('strips emojis when the config has been set accordingly', () => { - const emoji = '🚀💎'; - const otherText = 'regular text'; - const text = `${emoji} ${otherText}`; - setEmojiConfig({ unicodeEmoji: false }); - const result = unemojify(text); - expect(result).not.toContain(emoji); + beforeEach(() => { + setEmojiConfig({ unicodeEmoji: true }); }); - it('does not strip emojis when the config demands it', () => { - const emoji = '🚀💎'; - const otherText = 'regular text'; - const text = `${emoji} ${otherText}`; - setEmojiConfig({ unicodeEmoji: true }); - const result = unemojify(text); - expect(result).toEqual(text); + describe('emojify', () => { + it('encodes known shortcodes', () => { + expect(emojify('Let it :bee:')).toEqual('Let it ðŸ'); + }); + + it('encodes aliases', () => { + const bee = emojify(':bee:'); + const honeyBee = emojify(':honeybee:'); + expect(bee).toEqual(honeyBee); + }); + + it('omits unknown shortcodes', () => { + expect(emojify(':foo: :bar: :bee:')).toEqual(':foo: :bar: ðŸ'); + }); + + it('does not encode when config option is disabled', () => { + setEmojiConfig({ unicodeEmoji: false }); + expect(emojify('Let it :bee:')).toEqual('Let it :bee:'); + }); + }); + + describe('unemojify', () => { + it('strips emojis when the config has been set accordingly', () => { + const emoji = '🚀💎'; + const otherText = 'regular text'; + const text = `${emoji} ${otherText}`; + setEmojiConfig({ unicodeEmoji: false }); + const result = unemojify(text); + expect(result).not.toContain(emoji); + }); + + it('does not strip emojis when the config demands it', () => { + const emoji = '🚀💎'; + const otherText = 'regular text'; + const text = `${emoji} ${otherText}`; + setEmojiConfig({ unicodeEmoji: true }); + const result = unemojify(text); + expect(result).toEqual(text); + }); + + describe('unsupported characters', () => { + const unsupported = '🪆'; + + it('uses replacement character', () => { + setEmojiConfig({ unicodeEmoji: false }); + expect(unemojify(unsupported)).toEqual('�'); + }); + }); + }); + + describe('problem characters', () => { + it.each(['🚀', '💎', '🧹', '📦'])('converts %s forth and back', (char) => { + setEmojiConfig({ unicodeEmoji: false }); + const codified = unemojify(char); + expect(codified).not.toEqual(char); + + setEmojiConfig({ unicodeEmoji: true }); + const emojified = emojify(codified); + expect(emojified).toEqual(char); + }); + }); + + describe('stripEmojis', () => { + const makeEmoji = (hexCode: string): string => + fromCodepointToUnicode(fromHexcodeToCodepoint(hexCode)); + + it('is independent of config option', () => { + const x: string = makeEmoji('26A0-FE0F'); + const y: string = makeEmoji('26A0'); + + setEmojiConfig({ unicodeEmoji: true }); + expect(stripEmojis(`foo ${x} bar`)).toEqual(`foo ${y} bar`); + + setEmojiConfig({ unicodeEmoji: false }); + expect(stripEmojis(`foo ${x} bar`)).toEqual(`foo ${y} bar`); + }); }); }); diff --git a/lib/util/emoji.ts b/lib/util/emoji.ts index ff21eed5aca9fed3571c5a5dc3ff603ddf28f59f..9e2a8b73ea93e96d27fc83c56199c286c8f87f90 100644 --- a/lib/util/emoji.ts +++ b/lib/util/emoji.ts @@ -1,16 +1,103 @@ -import emoji from 'node-emoji'; +import is from '@sindresorhus/is'; +import mathiasBynensEmojiRegex from 'emoji-regex'; +import { + fromCodepointToUnicode, + fromHexcodeToCodepoint, + fromUnicodeToHexcode, +} from 'emojibase'; +import emojibaseEmojiRegex from 'emojibase-regex/emoji'; +import SHORTCODE_REGEX from 'emojibase-regex/shortcode'; import type { RenovateConfig } from '../config/types'; +import dataFiles from '../data-files.generated'; +import { regEx } from './regex'; let unicodeEmoji = true; +let mappingsInitialized = false; +const shortCodesByHex = new Map<string, string>(); +const hexCodesByShort = new Map<string, string>(); + +function lazyInitMappings(): void { + if (!mappingsInitialized) { + const table: Record<string, string | string[]> = JSON.parse( + dataFiles.get('node_modules/emojibase-data/en/shortcodes/github.json') + ); + for (const [hex, val] of Object.entries(table)) { + const shortCodes: string[] = is.array<string>(val) ? val : [val]; + shortCodesByHex.set(hex, `:${shortCodes[0]}:`); + shortCodes.forEach((shortCode) => { + hexCodesByShort.set(`:${shortCode}:`, hex); + }); + } + mappingsInitialized = true; + } +} + export function setEmojiConfig(_config: RenovateConfig): void { unicodeEmoji = _config.unicodeEmoji; } +const shortCodeRegex = regEx(SHORTCODE_REGEX.source, 'g'); + export function emojify(text: string): string { - return unicodeEmoji ? emoji.emojify(text) : text; + if (!unicodeEmoji) { + return text; + } + lazyInitMappings(); + return text.replace(shortCodeRegex, (shortCode) => { + const hexCode = hexCodesByShort.get(shortCode); + return hexCode + ? fromCodepointToUnicode(fromHexcodeToCodepoint(hexCode)) + : shortCode; + }); +} + +const emojiRegexSrc = [emojibaseEmojiRegex, mathiasBynensEmojiRegex()].map( + ({ source }) => source +); +const emojiRegex = new RegExp(`(?:${emojiRegexSrc.join('|')})`, 'g'); +const excludedModifiers = new Set([ + '20E3', + '200D', + 'FE0E', + 'FE0F', + '1F3FB', + '1F3FC', + '1F3FD', + '1F3FE', + '1F3FF', + '1F9B0', + '1F9B1', + '1F9B2', + '1F9B3', +]); + +export function stripHexCode(input: string): string { + return input + .split('-') + .filter((modifier) => !excludedModifiers.has(modifier)) + .join('-'); } export function unemojify(text: string): string { - return unicodeEmoji ? text : emoji.unemojify(text); + if (unicodeEmoji) { + return text; + } + lazyInitMappings(); + return text.replace(emojiRegex, (emoji) => { + const hexCode = stripHexCode(fromUnicodeToHexcode(emoji)); + const shortCode = shortCodesByHex.get(hexCode); + return shortCode || '�'; + }); +} + +function stripEmoji(emoji: string): string { + const hexCode = stripHexCode(fromUnicodeToHexcode(emoji)); + const codePoint = fromHexcodeToCodepoint(hexCode); + const result = fromCodepointToUnicode(codePoint); + return result; +} + +export function stripEmojis(input: string): string { + return input.replace(emojiRegex, stripEmoji); } diff --git a/lib/workers/pr/__snapshots__/index.spec.ts.snap b/lib/workers/pr/__snapshots__/index.spec.ts.snap index f3ac046eb730fd49d2eede6b301b5823afe46fec..19997363f4e8a00168d4b1e8b4ffb9e54b620779 100644 --- a/lib/workers/pr/__snapshots__/index.spec.ts.snap +++ b/lib/workers/pr/__snapshots__/index.spec.ts.snap @@ -64,7 +64,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | pin | \`1.0.0\` -> \`1.1.0\` |\\n\\n📌 **Important**: Renovate will wait until you have merged this Pin PR before creating any *upgrade* PRs for the affected packages. Add the preset \`:preserveSemverRanges\` to your config if you instead don't wish to pin dependencies.\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" in timezone some timezone.\\n\\n🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™»ï¸ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | pin | \`1.0.0\` -> \`1.1.0\` |\\n\\n📌 **Important**: Renovate will wait until you have merged this Pin PR before creating any *upgrade* PRs for the affected packages. Add the preset \`:preserveSemverRanges\` to your config if you instead don't wish to pin dependencies.\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" in timezone some timezone.\\n\\n🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™» **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/dummy-1.x", "targetBranch": undefined, @@ -97,7 +97,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\n🚦 **Automerge**: Enabled.\\n\\nâ™»ï¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\n🚦 **Automerge**: Enabled.\\n\\nâ™» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/dummy-1.x", "targetBranch": undefined, @@ -117,7 +117,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [gitlabdummy](https://dummy.com) ([source](https://gitlab.com/renovateapp/gitlabdummy), [changelog](https://gitlab.com/renovateapp/gitlabdummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\n🚦 **Automerge**: Enabled.\\n\\nâ™»ï¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [gitlabdummy](https://dummy.com) ([source](https://gitlab.com/renovateapp/gitlabdummy), [changelog](https://gitlab.com/renovateapp/gitlabdummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\n🚦 **Automerge**: Enabled.\\n\\nâ™» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/gitlabdummy-1.x", "targetBranch": undefined, @@ -137,7 +137,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | lockFileMaintenance | \`1.0.0\` -> \`1.1.0\` |\\n| a | | | \`zzzzzz\` -> \`aaaaaaa\` |\\n| b | | pin | \`some_old_value\` -> \`some_new_value\` |\\n| c | | | \`\` -> \`\` |\\n| d | | lockFileMaintenance | \`\` -> \`\` |\\n\\nnote 1\\n\\nnote 2\\n\\n:warning: Release Notes retrieval for this PR were skipped because no github.com credentials were available.\\nIf you are using the hosted GitLab app, please follow [this guide](https://docs.renovatebot.com/install-gitlab-app/#configuring-a-token-for-githubcom-hosted-release-notes). If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/self-hosting.md#githubcom-token-for-release-notes) instead.\\n\\n🔡 If you wish to disable git hash updates, add \`\\":disableDigestUpdates\\"\` to the extends array in your config.\\n\\n🔧 This Pull Request updates lock files to use the latest dependency versions.\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: At any time (no schedule defined).\\n\\n🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™»ï¸ **Rebasing**: Never, or you tick the rebase/retry checkbox.\\n\\n👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | lockFileMaintenance | \`1.0.0\` -> \`1.1.0\` |\\n| a | | | \`zzzzzz\` -> \`aaaaaaa\` |\\n| b | | pin | \`some_old_value\` -> \`some_new_value\` |\\n| c | | | \`\` -> \`\` |\\n| d | | lockFileMaintenance | \`\` -> \`\` |\\n\\nnote 1\\n\\nnote 2\\n\\n:warning: Release Notes retrieval for this PR were skipped because no github.com credentials were available.\\nIf you are using the hosted GitLab app, please follow [this guide](https://docs.renovatebot.com/install-gitlab-app/#configuring-a-token-for-githubcom-hosted-release-notes). If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/self-hosting.md#githubcom-token-for-release-notes) instead.\\n\\n🔡 If you wish to disable git hash updates, add \`\\":disableDigestUpdates\\"\` to the extends array in your config.\\n\\n🔧 This Pull Request updates lock files to use the latest dependency versions.\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: At any time (no schedule defined).\\n\\n🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™» **Rebasing**: Never, or you tick the rebase/retry checkbox.\\n\\n👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/dummy-1.x", "targetBranch": undefined, @@ -157,7 +157,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>someproject</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: At any time (no schedule defined).\\n\\n🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™»ï¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>someproject</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: At any time (no schedule defined).\\n\\n🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/dummy-1.x", "targetBranch": undefined, @@ -231,7 +231,7 @@ Array [ exports[`workers/pr/index ensurePr should return modified existing PR 1`] = ` Object { - "body": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\n🚦 **Automerge**: Enabled.\\n\\nâ™»ï¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "body": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\n🚦 **Automerge**: Enabled.\\n\\nâ™» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "displayNumber": "Existing PR", "title": "Update dependency dummy to v1.1.0", } @@ -239,7 +239,7 @@ Object { exports[`workers/pr/index ensurePr should return modified existing PR title 1`] = ` Object { - "body": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\n🚦 **Automerge**: Enabled.\\n\\nâ™»ï¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "body": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n<details>\\n<summary>renovateapp/dummy</summary>\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n</details>\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\n🚦 **Automerge**: Enabled.\\n\\nâ™» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "displayNumber": "Existing PR", "title": "wrong", } diff --git a/lib/workers/pr/body/__snapshots__/controls.spec.ts.snap b/lib/workers/pr/body/__snapshots__/controls.spec.ts.snap index 41e5203f65f86fad9e50b8e494ab3f39e02d7f0b..63dd7e7a0066abc5254aae215f731cca90cdd16c 100644 --- a/lib/workers/pr/body/__snapshots__/controls.spec.ts.snap +++ b/lib/workers/pr/body/__snapshots__/controls.spec.ts.snap @@ -5,7 +5,7 @@ exports[`workers/pr/body/controls getControls when the branch is modified has t --- - - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box. âš ï¸ **Warning**: custom changes will be lost. + - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box. âš **Warning**: custom changes will be lost. " `; diff --git a/lib/workers/pr/body/controls.ts b/lib/workers/pr/body/controls.ts index 5d7d5943f7927022a57a0c1ca29620641f6b9ba0..2ee42f83560c96e28e72ee6ff22626d2ae64e852 100644 --- a/lib/workers/pr/body/controls.ts +++ b/lib/workers/pr/body/controls.ts @@ -1,4 +1,4 @@ -import { emojify } from 'node-emoji'; +import { emojify } from '../../../util/emoji'; import { isBranchModified } from '../../../util/git'; import { BranchConfig } from '../../types'; diff --git a/lib/workers/pr/index.ts b/lib/workers/pr/index.ts index 9d7cbbf3ce887352eb74058810e7da8adfdbd4fc..d5a92eb901e08ab88a3ee71c112619c1374cb726 100644 --- a/lib/workers/pr/index.ts +++ b/lib/workers/pr/index.ts @@ -10,6 +10,7 @@ import { PlatformPrOptions, Pr, platform } from '../../platform'; import { BranchStatus } from '../../types'; import { ExternalHostError } from '../../types/errors/external-host-error'; import { sampleSize } from '../../util'; +import { stripEmojis } from '../../util/emoji'; import { deleteBranch, getBranchLastCommitTime } from '../../util/git'; import * as template from '../../util/template'; import { Limit, incLimitedValue, isLimitReached } from '../global/limits'; @@ -341,17 +342,19 @@ export async function ensurePr( logger.debug('Stripping Reviewable content'); existingPrBody = existingPrBody.slice(0, reviewableIndex); } + const existingPrTitle = stripEmojis(existingPr.title); + const newPrTitle = stripEmojis(prTitle); existingPrBody = existingPrBody.trim(); if ( - existingPr.title === prTitle && - noWhitespaceOrHeadings(existingPrBody) === - noWhitespaceOrHeadings(prBody) + existingPrTitle === newPrTitle && + noWhitespaceOrHeadings(stripEmojis(existingPrBody)) === + noWhitespaceOrHeadings(stripEmojis(prBody)) ) { logger.debug(`${existingPr.displayNumber} does not need updating`); return { prResult: PrResult.NotUpdated, pr: existingPr }; } // PR must need updating - if (existingPr.title !== prTitle) { + if (existingPrTitle !== newPrTitle) { logger.debug( { branchName, diff --git a/lib/workers/repository/onboarding/pr/__snapshots__/errors-warnings.spec.ts.snap b/lib/workers/repository/onboarding/pr/__snapshots__/errors-warnings.spec.ts.snap index ba787dda8c7639f1102b8143d67fa15c0886244f..0262a1986aaba4697372518d12772fb69f0c10d6 100644 --- a/lib/workers/repository/onboarding/pr/__snapshots__/errors-warnings.spec.ts.snap +++ b/lib/workers/repository/onboarding/pr/__snapshots__/errors-warnings.spec.ts.snap @@ -4,7 +4,7 @@ exports[`workers/repository/onboarding/pr/errors-warnings getDepWarnings() retur " --- -### âš ï¸ Dependency Lookup Warnings âš ï¸ +### âš Dependency Lookup Warnings âš Please correct - or verify that you can safely ignore - these lookup failures before you merge this PR. diff --git a/package.json b/package.json index 00230ebed72469fb297d443f898a099e3af52c3f..8a4ac3f1d3d0e0b3bf21be3719a2c0adc8371ef1 100644 --- a/package.json +++ b/package.json @@ -144,6 +144,9 @@ "dequal": "2.0.2", "detect-indent": "6.0.0", "email-addresses": "4.0.0", + "emoji-regex": "9.2.2", + "emojibase": "5.1.1", + "emojibase-regex": "5.1.2", "fast-safe-stringify": "2.0.7", "find-up": "5.0.0", "fs-extra": "10.0.0", @@ -165,7 +168,6 @@ "markdown-table": "2.0.0", "minimatch": "3.0.4", "moo": "0.5.1", - "node-emoji": "1.10.0", "node-html-parser": "3.3.4", "p-all": "3.0.0", "p-map": "4.0.0", @@ -235,6 +237,7 @@ "@typescript-eslint/parser": "4.25.0", "conventional-changelog-conventionalcommits": "4.6.0", "cross-env": "7.0.3", + "emojibase-data": "6.1.2", "eslint": "7.27.0", "eslint-config-airbnb-typescript": "12.3.1", "eslint-config-prettier": "8.3.0", diff --git a/tools/generate-imports.mjs b/tools/generate-imports.mjs index db13c8cddd318700cdb5ae6a5425960733f1a0cb..4633a0d09e64408b5b9086c282a887b1637475bd 100644 --- a/tools/generate-imports.mjs +++ b/tools/generate-imports.mjs @@ -28,7 +28,10 @@ async function updateFile(file, code) { newFiles.add(file); } -const dataPaths = ['data']; +const dataPaths = [ + 'data', + 'node_modules/emojibase-data/en/shortcodes/github.json', +]; /** * diff --git a/yarn.lock b/yarn.lock index 6ce0a1037a84c676f3cbe48ce3721f25eae61708..70e235dc7792a08db4280f5b5f1b66155cbca3e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3696,11 +3696,31 @@ emittery@^0.8.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emoji-regex@9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emojibase-data@6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/emojibase-data/-/emojibase-data-6.1.2.tgz#c30331602419239579c0d7a032fa1232d8dc298f" + integrity sha512-1fYgpAYkAJq7bmGO3XboP0dQHrtRiyWrAluCmv5NHSK1nruz0x8kXhpLiz48rAJGDQOWYUpLGaPASTNXVZX87Q== + +emojibase-regex@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/emojibase-regex/-/emojibase-regex-5.1.2.tgz#96cc95e26b186866de1c42a16782d3242cc29c11" + integrity sha512-/n1k9npdzvJecnTy7qu324btGxWSi4GC4Uk9R4DLDc8CIuNx+a2RQLrDSL/0kOi0dIg4lP4h65wKICYGjyIDbw== + +emojibase@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/emojibase/-/emojibase-5.1.1.tgz#fe812023959cb60d0b1b6da16042b180c96c2b7c" + integrity sha512-qc/qfuzZ93xmYMhnVA15yP2k3t4zJvBbqCc8dpDXFf75eJ+IogbzJuWQur8fkwsZ0u7YvJDBh939F9DapQqktQ== + encoding@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -6906,7 +6926,7 @@ nock@13.0.11: lodash.set "^4.3.2" propagate "^2.0.0" -node-emoji@1.10.0, node-emoji@^1.10.0: +node-emoji@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==