feat: simple snapshooter (#232)

This commit is contained in:
Miralem Drek
2019-12-11 10:33:40 +01:00
committed by GitHub
parent 326465d0ed
commit cf717a57e7
29 changed files with 1000 additions and 199 deletions

View File

@@ -72,6 +72,10 @@ jobs:
name: Test component
command: yarn run test:component --chrome.browserWSEndpoint "ws://localhost:3000" --no-launch
- run:
name: Test mashup
command: yarn run test:mashup --chrome.browserWSEndpoint "ws://localhost:3000" --no-launch
- run:
name: Test integration
command: APP_ID=$DOC_ID yarn run test:integration --chrome.browserWSEndpoint "ws://localhost:3000" --no-launch
@@ -93,5 +97,9 @@ jobs:
cd generated/barchart
yarn run build
APP_ID=$DOC_ID yarn run test:integration --mocha.timeout 30000 --chrome.browserWSEndpoint "ws://localhost:3000" --no-launch
- store_artifacts:
path: generated/barchart/screenshots
- store_artifacts:
path: test/mashup/__artifacts__

2
.gitignore vendored
View File

@@ -12,3 +12,5 @@ node_modules/
coverage/
dist/
temp/
test/**/__artifacts__/regression
test/**/__artifacts__/diff

View File

@@ -12,6 +12,9 @@ export default function translator({ initial = 'en-US', fallback = 'en-US' } = {
* @interface Translator
*/
const api = {
language: () => {
return currentLocale;
},
/**
* Register a string in multiple locales
* @param {object} item

View File

@@ -1,5 +1,5 @@
/* eslint-disable react/jsx-props-no-spreading */
import React, { forwardRef, useImperativeHandle, useEffect, useState, useContext, useReducer } from 'react';
import React, { forwardRef, useImperativeHandle, useEffect, useState, useContext, useReducer, useRef } from 'react';
import { Grid, Paper } from '@material-ui/core';
import { useTheme } from '@nebula.js/ui/theme';
@@ -170,6 +170,7 @@ const Cell = forwardRef(({ nebulaContext, model, initialSnContext, initialSnOpti
const [contentRef, contentRect, , contentNode] = useRect();
const [snContext, setSnContext] = useState(initialSnContext);
const [snOptions, setSnOptions] = useState(initialSnOptions);
const cellRef = useRef();
useEffect(() => {
const validate = sn => {
@@ -231,7 +232,7 @@ const Cell = forwardRef(({ nebulaContext, model, initialSnContext, initialSnOpti
() => ({
setSnContext,
setSnOptions,
takeSnapshot: async () => {
async takeSnapshot() {
const snapshot = {
...layout,
snapshotData: {
@@ -248,8 +249,29 @@ const Cell = forwardRef(({ nebulaContext, model, initialSnContext, initialSnOpti
}
return snapshot;
},
async exportImage() {
if (!nebulaContext.snapshot.capture) {
throw new Error('Nebula has not been configured with snapshot.capture');
}
const lyt = await this.takeSnapshot(); // eslint-disable-line
const { width, height } = cellRef.current.getBoundingClientRect();
const s = {
meta: {
language: translator.language(),
theme: theme.name,
// direction: 'ltr',
size: {
width: Math.round(width),
height: Math.round(height),
},
},
layout: lyt,
};
return nebulaContext.snapshot.capture(s);
},
}),
[state.sn, contentRect, layout]
[state.sn, contentRect, layout, theme.name]
);
// console.log('content', state);
let Content = null;
@@ -276,6 +298,7 @@ const Cell = forwardRef(({ nebulaContext, model, initialSnContext, initialSnOpti
elevation={0}
square
className="nebulajs-cell"
ref={cellRef}
>
<Grid
container

View File

@@ -48,6 +48,25 @@ const DEFAULT_CONFIG = {
themes: [],
/** */
env: {},
snapshot: {
get: async id => {
const res = await fetch(`/njs/snapshot/${id}`);
if (!res.ok) {
throw new Error(res.statusText);
}
return res.json();
},
capture(payload) {
return fetch(`/njs/capture`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
}).then(res => res.json());
},
},
};
const mergeConfigs = (base, c) => ({
@@ -57,6 +76,9 @@ const mergeConfigs = (base, c) => ({
locale: {
language: (c.locale ? c.locale.language : '') || base.locale.language,
},
snapshot: {
...(c.snapshot || base.snapshot),
},
types: [
// TODO - filter to avoid duplicates
...(base.types || []),
@@ -143,6 +165,7 @@ function nuked(configuration = {}, prev = {}) {
types,
root,
theme: appTheme.externalAPI,
snapshot: configuration.snapshot,
};
let currentThemePromise = appTheme.setTheme(configuration.theme);
@@ -251,6 +274,7 @@ function nuked(configuration = {}, prev = {}) {
* nucleus(app).create({ type: 'mekko' }); // will throw error since 'mekko' is not a register type on the default instance
*/
nucleus.configured = c => nuked(mergeConfigs(configuration, c));
nucleus.config = configuration;
return nucleus;
}

View File

@@ -113,8 +113,12 @@ export default function viz({ model, context: nebulaContext } = {}) {
return api;
},
takeSnapshot() {
// TODO - decide if this method is useful at all
return cellRef.current.takeSnapshot();
},
exportImage(settings) {
return cellRef.current.exportImage(settings);
},
// QVisualization API
// close() {},

View File

@@ -0,0 +1,80 @@
/* eslint no-param-reassign: 0 */
const puppeteer = require('puppeteer');
function snapshooter({ snapshotUrl, chrome = {} } = {}) {
const snapshots = {};
const images = {};
let browser;
let cachedPage;
let rendering = false;
const hasConnectOptions = chrome.browserWSEndpoint || chrome.browserURL;
const createBrowser = () => (hasConnectOptions ? puppeteer.connect(chrome) : puppeteer.launch(chrome));
const doIt = async (snapshot, runCached) => {
browser = browser || (await createBrowser());
let page;
if (runCached) {
cachedPage = cachedPage || (await browser.newPage());
page = cachedPage;
} else {
page = await browser.newPage();
}
await page.setViewport({
width: snapshot.meta.size.width,
height: snapshot.meta.size.height,
});
await page.goto(`${snapshotUrl}?snapshot=${snapshot.key}`);
try {
await page.waitFor(
() =>
(document.querySelector('.nebulajs-sn') &&
+document.querySelector('.nebulajs-sn').getAttribute('data-render-count') > 0) ||
document.querySelector('[data-njs-error]')
);
} catch (e) {
// empty
}
const image = await page.screenshot();
images[snapshot.key] = image;
rendering = false;
return snapshot.key;
};
return {
getStoredImage(id) {
return images[id];
},
getStoredSnapshot(id) {
return snapshots[id];
},
storeSnapshot(snapshot) {
if (!snapshot) {
throw new Error('Empty snapshot');
}
if (!snapshot.key) {
snapshot.key = String(+Date.now());
}
snapshots[snapshot.key] = snapshot;
return snapshot.key;
},
async captureImageOfSnapshot(snapshot) {
this.storeSnapshot(snapshot);
if (rendering) {
return doIt(snapshot);
}
rendering = true;
const key = await doIt(snapshot, true);
rendering = false;
return key;
},
async close() {
if (browser) {
await browser.close();
}
},
};
}
module.exports = snapshooter;

View File

@@ -0,0 +1,32 @@
{
"name": "@nebula.js/snapshooter",
"version": "0.1.0-alpha.25",
"description": "",
"license": "MIT",
"author": "QlikTech International AB",
"keywords": [
"qlik",
"nebula",
"supernova",
"snapshot"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/qlik-oss/nebula.js.git"
},
"main": "lib/snapshooter.js",
"files": [
"lib",
"dist"
],
"scripts": {
"build": "cross-env NODE_ENV=production rollup --config ./rollup.config.js",
"prepublishOnly": "rm -rf dist && yarn run build"
},
"dependencies": {
"puppeteer": "2.0.0"
}
}

View File

@@ -0,0 +1,61 @@
const path = require('path');
const babel = require('rollup-plugin-babel'); // eslint-disable-line
const { terser } = require('rollup-plugin-terser'); // eslint-disable-line
const cwd = process.cwd();
const pkg = require(path.join(cwd, 'package.json')); // eslint-disable-line
const { name, version, license } = pkg;
const banner = `/*
* ${name} v${version}
* Copyright (c) ${new Date().getFullYear()} QlikTech International AB
* Released under the ${license} license.
*/
`;
const browserList = [
'last 2 Chrome versions',
'last 2 Firefox versions',
'last 2 Edge versions',
'Safari >= 10.0',
'iOS >= 11.2',
];
const cfg = {
input: path.resolve(cwd, 'src', 'renderer'),
output: {
file: path.resolve(cwd, 'dist/renderer.js'),
format: 'umd',
exports: 'default',
name: 'snapshooter',
banner,
},
plugins: [
babel({
babelrc: false,
presets: [
[
'@babel/preset-env',
{
modules: false,
targets: {
browsers: [...browserList, ...['ie 11', 'chrome 47']],
},
},
],
],
}),
],
};
if (process.env.NODE_ENV === 'production') {
cfg.plugins.push(
terser({
output: {
preamble: banner,
},
})
);
}
module.exports = [cfg].filter(Boolean);

View File

@@ -0,0 +1,81 @@
/* eslint no-param-reassign: 0 */
async function renderSnapshot({ nucleus, element }) {
const params = (() => {
const opts = {};
const { pathname } = window.location;
const am = pathname.match(/\/app\/([^/?&]+)/);
if (am) {
opts.app = decodeURIComponent(am[1]);
}
window.location.search
.substring(1)
.split('&')
.forEach(pair => {
const idx = pair.indexOf('=');
const name = pair.substr(0, idx);
const value = decodeURIComponent(pair.substring(idx + 1));
opts[name] = value;
});
return opts;
})();
let snapshot = {};
const renderError = e => {
element.innerHTML = `
<p>${e.message}</p>
`;
element.setAttribute('data-njs-error', e.message);
};
try {
snapshot = await nucleus.config.snapshot.get(params.snapshot);
} catch (e) {
renderError(e);
throw e;
}
const {
meta: { theme, language },
layout,
} = snapshot;
const objectModel = {
async getLayout() {
return layout;
},
on() {},
once() {},
};
const app = {
async getObject(id) {
if (id === layout.qInfo.qId) {
return objectModel;
}
return Promise.reject();
},
};
const nebbie = await nucleus(app, {
theme,
locale: {
language,
},
});
nebbie
.get(
{
id: layout.qInfo.qId,
},
{
element,
}
)
.catch(e => {
renderError(e || { message: 'Failed to render supernova' });
throw e;
});
}
export default renderSnapshot;

View File

@@ -0,0 +1,54 @@
const express = require('express');
const bodyParser = require('body-parser');
module.exports = function router({ base, snapshotUrl, snapshooter }) {
const r = express.Router();
r.use(
bodyParser.json({
limit: '10mb',
})
);
// returns an existing image
r.get('/image/:id', (req, res) => {
const p = snapshooter.getStoredImage(req.params.id);
if (p) {
res.contentType('image/png');
res.end(p, 'binary');
} else {
res.sendStatus('404');
}
});
r.post('/capture', async (req, res) => {
try {
const key = await snapshooter.captureImageOfSnapshot(req.body);
res.send({
url: `${base}/image/${key}`,
});
} catch (e) {
console.error(e);
res.sendStatus('500');
}
});
// post snapshot
r.post('/snapshot', (req, res) => {
const key = snapshooter.storeSnapshot(req.body);
res.json({
url: `${snapshotUrl}?snapshot=${key}`,
});
});
r.get('/snapshot/:id', (req, res) => {
const p = snapshooter.getStoredSnapshot(req.params.id);
if (p) {
res.json(p);
} else {
res.sendStatus('404');
}
});
return r;
};

View File

@@ -1,90 +0,0 @@
const bodyParser = require('body-parser');
const puppeteer = require('puppeteer');
function snapshotter({ host, port }) {
const snapshots = {};
const images = {};
let browser;
return {
addRoutes(app) {
app.use(
bodyParser.json({
limit: '10mb',
})
);
app.get('/image/:id', (req, res) => {
const p = images[req.params.id];
if (p) {
res.contentType('image/png');
res.end(p, 'binary');
} else {
res.sendStatus('404');
}
});
app.post('/image', async (req, res) => {
let snap;
try {
snap = req.body;
if (!snap.key) {
snap.key = String(+Date.now());
}
snapshots[snap.key] = snap;
} catch (e) {
res.sendStatus('400');
}
try {
browser = browser || (await puppeteer.launch());
const page = await browser.newPage();
await page.setViewport({
width: snap.meta.size.width,
height: snap.meta.size.height,
});
await page.goto(`http://${host}:${port}/render?snapshot=${snap.key}`);
await page.waitFor(
() =>
document.querySelector('.nebulajs-sn') &&
+document.querySelector('.nebulajs-sn').getAttribute('data-render-count') > 0
);
const image = await page.screenshot();
images[snap.key] = image;
res.send({
url: `/image/${snap.key}`,
});
} catch (e) {
console.error(e);
res.sendStatus('503');
}
});
app.post('/snapshot', (req, res) => {
try {
const snap = req.body;
if (!snap.key) {
snap.key = String(+Date.now());
}
snapshots[snap.key] = snap;
res.json({
url: `/render?snapshot=${snap.key}`,
});
} catch (e) {
console.error(e);
res.sendStatus('400');
}
});
app.get('/snapshot/:id', (req, res) => {
const p = snapshots[req.params.id];
if (p) {
res.json(p);
} else {
res.sendStatus('404');
}
});
},
};
}
module.exports = snapshotter;

View File

@@ -30,6 +30,7 @@ const cfg = ({ srcDir, distDir, dev = false, serveConfig = {} }) => {
'@nebula.js/nucleus/src/hooks': path.resolve(process.cwd(), 'apis/nucleus/src/hooks'),
'@nebula.js/nucleus/src/object': path.resolve(process.cwd(), 'apis/nucleus/src/object'),
'@nebula.js/nucleus': path.resolve(process.cwd(), 'apis/nucleus/src'),
'@nebula.js/snapshooter/dist': path.resolve(process.cwd(), 'apis/snapshooter/src'),
'@nebula.js/supernova': path.resolve(process.cwd(), 'apis/supernova/src'),
'@nebula.js/theme': path.resolve(process.cwd(), 'apis/theme/src'),
'@nebula.js/locale': path.resolve(process.cwd(), 'apis/locale/src'),

View File

@@ -6,7 +6,8 @@ const express = require('express');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const snapshotter = require('./snapshot');
const snapshooter = require('@nebula.js/snapshooter');
const snapshotRouter = require('./snapshot-router');
module.exports = async ({
host,
@@ -23,10 +24,12 @@ module.exports = async ({
let config;
let contentBase;
const snapper = snapshotter({
host,
port,
images: serveConfig.images || [],
const snapshotRoute = '/njs';
const snapRouter = snapshotRouter({
base: `http://${host}:${port}${snapshotRoute}`,
snapshotUrl: `http://${host}:${port}/eRender.html`,
snapshooter: snapshooter({ snapshotUrl: `http://${host}:${port}/eRender.html` }),
});
const themes = serveConfig.themes || [];
@@ -69,7 +72,7 @@ module.exports = async ({
index: '/eHub.html',
},
before(app) {
snapper.addRoutes(app);
app.use(snapshotRoute, snapRouter);
entryWatcher.addRoutes(app);

View File

@@ -27,6 +27,7 @@
},
"dependencies": {
"@nebula.js/cli-build": "0.1.0-alpha.25",
"@nebula.js/snapshooter": "0.1.0-alpha.25",
"body-parser": "1.19.0",
"chalk": "3.0.0",
"chokidar": "3.3.0",

View File

@@ -11,30 +11,6 @@ import VizContext from '../contexts/VizContext';
import Chart from './Chart';
const takeAndSendSnapshot = ({ ref, route = '/snapshot', theme, language }) =>
ref.viz.takeSnapshot().then(snapshot => {
const containerSize = ref.el.getBoundingClientRect();
return fetch(route, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
key: snapshot.qInfo.qId,
meta: {
language,
theme,
direction: '',
size: {
width: Math.round(containerSize.width),
height: Math.round(containerSize.height),
},
},
layout: snapshot,
}),
}).then(response => response.json());
});
export default function({ id, expandable, minHeight }) {
const language = 'en-US'; // TODO - useLocale
const app = useContext(AppContext);
@@ -90,7 +66,7 @@ export default function({ id, expandable, minHeight }) {
}
if (doExport) {
setExporting(true);
takeAndSendSnapshot({ ref: vizRef.current, route: '/image', theme: currentThemeName, language }).then(res => {
vizRef.current.viz.exportImage().then(res => {
if (res && res.url) {
window.open(res.url);
}
@@ -98,10 +74,11 @@ export default function({ id, expandable, minHeight }) {
});
} else {
const containerSize = vizRef.current.el.getBoundingClientRect();
takeAndSendSnapshot({ ref: vizRef.current, theme: currentThemeName, language }).then(res => {
vizRef.current.viz.exportImage().then(res => {
if (res && res.url) {
const key = /\/([A-z0-9_]+)$/.exec(res.url)[1];
window.open(
res.url,
`/render?snapshot=${key}`,
'snapshot',
`height=${Math.round(containerSize.height)},width=${Math.round(containerSize.width)}`
);

View File

@@ -1,4 +1,6 @@
import nucleus from '@nebula.js/nucleus';
import snapshooter from '@nebula.js/snapshooter/dist/renderer';
import { openApp, params, info as serverInfo } from './connect';
import runFixture from './run-fixture';
@@ -65,56 +67,28 @@ async function renderWithEngine() {
}
async function renderSnapshot() {
const info = await serverInfo;
const { themes, supernova } = await serverInfo;
const element = document.querySelector('#chart-container');
element.classList.toggle('full', true);
const snapshot = await (await fetch(`/snapshot/${params.snapshot}`)).json();
const layout = {
...snapshot.layout,
visualization: info.supernova.name,
};
const {
meta: { theme, language },
} = snapshot;
const objectModel = {
async getLayout() {
return layout;
},
on() {},
once() {},
};
const app = {
async getObject(id) {
if (id === layout.qInfo.qId) {
return objectModel;
}
return Promise.reject();
},
};
const nebbie = await nuke({ app, ...info, theme, language });
const getCfg = {
id: layout.qInfo.qId,
};
const vizCfg = {
element,
context: {
permissions: ['passive'],
},
options: {
onInitialRender() {
document.querySelector('.nebulajs-sn').setAttribute('data-rendered', '1');
const n = nucleus.configured({
themes: themes
? themes.map(t => ({
key: t,
load: async () => (await fetch(`/theme/${t}`)).json(),
}))
: undefined,
types: [
{
load: (type, config) => config.Promise.resolve(window[type.name]),
name: supernova.name,
},
},
};
const render = () => {
nebbie.get(getCfg, vizCfg);
};
window.onHotChange(info.supernova.name, () => render());
],
});
snapshooter({
nucleus: n,
element,
});
}
const renderFixture = async () => {

View File

@@ -12,6 +12,7 @@
"start:ui": "start-storybook",
"spec": "lerna run spec --stream --concurrency 99",
"test": "yarn run test:unit",
"test:mashup": "aw puppet -c aw.config.js --testExt '*.int.js' --glob 'test/mashup/**/*.int.js'",
"test:integration": "aw puppet -c aw.config.js --testExt '*.int.js' --glob 'test/integration/*.int.js'",
"test:component": "aw puppet -c aw.config.js --testExt '*.comp.js' --glob 'test/component/**/*.comp.js'",
"test:unit": "aw -c aw.config.js --nyc.reportDir coverage/unit --coverage"
@@ -45,6 +46,7 @@
"@storybook/react": "5.2.8",
"babel-loader": "8.0.6",
"babel-plugin-istanbul": "5.2.0",
"body-parser": "1.19.0",
"cross-env": "6.0.3",
"enigma.js": "2.6.3",
"eslint": "6.7.2",
@@ -55,7 +57,9 @@
"eslint-plugin-mocha": "6.2.2",
"eslint-plugin-prettier": "3.1.1",
"eslint-plugin-react": "7.17.0",
"express": "4.17.1",
"husky": "3.1.0",
"jimp": "^0.9.3",
"lerna": "3.19.0",
"lint-staged": "9.5.0",
"mocha-junit-reporter": "^1.23.1",

View File

@@ -80,7 +80,9 @@ const overrides = theme => ({
export default function create(definition) {
let def = light;
let name = '';
if (typeof definition === 'string') {
name = definition;
if (definition !== 'light' && definition !== 'dark') {
console.warn(`Invalid theme: '${definition}'`);
} else if (definition === 'dark') {
@@ -116,32 +118,7 @@ export default function create(definition) {
overrides: overrides(withDefaults),
});
// cache[key] = createMuiTheme({
// typography: {
// ...defaults.typography,
// },
// shadows: defaults.shadows,
// props: {
// ...defaults.props,
// },
// shape: {
// ...defaults.shape,
// },
// overrides: {
// ...defaults.overrides,
// ...def.overrides,
// },
// palette: {
// secondary: {
// light: '#0AAF54',
// main: '#009845',
// dark: '#006937',
// contrastText: '#fff',
// },
// type: def.type,
// ...def.palette,
// },
// });
cache[key].name = name;
return cache[key];
}

View File

@@ -137,6 +137,7 @@ const config = isEsm => {
include: [
'/**/apis/locale/**',
'/**/apis/nucleus/**',
'/**/apis/snapshooter/**',
'/**/apis/supernova/**',
'/**/apis/theme/**',
'/**/packages/ui/**',
@@ -170,4 +171,4 @@ const config = isEsm => {
return cfg;
};
module.exports = [watch ? false : config(), config(true)].filter(Boolean);
module.exports = [watch ? false : config(), pkg.module ? config(true) : false].filter(Boolean);

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

77
test/mashup/server.js Normal file
View File

@@ -0,0 +1,77 @@
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const yargs = require('yargs/yargs');
module.exports = async () => {
let server;
let snapshooter;
const app = express();
const port = 8050;
const url = `http://localhost:${port}`;
const args = yargs(process.argv.slice(2)).argv;
// eslint-disable-next-line
snapshooter = require('@nebula.js/snapshooter')({
snapshotUrl: `${url}/snaps/single.html`,
chrome: args.chrome || {},
});
app.use(express.static(path.resolve(__dirname)));
app.use('/apis', express.static(path.resolve(__dirname, '../../apis')));
app.use('/node_modules', express.static(path.resolve(__dirname, '../../node_modules')));
app.use(
bodyParser.json({
limit: '10mb',
})
);
app.get('/njs/image/:id', (req, res) => {
const p = snapshooter.getStoredImage(req.params.id);
if (p) {
res.contentType('image/png');
res.end(p, 'binary');
} else {
res.sendStatus('404');
}
});
app.post('/njs/capture', async (req, res) => {
try {
const key = await snapshooter.captureImageOfSnapshot(req.body);
res.send({
url: `/njs/image/${key}`,
});
} catch (e) {
console.error(e);
res.sendStatus('500');
}
});
app.get('/njs/snapshot/:id', (req, res) => {
const p = snapshooter.getStoredSnapshot(req.params.id);
if (p) {
res.json(p);
} else {
res.sendStatus('404');
}
});
await new Promise(resolve => {
server = app.listen(port, () => {
console.log(`Running mashup server at localhost:${port}`);
resolve();
});
});
return {
url,
async close() {
server.close();
await snapshooter.close();
},
};
};

22
test/mashup/setup.int.js Normal file
View File

@@ -0,0 +1,22 @@
const server = require('./server');
if (!process.env.BASE_URL) {
let s;
before(async () => {
s = await server();
process.env.BASE_URL = s.url;
page.on('pageerror', e => {
console.error('Web: ', e.message);
});
page.on('console', msg => {
for (let i = 0; i < msg.args().length; ++i) {
console.log(`console ${msg.text()}`);
}
});
});
after(async () => {
await s.close();
});
}

View File

@@ -0,0 +1,41 @@
const pie = {
component: {
mounted(el) {
el.innerHTML = 'Hello pie'; // eslint-disable-line
},
},
};
const bar = function(env) {
env.translator.add({
id: 'hello',
locale: {
'sv-SE': 'Hej {0}!',
},
});
return {
component: {
mounted(el) {
el.innerHTML = `<div style="font-size: 64px;">${env.translator.get('hello', ['bar'])}</div>`; // eslint-disable-line
},
},
};
};
// eslint-disable-next-line
const configured = nucleus.configured({
theme: 'dark',
locale: {
language: 'sv-SE',
},
types: [
{
name: 'pie',
load: () => Promise.resolve(pie),
},
{
name: 'bar',
load: () => Promise.resolve(bar),
},
],
});

View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="/apis/nucleus/dist/nucleus.js"></script>
<script src="/apis/snapshooter/dist/renderer.js"></script>
<script src="configured.js"></script>
<style>
html,
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#object {
position: absolute;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="object"></div>
<script>
snapshooter({
nucleus: configured,
element: document.querySelector('#object'),
});
</script>
</body>
</html>

View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Mashup</title>
<script src="/apis/nucleus/dist/nucleus.js"></script>
<script src="configured.js"></script>
<style>
body {
background: #eee;
}
.object {
display: inline-block;
position: relative;
width: 300px;
height: 200px;
margin: 16px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body>
<div class="object" data-type="pie"></div>
<div class="object" data-type="bar"></div>
<script src="snapper.js"></script>
</body>
</html>

View File

@@ -0,0 +1,37 @@
const path = require('path');
const jimp = require('jimp');
const differ = () => {
const artifacts = path.resolve(__dirname, '../__artifacts__/');
return {
async looksLike(name, captured) {
const file = `${path.basename(__dirname)}-${name}`;
const stored = await jimp.read(path.resolve(artifacts, 'baseline', file));
const distance = jimp.distance(stored, captured);
const diff = jimp.diff(stored, captured);
if (distance > 0.001 || diff.percent > 0.001) {
captured.writeAsync(path.resolve(artifacts, 'regression', file));
throw new Error(`Images differ too much - distance: ${distance}, percent: ${diff.percent}`);
}
},
};
};
const d = differ();
describe('snapper', () => {
const object = '[data-type="bar"]';
const selector = `${object} .nebulajs-sn`;
it('should capture an image of a bar', async () => {
await page.goto(`${process.env.BASE_URL}/snaps/snapper.html`);
await page.waitForSelector(selector, { visible: true });
await page.click(selector);
await page.waitForSelector(`${object}[data-captured]`, { visible: true });
const imgSrc = await page.$eval(`${object}[data-captured]`, el => el.getAttribute('data-captured'));
const captured = await jimp.read(`${process.env.BASE_URL}${imgSrc}`);
await d.looksLike('bar.png', captured);
});
});

View File

@@ -0,0 +1,39 @@
/* global configured */
(() => {
document.querySelectorAll('.object').forEach(element => {
const type = element.getAttribute('data-type');
const obj = {
getLayout: () =>
Promise.resolve({
qInfo: {
qId: 'id:',
},
visualization: type,
}),
on() {},
once() {},
};
const app = {
createSessionObject: () => Promise.resolve({}),
getObject: () => Promise.resolve(obj),
};
const n = configured(app);
n.create(
{
type,
},
{
element,
}
).then(viz => {
element.addEventListener('click', () => {
viz.exportImage().then(res => {
element.setAttribute('data-captured', res.url);
});
});
});
});
})();

303
yarn.lock
View File

@@ -2040,6 +2040,13 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.7.2":
version "7.7.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f"
integrity sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/template@^7.1.0", "@babel/template@^7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
@@ -2611,6 +2618,16 @@
bmp-js "^0.1.0"
core-js "^2.5.7"
"@jimp/bmp@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.9.3.tgz#98eafc81674ce750f428ac9380007f1a4e90255e"
integrity sha512-wXZYccgGQAsIK8DZX0wZE3gbSd2mL2+eheSJMts6I5hQjxhVRZd1Gwu425nUQGzfKCOgKYTW0nLv7/8OoOTTkw==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
bmp-js "^0.1.0"
core-js "^3.4.1"
"@jimp/core@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.6.8.tgz#6a41089792516f6e64a5302d12eb562aa7847c7b"
@@ -2628,6 +2645,24 @@
pixelmatch "^4.0.2"
tinycolor2 "^1.4.1"
"@jimp/core@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.9.3.tgz#bffbf955c046569bf4b682b575228e31bb41e445"
integrity sha512-kB9lvst1QhgYOC963SAuPgv+DdVfxTProphrSffAAoo5eLeQab/Ca3ZUeX1E/SnLSr+NGVnNCd8c9gyuKDiENg==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
any-base "^1.1.0"
buffer "^5.2.0"
core-js "^3.4.1"
exif-parser "^0.1.12"
file-type "^9.0.0"
load-bmfont "^1.3.1"
mkdirp "0.5.1"
phin "^2.9.1"
pixelmatch "^4.0.2"
tinycolor2 "^1.4.1"
"@jimp/custom@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.6.8.tgz#0476d7b3f5da3121d98895a2e14f2899e602f2b6"
@@ -2636,6 +2671,15 @@
"@jimp/core" "^0.6.8"
core-js "^2.5.7"
"@jimp/custom@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.9.3.tgz#b49dfe1d6b24e62fd4101a7db77104024c8d97e8"
integrity sha512-2E7yabQMeqjcK8+ZFu3Ja5cWyrB0zv/pmzNSDg/BBPJ59HE0fj/qcERAz6VklcjHUYRUfmE5uODsb+4DE0o/YQ==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/core" "^0.9.3"
core-js "^3.4.1"
"@jimp/gif@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.6.8.tgz#848dd4e6e1a56ca2b3ce528969e44dfa99a53b14"
@@ -2645,6 +2689,16 @@
core-js "^2.5.7"
omggif "^1.0.9"
"@jimp/gif@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.9.3.tgz#b2b1a519092f94a913a955f252996f9a968930db"
integrity sha512-DshKgMQ8lXorI/xTRyeRkZqZ3JqgnL2aGYAhx0SkAunyHgXji27chmrOGj/6KVDBucrDf/6mSexnSoUDnlWrfA==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
omggif "^1.0.9"
"@jimp/jpeg@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.6.8.tgz#4cad85a6d1e15759acb56bddef29aa3473859f2c"
@@ -2654,6 +2708,16 @@
core-js "^2.5.7"
jpeg-js "^0.3.4"
"@jimp/jpeg@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.9.3.tgz#a759cb3bccf3cb163166873b9bdc0c949c5991b5"
integrity sha512-AJzcTJXfN9BHtpzAbICwR3+GoH0pSr6OYXbAS6yuKwz+xVn9UHrEjQb74CIzIRqrT/VWcIKg29cMQxgokzWY7w==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
jpeg-js "^0.3.4"
"@jimp/plugin-blit@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-blit/-/plugin-blit-0.6.8.tgz#646ebb631f35afc28c1e8908524bc43d1e9afa3d"
@@ -2662,6 +2726,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-blit@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-blit/-/plugin-blit-0.9.3.tgz#740346ac62ec0f7ae4458f5fd59c7582e630a8e8"
integrity sha512-+UxCsJ3XkRSdpigpTBJ9WkdwUc3OtBlhVZdU6OL6M9ldume5Gj3rTyWvMCqytOK1tZ/+7HmxoWe4IWX31hz9qA==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-blur@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-blur/-/plugin-blur-0.6.8.tgz#7b753ae94f6099103f57c268c3b2679047eefe95"
@@ -2670,6 +2743,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-blur@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-blur/-/plugin-blur-0.9.3.tgz#9df505aaa63de138060264cf83ed4a98304bf105"
integrity sha512-RADcYjZ5vbk5ZrUiK7qv0G4xOpHtu19HWVVX9JTDbm4VByWTxPboVKlgiYLA6l+IxIXNtEqDclsADIM0s9FQhA==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-color@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-color/-/plugin-color-0.6.8.tgz#4101cb1208879b331db6e43ea6b96eaf8dbaedbc"
@@ -2679,6 +2761,16 @@
core-js "^2.5.7"
tinycolor2 "^1.4.1"
"@jimp/plugin-color@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-color/-/plugin-color-0.9.3.tgz#4a5ad28f68901355878f5330186c260f4f87f944"
integrity sha512-gHDA5GVx4/R4fitEACKmWH7hNy0aU48MZWYRxmATvuqY39KidJ0fjwp+brQ3Ivgb35AgFVc2jQYc3U/JXv4RxQ==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
tinycolor2 "^1.4.1"
"@jimp/plugin-contain@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-contain/-/plugin-contain-0.6.8.tgz#af95d33b63d0478943374ae15dd2607fc69cad14"
@@ -2687,6 +2779,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-contain@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-contain/-/plugin-contain-0.9.3.tgz#d0da9892edea25549611c88e125bfcc59045c426"
integrity sha512-vdYAtp65LNDT/hMctow5o0a/SbD41/y7Z9AO7MGsfUIK92Woq90SNTWx7JplDl4HSZGrqaBONnfiEhRiYlDrdg==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-cover@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-cover/-/plugin-cover-0.6.8.tgz#490e3186627a34d93cc015c4169bac9070d6ad17"
@@ -2695,6 +2796,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-cover@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-cover/-/plugin-cover-0.9.3.tgz#2fca63620fcf8145bdecf315cf461588b09d9488"
integrity sha512-yOwsvakgyS2/C4iZF1a1wg63QKfYvqb2d6k+rgY/0vaAe44JtEx+Gbg+7iOt4EaMm5BDlxRwmcA2Q8Pef8TvAQ==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-crop@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-crop/-/plugin-crop-0.6.8.tgz#ffec8951a2f3eccad1e3cff9afff5326bd980ce7"
@@ -2703,6 +2813,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-crop@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-crop/-/plugin-crop-0.9.3.tgz#9b19c11293714a99c03d4b517ab597a5f88823e8"
integrity sha512-kqMXSyY8hrfo0idr6qY2USOWPrNqpDWs+D6Vwa+kV6SGJhj3rMTIcptQDaamIETSxbjkE8rwUu3K4Q5UD69D7w==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-displace@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-displace/-/plugin-displace-0.6.8.tgz#89df05ab7daaff6befc190bb8ac54ec8d57e533b"
@@ -2711,6 +2830,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-displace@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-displace/-/plugin-displace-0.9.3.tgz#07645687b29ebc8a8491244410172795d511ba21"
integrity sha512-0AdwxYRWDmJ2wIRIj2RR3sRmNjMhcy5Kwt9Jbi/RRnzxkRScZAiyzkNZhBul23EM7ClfjrUrZufuUvRMHxZRDw==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-dither@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-dither/-/plugin-dither-0.6.8.tgz#17e5b9f56575a871e329fef8b388e614b92d84f8"
@@ -2719,6 +2847,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-dither@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-dither/-/plugin-dither-0.9.3.tgz#292b3ee617a5dcfe065d13b643055e910f8b6934"
integrity sha512-8OE+Xak9xepiCwSV+oAsb/gupTnttG3aDKxtpSZjwHebnr+k1VG8NgICbMSFATTVJqqZ18oj6LC+5726qHUJ9w==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-flip@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-flip/-/plugin-flip-0.6.8.tgz#153df0c677f79d4078bb9e4c1f2ac392b96dc3a1"
@@ -2727,6 +2864,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-flip@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-flip/-/plugin-flip-0.9.3.tgz#a755ffa1d860106067215987cbac213501d22b41"
integrity sha512-w+lzE1ZF/UOjB8qJdeIm+dLQtOK1obZwGYdCIbgxZxw4SfkkjAftJdY8o8RNOXhHDZqGu+cYQZbMKP1zcoNkyQ==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-gaussian@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-gaussian/-/plugin-gaussian-0.6.8.tgz#100abc7ae1f19fe9c09ed41625b475aae7c6093c"
@@ -2735,6 +2881,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-gaussian@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-gaussian/-/plugin-gaussian-0.9.3.tgz#b10b5a5b4c37cb4edc3ed22a9b25294e68daf2f8"
integrity sha512-RPrWwzlZsbWC2opSgeyWt30JU9Uwg1+GwBnoNpEMLKeqm0Dv6snASASa4zVtviGWAIq//p3Jrap7g57hKqL0Cg==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-invert@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-invert/-/plugin-invert-0.6.8.tgz#f40bfaa3b592d21ff14ede0e49aabec88048cad0"
@@ -2743,6 +2898,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-invert@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-invert/-/plugin-invert-0.9.3.tgz#723a873133a1d62f9b93e023991f262c85917c78"
integrity sha512-0lRsh7IPkzyYqExrZDT50h38xdlB/+KrdiDcuxWwWyIlKauLMR0kInjwf8sPeb3elPLeETmze7uwPAxrIAtsGQ==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-mask@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-mask/-/plugin-mask-0.6.8.tgz#e64405f7dacf0672bff74f3b95b724d9ac517f86"
@@ -2751,6 +2915,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-mask@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-mask/-/plugin-mask-0.9.3.tgz#6329ec861269244ab10ab9b3f54b1624c4ce0bab"
integrity sha512-nZ0J62Hly9JtMZctlSDVgnTd8Fg2XGikzAYilSTCjzIRtbXL5Be/qSAZrMfLD3CZ8exTxdlEGRkEJI3RZKXYCw==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-normalize@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-normalize/-/plugin-normalize-0.6.8.tgz#a0180f2b8835e3638cdc5e057b44ac63f60db6ba"
@@ -2759,6 +2932,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-normalize@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-normalize/-/plugin-normalize-0.9.3.tgz#564155032d1b9dc567dbb7427a85606a25427c30"
integrity sha512-0IvgTt4R15QJnoCHvvqlK56zOtCsQV7Mkx757kdNah8uyPGjadTcFBuqCaOMK943X36IIv+o7Ix7yvNUJZt4aw==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-print@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-print/-/plugin-print-0.6.8.tgz#66309549e01896473111e3a0ad2cee428638bd6e"
@@ -2768,6 +2950,16 @@
core-js "^2.5.7"
load-bmfont "^1.4.0"
"@jimp/plugin-print@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-print/-/plugin-print-0.9.3.tgz#b4470137312232de9b35eaf412cd753f999c58d8"
integrity sha512-pV6oX5Bhe9O/dbgrotz46Bv6u1M+/n9G0kRUunDjwzXrvON5raBFEJHQDPcTXiqPT25Gc9Ba4/Akfo/Zl6+wgQ==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
load-bmfont "^1.4.0"
"@jimp/plugin-resize@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.6.8.tgz#c26d9a973f7eec51ad9018fcbbac1146f7a73aa0"
@@ -2776,6 +2968,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-resize@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.9.3.tgz#916abd57c4f9b426984354c77555ade1efda7a82"
integrity sha512-YzqVE8QoDIZpVuI52v+WejwEjEEiJfNFviQfprfm5af7uSSseZgDw1sJ0koqAu+liMSY+Ewp79v2SDrKoJKqtg==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-rotate@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-rotate/-/plugin-rotate-0.6.8.tgz#2afda247984eeebed95c1bb1b13ccd3be5973299"
@@ -2784,6 +2985,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-rotate@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-rotate/-/plugin-rotate-0.9.3.tgz#aa0d674c08726c0ae3ebc7f2adbfca0a927b1d9f"
integrity sha512-kADY2pI3/yMyHbuyvKB4nqPoKf8DPQBU1b4zz2K7SxcwKh1krFf4Fa9mmhhDLoFwuNSy0SPb1JCMUO4BtFCFLA==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugin-scale@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-scale/-/plugin-scale-0.6.8.tgz#5de403345859bb0b30bf3e242dedd8ceb6ecb96c"
@@ -2792,6 +3002,15 @@
"@jimp/utils" "^0.6.8"
core-js "^2.5.7"
"@jimp/plugin-scale@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugin-scale/-/plugin-scale-0.9.3.tgz#427fed7642883c27601aae33c25413980b6a2c50"
integrity sha512-vZaiL5Qc+WrgGEfUe4Y0vG+qbT6pe2TW68/mu124E1tKVcZjHKZUeFN0Wr/hP2myN6nqTYj0/sord2OS/04JpA==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
"@jimp/plugins@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/plugins/-/plugins-0.6.8.tgz#5618170a986ced1ea795adcd9376122f2543b856"
@@ -2817,6 +3036,32 @@
core-js "^2.5.7"
timm "^1.6.1"
"@jimp/plugins@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/plugins/-/plugins-0.9.3.tgz#bdff9d49484469c4d74ef47c2708e75773ca22b9"
integrity sha512-KYCSgFGoZBNC0224X5yUnMHCZnCdUVrsu2Yo67o3XZfUgDjO81J+vdzZ0twpPQ6qLLVAP+nQ8hkRV/QzEUstMw==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/plugin-blit" "^0.9.3"
"@jimp/plugin-blur" "^0.9.3"
"@jimp/plugin-color" "^0.9.3"
"@jimp/plugin-contain" "^0.9.3"
"@jimp/plugin-cover" "^0.9.3"
"@jimp/plugin-crop" "^0.9.3"
"@jimp/plugin-displace" "^0.9.3"
"@jimp/plugin-dither" "^0.9.3"
"@jimp/plugin-flip" "^0.9.3"
"@jimp/plugin-gaussian" "^0.9.3"
"@jimp/plugin-invert" "^0.9.3"
"@jimp/plugin-mask" "^0.9.3"
"@jimp/plugin-normalize" "^0.9.3"
"@jimp/plugin-print" "^0.9.3"
"@jimp/plugin-resize" "^0.9.3"
"@jimp/plugin-rotate" "^0.9.3"
"@jimp/plugin-scale" "^0.9.3"
core-js "^3.4.1"
timm "^1.6.1"
"@jimp/png@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.6.8.tgz#ee06cf078b381137ec7206c4bb1b4cfcbe15ca6f"
@@ -2826,6 +3071,16 @@
core-js "^2.5.7"
pngjs "^3.3.3"
"@jimp/png@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.9.3.tgz#5c1bbb89b32e2332891a13efdb423e87287a8321"
integrity sha512-LJXUemDTSbTGAGEp9hNQH0uTRSB8gYeE6FsfT3M00oZincu6/WzDzl0P8E95rMjNxZqAihdTyOP3+kcrbbqX+w==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.3"
core-js "^3.4.1"
pngjs "^3.3.3"
"@jimp/tiff@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.6.8.tgz#79bd22ed435edbe29d02a2c8c9bf829f988ebacc"
@@ -2834,6 +3089,15 @@
core-js "^2.5.7"
utif "^2.0.1"
"@jimp/tiff@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.9.3.tgz#a4498c0616fb24034f5512b159b75b0aea389e9c"
integrity sha512-w9H6dT+GDHN//Srsv27JhRn7R2byzUahOGfFw7KpIn95jg0ogcxjKTo/RAGQC56sr4U092e4Npl7E85Lt934WQ==
dependencies:
"@babel/runtime" "^7.7.2"
core-js "^3.4.1"
utif "^2.0.1"
"@jimp/types@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.6.8.tgz#4510eb635cd00b201745d70e38f791748baa7075"
@@ -2847,6 +3111,20 @@
core-js "^2.5.7"
timm "^1.6.1"
"@jimp/types@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.9.3.tgz#75337245a1a8c7c84a414beca3cfeded338c0ef1"
integrity sha512-hUJKoT2IhnbO/trxNWzN19n8g+p7aKbM1R+71n4wMZnD41PzrVtz+sBBCdB+JCjBJs/i7fJt4d9z0i3Xe8m7Zw==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/bmp" "^0.9.3"
"@jimp/gif" "^0.9.3"
"@jimp/jpeg" "^0.9.3"
"@jimp/png" "^0.9.3"
"@jimp/tiff" "^0.9.3"
core-js "^3.4.1"
timm "^1.6.1"
"@jimp/utils@^0.6.8":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.6.8.tgz#09f794945631173567aa50f72ac28170de58a63d"
@@ -2854,6 +3132,14 @@
dependencies:
core-js "^2.5.7"
"@jimp/utils@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.9.3.tgz#fd7af0d1138febbeacc841be4b802218444ce088"
integrity sha512-9D2Of6BcjYONtl77YfmU2y5aRMLe0/O2e2aQvfCxdNwD33jRdwNdN4i3m73dpiClNquApIjL4nYGhTixA4UstA==
dependencies:
"@babel/runtime" "^7.7.2"
core-js "^3.4.1"
"@lerna/add@3.19.0":
version "3.19.0"
resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.19.0.tgz#33b6251c669895f842c14f05961432d464166249"
@@ -6733,6 +7019,11 @@ core-js@^3.0.1, core-js@^3.0.4:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.3.6.tgz#6ad1650323c441f45379e176ed175c0d021eac92"
integrity sha512-u4oM8SHwmDuh5mWZdDg9UwNVq5s1uqq6ZDLLIs07VY+VJU91i3h4f3K/pgFvtUQPGdeStrZ+odKyfyt4EnKHfA==
core-js@^3.4.1:
version "3.4.8"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.4.8.tgz#e0fc0c61f2ef90cbc10c531dbffaa46dfb7152dd"
integrity sha512-b+BBmCZmVgho8KnBUOXpvlqEMguko+0P+kXCwD4vIprsXC6ht1qgPxtb1OK6XgSlrySF71wkwBQ0Hv695bk9gQ==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -10501,6 +10792,18 @@ jimp@0.6.8:
core-js "^2.5.7"
regenerator-runtime "^0.13.3"
jimp@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.9.3.tgz#85e8e80eea65a7e6de806c6bb622ec6a7244e6f3"
integrity sha512-dIxvT1OMRkd3+B18XUhJ5WZ2Dw7Hp8mvjaTqfi945zZ7fga6LT22h3NLYDorHHAiy9z30KjfNnOgpBoxrdjDZg==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/custom" "^0.9.3"
"@jimp/plugins" "^0.9.3"
"@jimp/types" "^0.9.3"
core-js "^3.4.1"
regenerator-runtime "^0.13.3"
jpeg-js@^0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.4.tgz#dc2ba501ee3d58b7bb893c5d1fab47294917e7e7"