diff --git a/packages/metascraper/src/get-data.js b/packages/metascraper/src/get-data.js index de160ec9c..a5632b6d4 100644 --- a/packages/metascraper/src/get-data.js +++ b/packages/metascraper/src/get-data.js @@ -3,6 +3,11 @@ const debug = require('debug-logfmt')('metascraper:get-data') const { findRule, has } = require('@metascraper/helpers') +const PRINTABLE_TYPES = new Set(['string', 'number', 'boolean', 'bigint']) + +const formatValue = value => + value === null || PRINTABLE_TYPES.has(typeof value) ? value : typeof value + const getData = async ({ rules, ...props }) => { const data = {} @@ -26,7 +31,9 @@ const getData = async ({ rules, ...props }) => { } duration( - `${propName}=${normalizedValue} rules=${innerRules.length} status=${status}` + `${propName}=${formatValue(normalizedValue)} rules=${ + innerRules.length + } status=${status}` ) data[propName] = normalizedValue }) diff --git a/packages/metascraper/test/unit/index.js b/packages/metascraper/test/unit/index.js index 8786b4e41..8957443b0 100644 --- a/packages/metascraper/test/unit/index.js +++ b/packages/metascraper/test/unit/index.js @@ -1,8 +1,32 @@ 'use strict' +const path = require('path') + const test = require('ava').default const createMetascraper = require('../..') +const debug = require(require.resolve('debug', { + paths: [path.dirname(require.resolve('debug-logfmt'))] +})) + +const withGetDataLogs = async fn => { + const previous = debug.disable() + debug.enable('metascraper:get-data') + const chunks = [] + const write = process.stderr.write + process.stderr.write = (chunk, encoding, cb) => { + chunks.push(Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)) + return write.call(process.stderr, chunk, encoding, cb) + } + try { + await fn() + return chunks.join('') + } finally { + process.stderr.write = write + debug.disable() + if (previous) debug.enable(previous) + } +} test('returns all rules fields by default', async t => { const rulesNames = [ @@ -86,3 +110,87 @@ test('avoid to run rules by `omitPropNames`', async t => { rulesNames.filter(field => field !== 'author') ) }) + +test('a rule value without a string form is returned as is', async t => { + const location = Object.assign(Object.create(null), { + href: 'https://example.com/' + }) + const cyclic = { name: 'cyclic' } + cyclic.self = cyclic + const customInspect = { + name: 'custom', + [Symbol.for('nodejs.util.inspect.custom')]: () => { + throw new Error('custom') + } + } + const revoked = Proxy.revocable({}, {}) + revoked.revoke() + const symbol = Symbol('value') + const bareFunction = Object.setPrototypeOf(() => {}, null) + + const metascraper = createMetascraper([ + { + location: [() => location], + cyclic: [() => cyclic], + customInspect: [() => customInspect], + revoked: [() => revoked.proxy], + symbol: [() => symbol], + bareFunction: [() => bareFunction], + title: [() => 'title'] + } + ]) + const metadata = await metascraper({ url: 'https://example.com' }) + + t.is(metadata.location, location) + t.is(metadata.cyclic, cyclic) + t.is(metadata.customInspect, customInspect) + t.is(metadata.revoked, null) + t.is(metadata.symbol, symbol) + t.is(metadata.bareFunction, bareFunction) + t.is(metadata.title, 'title') +}) + +test.serial( + 'logs a primitive rule value and the type of any other', + async t => { + const location = Object.assign(Object.create(null), { + href: 'https://example.com/' + }) + const symbol = Symbol('value') + const bareFunction = Object.setPrototypeOf(() => {}, null) + const metascraper = createMetascraper([ + { + label: [() => 'hello'], + count: [() => 0], + flag: [() => false], + big: [() => 10n], + empty: [() => null], + location: [() => location], + symbol: [() => symbol], + bareFunction: [() => bareFunction] + } + ]) + + let metadata + const log = await withGetDataLogs(async () => { + metadata = await metascraper({ url: 'https://example.com' }) + }) + + t.is(metadata.label, 'hello') + t.is(metadata.count, 0) + t.is(metadata.flag, false) + t.is(metadata.big, 10n) + t.is(metadata.empty, null) + t.is(metadata.location, location) + t.is(metadata.symbol, symbol) + t.is(metadata.bareFunction, bareFunction) + t.regex(log, /label=hello rules=/) + t.regex(log, /count=0 rules=/) + t.regex(log, /flag=false rules=/) + t.regex(log, /big=10 rules=/) + t.regex(log, /empty=null rules=/) + t.regex(log, /location=object rules=/) + t.regex(log, /symbol=symbol rules=/) + t.regex(log, /bareFunction=function rules=/) + } +)