fix: add better perf marks / logging system

This commit is contained in:
Nolan Lawson 2020-06-04 18:31:31 -07:00
parent 9ffe091598
commit 51040b3d96
13 changed files with 100 additions and 59 deletions

View File

@ -6,9 +6,9 @@
"module": "index.js",
"type": "module",
"files": [
"database.js",
"index.js",
"picker.js"
"database.js*",
"index.js*",
"picker.js*"
],
"scripts": {
"build": "NODE_ENV=production rollup -c",
@ -70,6 +70,7 @@
"indexedDB",
"IDBKeyRange",
"matchMedia",
"performance",
"requestAnimationFrame",
"requestIdleCallback"
]

View File

@ -18,6 +18,8 @@ const baseConfig = {
cjs(),
json(),
replace({
'process.env.NODE_ENV': dev ? '"development"' : '"production"',
'process.env.PERF': !!process.env.PERF,
'process.env.VERSIONS_AND_TEST_EMOJI': JSON.stringify(versionsAndTestEmoji)
}),
replace({

View File

@ -11,6 +11,7 @@ import {
isEmpty, hasData, loadData, getEmojiByGroup,
getEmojiBySearchPrefix, getEmojiByShortcode, getEmojiByUnicode
} from './idbInterface'
import { log } from '../shared/log'
export default class Database {
constructor ({ dataSource = DEFAULT_DATA_SOURCE, locale = DEFAULT_LOCALE } = {}) {
@ -37,7 +38,7 @@ export default class Database {
const eTag = headResponse.headers.get('etag')
warnETag(eTag)
if (eTag && await hasData(this._db, url, eTag)) {
console.log('Database already populated')
log('Database already populated')
return // fast init, data is already loaded
}
}
@ -62,7 +63,7 @@ export default class Database {
eTag = await jsonChecksum(emojiBaseData)
}
if (!empty && await hasData(this._db, url, eTag)) {
console.log('Database already populated')
log('Database already populated')
return // data already loaded
}

View File

@ -6,6 +6,7 @@ import {
STORE_META
} from './constants'
import { transformEmojiBaseData } from './transformEmojiBaseData'
import { mark, stop } from '../shared/marks'
export async function isEmpty (db) {
return !(await get(db, STORE_META, KEY_URL))
@ -17,59 +18,64 @@ export async function hasData (db, url, eTag) {
}
export async function loadData (db, emojiBaseData, url, eTag) {
const transformedData = transformEmojiBaseData(emojiBaseData)
const [oldETag, oldUrl] = await get(db, STORE_META, [KEY_ETAG, KEY_URL])
if (oldETag === eTag && oldUrl === url) {
return
}
await dbPromise(db, [STORE_EMOJI, STORE_META], MODE_READWRITE, ([emojiStore, metaStore]) => {
let oldETag
let oldUrl
let oldKeys
let todo = 0
function checkFetched () {
if (++todo === 3) {
onFetched()
}
mark('loadData')
try {
const transformedData = transformEmojiBaseData(emojiBaseData)
const [oldETag, oldUrl] = await get(db, STORE_META, [KEY_ETAG, KEY_URL])
if (oldETag === eTag && oldUrl === url) {
return
}
await dbPromise(db, [STORE_EMOJI, STORE_META], MODE_READWRITE, ([emojiStore, metaStore]) => {
let oldETag
let oldUrl
let oldKeys
let todo = 0
function onFetched () {
if (oldETag === eTag && oldUrl === url) {
// check again within the transaction to guard against concurrency, e.g. multiple browser tabs
return
}
if (oldKeys.length) {
for (const key of oldKeys) {
emojiStore.delete(key)
function checkFetched () {
if (++todo === 3) {
onFetched()
}
}
insertData()
}
function insertData () {
for (const data of transformedData) {
emojiStore.put(data)
function onFetched () {
if (oldETag === eTag && oldUrl === url) {
// check again within the transaction to guard against concurrency, e.g. multiple browser tabs
return
}
if (oldKeys.length) {
for (const key of oldKeys) {
emojiStore.delete(key)
}
}
insertData()
}
metaStore.put(eTag, KEY_ETAG)
metaStore.put(url, KEY_URL)
}
metaStore.get(KEY_ETAG).onsuccess = e => {
oldETag = e.target.result
checkFetched()
}
function insertData () {
for (const data of transformedData) {
emojiStore.put(data)
}
metaStore.put(eTag, KEY_ETAG)
metaStore.put(url, KEY_URL)
}
metaStore.get(KEY_URL).onsuccess = e => {
oldUrl = e.target.result
checkFetched()
}
metaStore.get(KEY_ETAG).onsuccess = e => {
oldETag = e.target.result
checkFetched()
}
emojiStore.getAllKeys().onsuccess = e => {
oldKeys = e.target.result
checkFetched()
}
})
metaStore.get(KEY_URL).onsuccess = e => {
oldUrl = e.target.result
checkFetched()
}
emojiStore.getAllKeys().onsuccess = e => {
oldKeys = e.target.result
checkFetched()
}
})
} finally {
stop('loadData')
}
}
export async function getEmojiByGroup (db, group) {

View File

@ -1,4 +1,5 @@
import { MIN_SEARCH_TEXT_LENGTH } from '../shared/constants'
import { mark, stop } from '../shared/marks'
function extractTokens (annotation) {
return annotation
@ -11,7 +12,8 @@ function extractTokens (annotation) {
}
export function transformEmojiBaseData (emojiBaseData) {
return emojiBaseData.map(({ annotation, emoticon, group, order, shortcodes, tags, emoji, version }) => {
mark('transformEmojiBaseData')
const res = emojiBaseData.map(({ annotation, emoticon, group, order, shortcodes, tags, emoji, version }) => {
const tokens = [...new Set(
[
...shortcodes.map(extractTokens).flat(),
@ -38,4 +40,6 @@ export function transformEmojiBaseData (emojiBaseData) {
}
return res
})
stop('transformEmojiBaseData')
return res
}

View File

@ -10,6 +10,8 @@ import { calculateTextWidth } from '../../utils/calculateTextWidth'
import { hasZwj } from '../../utils/hasZwj'
import { thunk } from '../../utils/thunk'
import { emojiSupportLevel, supportedZwjEmojis } from '../../utils/emojiSupport'
import { log } from '../../../shared/log'
import { mark, stop } from '../../../shared/marks'
let database
let currentEmojis = []
@ -59,6 +61,7 @@ $: {
}
function checkZwjSupport (zwjEmojisToCheck) {
mark('checkZwjSupport')
const rootNode = rootElement.getRootNode()
for (const emoji of zwjEmojisToCheck) {
const domNode = rootNode.getElementById(`emoji-${emoji.unicode}`)
@ -68,9 +71,10 @@ function checkZwjSupport (zwjEmojisToCheck) {
const supported = emojiWidth.toFixed(1) === baselineEmojiWidth.toFixed(1)
supportedZwjEmojis.set(emoji.unicode, supported)
if (!supported) {
console.log('Filtered unsupported emoji', emoji.unicode)
log('Filtered unsupported emoji', emoji.unicode)
}
}
stop('checkZwjSupport')
// force update
currentEmojis = currentEmojis // eslint-disable-line no-self-assign
}

View File

@ -1,7 +1,6 @@
// get the width of the text inside of a DOM node, via https://stackoverflow.com/a/59525891/680742
let range
export function calculateTextWidth (node) {
range = range || document.createRange()
const range = document.createRange()
range.selectNode(node.firstChild)
return range.getBoundingClientRect().width
}

View File

@ -1,10 +1,12 @@
// rather than check every emoji ever, which would be expensive, just check some representatives from the
// different emoji releases to determine what the font supports
import isEmoji from 'if-emoji'
import { mark, stop } from '../../shared/marks'
const versionsAndTestEmoji = process.env.VERSIONS_AND_TEST_EMOJI
export function determineEmojiSupportLevel () {
mark('determineEmojiSupportLevel')
const versionsWithSupports = versionsAndTestEmoji.map(({ version, emoji }) => {
const supported = isEmoji(emoji)
return {
@ -12,8 +14,10 @@ export function determineEmojiSupportLevel () {
supported
}
})
return versionsWithSupports
const res = versionsWithSupports
.filter(_ => _.supported)
.map(_ => _.version)
.sort((a, b) => a < b ? 1 : -1)[0]
stop('determineEmojiSupportLevel')
return res
}

View File

@ -1,8 +1,9 @@
import { determineEmojiSupportLevel } from './determineEmojiSupportLevel'
import { log } from '../../shared/log'
// Check which emojis we know for sure aren't supported, based on Unicode version level
export const emojiSupportLevel = determineEmojiSupportLevel()
// determine which emojis containing ZWJ (zero width joiner) characters
// are supported (rendered as one glyph) rather than unsupported (rendered as two or more glyphs)
export const supportedZwjEmojis = new Map()
console.log('emoji support level', emojiSupportLevel)
log('emoji support level', emojiSupportLevel)

View File

@ -1,3 +1,3 @@
const rIC = typeof requestIdleCallback === 'function' ? requestIdleCallback : requestAnimationFrame
const rIC = typeof requestIdleCallback === 'function' ? requestIdleCallback : setTimeout
export { rIC as requestIdleCallback }

6
src/shared/log.js Normal file
View File

@ -0,0 +1,6 @@
// @rollup/plugin-strip doesn't strip console.logs properly
export function log () {
if (process.env.NODE_ENV !== 'production') {
console.log(...arguments)
}
}

13
src/shared/marks.js Normal file
View File

@ -0,0 +1,13 @@
// @rollup/plugin-strip doesn't properly strip performance.mark/measure
export function mark (str) {
if (process.env.NODE_ENV !== 'production' || process.env.PERF) {
performance.mark(str)
}
}
export function stop (str) {
if (process.env.NODE_ENV !== 'production' || process.env.PERF) {
performance.measure(str, str)
}
}

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ad-hoc test</title>
<title>Ad-hoc lite-emoji-picker test</title>
<style>
.container {
height: 300px;
@ -11,12 +11,12 @@
</style>
</head>
<body>
<h1>Ad-hoc test</h1>
<h1>Ad-hoc lite-emoji-picker test</h1>
<div class="container">
<lite-emoji-picker></lite-emoji-picker>
</div>
<script type="module">
import '../index.js'
import './index.js'
</script>
</body>
</html>