Reduce bundle size (#7246)

* chore: use ESModule imports for d3 libraries

* chore: add d3 types

* chore: use minified plotly

* chore: use ESModule style imports for printj

* chore: use `terser-webpack-plugin` to minimize

* Revert "chore: use minified plotly"

This reverts commit 0ae9b39d41b6e38f0fe38cd89a2cd73869f31c36.

* Revert "Revert "chore: use minified plotly""

This reverts commit 08973a2d2e6675206907f678d447717ac6526613.

* fix: use default minification options

* test: stabilize notebook image drop e2e test

* test(fix): remove .only()

* refactor: convert TelemetryValueFormatter to es6 class

---------

Co-authored-by: Scott Bell <scott@traclabs.com>
This commit is contained in:
Jesse Mazzella 2023-12-20 11:23:24 -08:00 committed by GitHub
parent 0d97675a0a
commit 715a44864e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 62 deletions

View File

@ -69,9 +69,9 @@ const config = {
csv: 'comma-separated-values', csv: 'comma-separated-values',
EventEmitter: 'eventemitter3', EventEmitter: 'eventemitter3',
bourbon: 'bourbon.scss', bourbon: 'bourbon.scss',
'plotly-basic': 'plotly.js-basic-dist', 'plotly-basic': 'plotly.js-basic-dist-min',
'plotly-gl2d': 'plotly.js-gl2d-dist', 'plotly-gl2d': 'plotly.js-gl2d-dist-min',
printj: path.join(projectRootDir, 'node_modules/printj/dist/printj.min.js'), printj: 'printj/printj.mjs',
styles: path.join(projectRootDir, 'src/styles'), styles: path.join(projectRootDir, 'src/styles'),
MCT: path.join(projectRootDir, 'src/MCT'), MCT: path.join(projectRootDir, 'src/MCT'),
testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'), testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'),

View File

@ -19,12 +19,13 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/* global __dirname */
/* /*
This test suite is dedicated to tests which verify the basic operations surrounding Notebooks. This test suite is dedicated to tests which verify the basic operations surrounding Notebooks.
*/ */
const fs = require('fs').promises; const fs = require('fs').promises;
const path = require('path');
const { test, expect } = require('../../../../pluginFixtures'); const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions'); const { createDomainObjectWithDefaults } = require('../../../../appActions');
@ -176,7 +177,9 @@ test.describe('Snapshot image tests', () => {
}); });
test('Can drop an image onto a notebook and create a new entry', async ({ page }) => { test('Can drop an image onto a notebook and create a new entry', async ({ page }) => {
const imageData = await fs.readFile('src/images/favicons/favicon-96x96.png'); const imageData = await fs.readFile(
path.resolve(__dirname, '../../../../../src/images/favicons/favicon-96x96.png')
);
const imageArray = new Uint8Array(imageData); const imageArray = new Uint8Array(imageData);
const fileData = Array.from(imageArray); const fileData = Array.from(imageArray);
@ -201,14 +204,17 @@ test.describe('Snapshot image tests', () => {
// drop another image onto the entry // drop another image onto the entry
await page.dispatchEvent('.c-snapshots', 'drop', { dataTransfer: dropTransfer }); await page.dispatchEvent('.c-snapshots', 'drop', { dataTransfer: dropTransfer });
const secondThumbnail = page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).nth(1);
await secondThumbnail.waitFor({ state: 'attached' });
// expect two embedded images now // expect two embedded images now
expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(2); expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(2);
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click(); await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click();
await page.getByRole('menuitem', { name: /Remove This Embed/ }).click(); await page.getByRole('menuitem', { name: /Remove This Embed/ }).click();
await page.getByRole('button', { name: 'Ok', exact: true }).click(); await page.getByRole('button', { name: 'Ok', exact: true }).click();
// Ensure that the thumbnail is removed before we assert
await secondThumbnail.waitFor({ state: 'detached' });
// expect one embedded image now as we deleted the other // expect one embedded image now as we deleted the other
expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(1); expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(1);

View File

@ -11,6 +11,9 @@
"@percy/cli": "1.27.4", "@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4", "@percy/playwright": "1.0.4",
"@playwright/test": "1.39.0", "@playwright/test": "1.39.0",
"@types/d3-axis": "3.0.6",
"@types/d3-scale": "4.0.8",
"@types/d3-selection": "3.0.10",
"@types/eventemitter3": "1.2.0", "@types/eventemitter3": "1.2.0",
"@types/jasmine": "5.1.2", "@types/jasmine": "5.1.2",
"@types/lodash": "4.14.192", "@types/lodash": "4.14.192",
@ -62,8 +65,8 @@
"npm-run-all2": "6.1.1", "npm-run-all2": "6.1.1",
"nyc": "15.1.0", "nyc": "15.1.0",
"painterro": "1.2.87", "painterro": "1.2.87",
"plotly.js-basic-dist": "2.20.0", "plotly.js-basic-dist-min": "2.20.0",
"plotly.js-gl2d-dist": "2.20.0", "plotly.js-gl2d-dist-min": "2.20.0",
"prettier": "2.8.7", "prettier": "2.8.7",
"printj": "1.3.1", "printj": "1.3.1",
"resolve-url-loader": "5.0.0", "resolve-url-loader": "5.0.0",
@ -72,6 +75,7 @@
"sass-loader": "13.3.2", "sass-loader": "13.3.2",
"sinon": "17.0.0", "sinon": "17.0.0",
"style-loader": "3.3.3", "style-loader": "3.3.3",
"terser-webpack-plugin": "5.3.9",
"tiny-emitter": "2.1.0", "tiny-emitter": "2.1.0",
"typescript": "5.2.2", "typescript": "5.2.2",
"uuid": "9.0.1", "uuid": "9.0.1",

View File

@ -20,39 +20,25 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define(['lodash', 'printj'], function (_, printj) { import _ from 'lodash';
import { sprintf } from 'printj';
// TODO: needs reference to formatService; // TODO: needs reference to formatService;
function TelemetryValueFormatter(valueMetadata, formatMap) { export default class TelemetryValueFormatter {
constructor(valueMetadata, formatMap) {
this.valueMetadata = valueMetadata;
this.formatMap = formatMap;
this.valueMetadataFormat = this.getNonArrayValue(valueMetadata.format);
const numberFormatter = { const numberFormatter = {
parse: function (x) { parse: (x) => Number(x),
return Number(x); format: (x) => x,
}, validate: (x) => true
format: function (x) {
return x;
},
validate: function (x) {
return true;
}
}; };
this.valueMetadata = valueMetadata;
function getNonArrayValue(value) {
//metadata format could have array formats ex. string[]/number[]
const arrayRegex = /\[\]$/g;
if (value && value.match(arrayRegex)) {
return value.replace(arrayRegex, '');
}
return value;
}
let valueMetadataFormat = getNonArrayValue(valueMetadata.format);
// Is there an existing formatter for the format specified? If not, default to number format // Is there an existing formatter for the format specified? If not, default to number format
this.formatter = formatMap.get(valueMetadataFormat) || numberFormatter; this.formatter = formatMap.get(this.valueMetadataFormat) || numberFormatter;
if (this.valueMetadataFormat === 'enum') {
if (valueMetadataFormat === 'enum') {
this.formatter = {}; this.formatter = {};
this.enumerations = valueMetadata.enumerations.reduce( this.enumerations = valueMetadata.enumerations.reduce(
function (vm, e) { function (vm, e) {
@ -66,14 +52,14 @@ define(['lodash', 'printj'], function (_, printj) {
byString: {} byString: {}
} }
); );
this.formatter.format = function (value) { this.formatter.format = (value) => {
if (Object.prototype.hasOwnProperty.call(this.enumerations.byValue, value)) { if (Object.prototype.hasOwnProperty.call(this.enumerations.byValue, value)) {
return this.enumerations.byValue[value]; return this.enumerations.byValue[value];
} }
return value; return value;
}.bind(this); };
this.formatter.parse = function (string) { this.formatter.parse = (string) => {
if (typeof string === 'string') { if (typeof string === 'string') {
if (Object.prototype.hasOwnProperty.call(this.enumerations.byString, string)) { if (Object.prototype.hasOwnProperty.call(this.enumerations.byString, string)) {
return this.enumerations.byString[string]; return this.enumerations.byString[string];
@ -81,19 +67,19 @@ define(['lodash', 'printj'], function (_, printj) {
} }
return Number(string); return Number(string);
}.bind(this); };
} }
// Check for formatString support once instead of per format call. // Check for formatString support once instead of per format call.
if (valueMetadata.formatString) { if (valueMetadata.formatString) {
const baseFormat = this.formatter.format; const baseFormat = this.formatter.format;
const formatString = getNonArrayValue(valueMetadata.formatString); const formatString = this.getNonArrayValue(valueMetadata.formatString);
this.formatter.format = function (value) { this.formatter.format = function (value) {
return printj.sprintf(formatString, baseFormat.call(this, value)); return sprintf(formatString, baseFormat.call(this, value));
}; };
} }
if (valueMetadataFormat === 'string') { if (this.valueMetadataFormat === 'string') {
this.formatter.parse = function (value) { this.formatter.parse = function (value) {
if (value === undefined) { if (value === undefined) {
return ''; return '';
@ -116,7 +102,17 @@ define(['lodash', 'printj'], function (_, printj) {
} }
} }
TelemetryValueFormatter.prototype.parse = function (datum) { getNonArrayValue(value) {
//metadata format could have array formats ex. string[]/number[]
const arrayRegex = /\[\]$/g;
if (value && value.match(arrayRegex)) {
return value.replace(arrayRegex, '');
}
return value;
}
parse(datum) {
const isDatumArray = Array.isArray(datum); const isDatumArray = Array.isArray(datum);
if (_.isObject(datum)) { if (_.isObject(datum)) {
const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source]; const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source];
@ -130,9 +126,9 @@ define(['lodash', 'printj'], function (_, printj) {
} }
return this.formatter.parse(datum); return this.formatter.parse(datum);
}; }
TelemetryValueFormatter.prototype.format = function (datum) { format(datum) {
const isDatumArray = Array.isArray(datum); const isDatumArray = Array.isArray(datum);
if (_.isObject(datum)) { if (_.isObject(datum)) {
const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source]; const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source];
@ -146,7 +142,5 @@ define(['lodash', 'printj'], function (_, printj) {
} }
return this.formatter.format(datum); return this.formatter.format(datum);
}; }
}
return TelemetryValueFormatter;
});

View File

@ -1,4 +1,4 @@
import printj from 'printj'; import { sprintf } from 'printj';
export default class CustomStringFormatter { export default class CustomStringFormatter {
constructor(openmct, valueMetadata, itemFormat) { constructor(openmct, valueMetadata, itemFormat) {
@ -14,7 +14,7 @@ export default class CustomStringFormatter {
} }
if (!this.itemFormat.startsWith('&')) { if (!this.itemFormat.startsWith('&')) {
return printj.sprintf(this.itemFormat, datum[this.valueMetadata.key]); return sprintf(this.itemFormat, datum[this.valueMetadata.key]);
} }
try { try {

View File

@ -26,9 +26,9 @@
</template> </template>
<script> <script>
import * as d3Axis from 'd3-axis'; import { axisTop } from 'd3-axis';
import { scaleLinear, scaleUtc } from 'd3-scale'; import { scaleLinear, scaleUtc } from 'd3-scale';
import * as d3Selection from 'd3-selection'; import { select } from 'd3-selection';
import { TIME_CONTEXT_EVENTS } from '../../api/time/constants'; import { TIME_CONTEXT_EVENTS } from '../../api/time/constants';
import utcMultiTimeFormat from './utcMultiTimeFormat.js'; import utcMultiTimeFormat from './utcMultiTimeFormat.js';
@ -78,9 +78,9 @@ export default {
} }
}, },
mounted() { mounted() {
let vis = d3Selection.select(this.$refs.axisHolder).append('svg:svg'); let vis = select(this.$refs.axisHolder).append('svg:svg');
this.xAxis = d3Axis.axisTop(); this.xAxis = axisTop();
this.dragging = false; this.dragging = false;
// draw x axis with labels. CSS is used to position them. // draw x axis with labels. CSS is used to position them.

View File

@ -26,9 +26,9 @@
</template> </template>
<script> <script>
import * as d3Axis from 'd3-axis'; import { axisTop } from 'd3-axis';
import { scaleLinear, scaleUtc } from 'd3-scale'; import { scaleLinear, scaleUtc } from 'd3-scale';
import * as d3Selection from 'd3-selection'; import { select } from 'd3-selection';
import utcMultiTimeFormat from '@/plugins/timeConductor/utcMultiTimeFormat'; import utcMultiTimeFormat from '@/plugins/timeConductor/utcMultiTimeFormat';
@ -89,7 +89,7 @@ export default {
this.useSVG = true; this.useSVG = true;
} }
this.container = d3Selection.select(this.$refs.axisHolder); this.container = select(this.$refs.axisHolder);
this.svgElement = this.container.append('svg:svg'); this.svgElement = this.container.append('svg:svg');
// draw x axis with labels. CSS is used to position them. // draw x axis with labels. CSS is used to position them.
this.axisElement = this.svgElement this.axisElement = this.svgElement
@ -165,7 +165,7 @@ export default {
this.xScale.range([PADDING, this.offsetWidth - PADDING * 2]); this.xScale.range([PADDING, this.offsetWidth - PADDING * 2]);
}, },
setAxis() { setAxis() {
this.xAxis = d3Axis.axisTop(this.xScale); this.xAxis = axisTop(this.xScale);
this.xAxis.tickFormat(utcMultiTimeFormat); this.xAxis.tickFormat(utcMultiTimeFormat);
if (this.width > 1800) { if (this.width > 1800) {