Skip to content

Commit

Permalink
misc(treemap): initialize app structure (#11635)
Browse files Browse the repository at this point in the history
  • Loading branch information
connorjclark committed Nov 13, 2020
1 parent 0a4598a commit fea7ffc
Show file tree
Hide file tree
Showing 20 changed files with 21,955 additions and 41 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
run: xvfb-run --auto-servernum yarn test-docs
- name: yarn test-viewer
run: xvfb-run --auto-servernum yarn test-viewer
- name: yarn test-treemap
run: xvfb-run --auto-servernum yarn test-treemap

- run: yarn diff:sample-json
- run: yarn type-check
Expand Down
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ script:
- yarn smoke:cicoverage
- yarn test-clients
- yarn test-viewer
- yarn test-treemap
- yarn test-lantern
- yarn test-bundle
- yarn i18n:checks
Expand Down
39 changes: 39 additions & 0 deletions build/build-treemap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

const fs = require('fs');
const GhPagesApp = require('./gh-pages-app.js');

/**
* Build treemap app, optionally deploying to gh-pages if `--deploy` flag was set.
*/
async function run() {
const app = new GhPagesApp({
name: 'treemap',
appDir: `${__dirname}/../lighthouse-treemap/app`,
html: {path: 'index.html'},
stylesheets: [
{path: 'styles/*'},
],
javascripts: [
fs.readFileSync(require.resolve('webtreemap-cdt'), 'utf8'),
{path: 'src/*'},
],
assets: [
{path: 'debug.json'},
],
});

await app.build();

const argv = process.argv.slice(2);
if (argv.includes('--deploy')) {
await app.deploy();
}
}

run();
3 changes: 2 additions & 1 deletion docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ git push --follow-tags
# Publish to npm.
npm publish

# Publish viewer.
# Publish viewer and treemap.
yarn deploy-viewer
yarn deploy-treemap
```

### Extensions
Expand Down
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module.exports = {
testMatch: [
'**/lighthouse-core/**/*-test.js',
'**/lighthouse-cli/**/*-test.js',
'**/lighthouse-treemap/**/*-test.js',
'**/lighthouse-treemap/**/*-test-pptr.js',
'**/lighthouse-viewer/**/*-test.js',
'**/lighthouse-viewer/**/*-test-pptr.js',
'**/clients/test/**/*-test.js',
Expand Down
2 changes: 1 addition & 1 deletion lighthouse-cli/test/fixtures/static-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class Server {
return;
}

if (filePath.startsWith('/dist/gh-pages/viewer')) {
if (filePath.startsWith('/dist/gh-pages')) {
// Rewrite lighthouse-viewer paths to point to that location.
absoluteFilePath = path.join(__dirname, '/../../../', filePath);
}
Expand Down
37 changes: 11 additions & 26 deletions lighthouse-core/audits/script-treemap-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,19 @@
* Creates treemap data for treemap app.
*/

const Audit = require('./audit.js');
const JsBundles = require('../computed/js-bundles.js');
const UnusedJavaScriptSummary = require('../computed/unused-javascript-summary.js');
const ModuleDuplication = require('../computed/module-duplication.js');

/**
* @typedef {RootNodeContainer[]} TreemapData
*/

/**
* Ex: https://gist.github.com/connorjclark/0ef1099ae994c075e36d65fecb4d26a7
* @typedef RootNodeContainer
* @property {string} name Arbitrary name identifier. Usually a script url.
* @property {Node} node
* @typedef {LH.Treemap.RootNodeContainer[]} TreemapData
*/

/**
* @typedef Node
* @property {string} name Arbitrary name identifier. Usually a path component from a source map.
* @property {number} resourceBytes
* @property {number=} unusedBytes
* @property {string=} duplicatedNormalizedModuleName If present, this module is a duplicate. String is normalized source path. See ModuleDuplication.normalizeSource
* @property {Node[]=} children
* @typedef {Omit<LH.Treemap.Node, 'name'|'children'>} SourceData
*/

/**
* @typedef {Omit<Node, 'name'|'children'>} SourceData
*/
const Audit = require('./audit.js');
const JsBundles = require('../computed/js-bundles.js');
const UnusedJavaScriptSummary = require('../computed/unused-javascript-summary.js');
const ModuleDuplication = require('../computed/module-duplication.js');

class ScriptTreemapDataAudit extends Audit {
/**
Expand All @@ -61,12 +46,12 @@ class ScriptTreemapDataAudit extends Audit {
* same data as the sum of all descendant leaf nodes.
* @param {string} sourceRoot
* @param {Record<string, SourceData>} sourcesData
* @return {Node}
* @return {LH.Treemap.Node}
*/
static prepareTreemapNodes(sourceRoot, sourcesData) {
/**
* @param {string} name
* @return {Node}
* @return {LH.Treemap.Node}
*/
function newNode(name) {
return {
Expand Down Expand Up @@ -124,7 +109,7 @@ class ScriptTreemapDataAudit extends Audit {

/**
* Collapse nodes that have only one child.
* @param {Node} node
* @param {LH.Treemap.Node} node
*/
function collapseAll(node) {
while (node.children && node.children.length === 1) {
Expand All @@ -151,7 +136,7 @@ class ScriptTreemapDataAudit extends Audit {
* @return {Promise<TreemapData>}
*/
static async makeRootNodes(artifacts, context) {
/** @type {RootNodeContainer[]} */
/** @type {LH.Treemap.RootNodeContainer[]} */
const rootNodeContainers = [];

let inlineScriptLength = 0;
Expand Down Expand Up @@ -199,7 +184,7 @@ class ScriptTreemapDataAudit extends Audit {
const unusedJavascriptSummary = await UnusedJavaScriptSummary.request(
{url: scriptElement.src, scriptCoverages, bundle}, context);

/** @type {Node} */
/** @type {LH.Treemap.Node} */
let node;
if (unusedJavascriptSummary.sourcesWastedBytes && !('errorMessage' in bundle.sizes)) {
// Create nodes for each module in a bundle.
Expand Down
12 changes: 12 additions & 0 deletions lighthouse-treemap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Lighthouse Treemap Viewer

## Development

```sh
yarn serve-treemap

# in separate terminal, start build watch
# dependency: `brew install entr`
find lighthouse-treemap | entr -s 'DEBUG=1 yarn build-treemap'
open http://localhost:8000/treemap/?debug
```
21,497 changes: 21,497 additions & 0 deletions lighthouse-treemap/app/debug.json

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions lighthouse-treemap/app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!--
Copyright 2020 The Lighthouse Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-->

<!doctype html>
<html>

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>Lighthouse Treemap</title>
<link rel="icon"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAEhklEQVR4AWJxL/BhIAesev1U5tcflpncgNrKIsqNIwzC9feMpDUzs70kOczMzMzJJcxwCTMzncPMnOwtzBwzMzPb0vRfeZPp0VhPS5I39V5fdiXV1/VD+9QC7OVn9BsyH1XIoEI1PfmJvLFowVV564+34DFUHudbmfDh4kVXh//7XwE+WjS/YfXZe3yr4j2rqj1AIhSB7hZ8ZtPZu/zw8cK523U4wE1/rvPfWrz4zs0m9ZdC9yUJAlASdBAgocRegfF/f3/h/PuaFsxMdwjAR0vm1+06eMMfIrhLqTWqdH4EumU2SPfMhigJAlRQbZrgrRsl9U+Y2DYDFCz3ILC9kiAiqSrMwbWT0nceEnR+9Kggc2zjOJCASDENkg0a5HfZZgDP81CM3CrQs2Z1+o7DJ6ePr8sK0AOCHv5Jjdt3evyYSaZ351VIStIxPRAUtrBYbxC6w+BZ0ivVSBKkIhJhemSyZpfB00EiPO2VjzYkxhcqXQqCWCShGplvi3y0QxqbuBurMjyJeWnkHZuAEgIQGsUBqwrfjZ+IlBgKyRJzVVYF8O6qFWdh86YzQzMrZigYmxAyfvHgLZQ/LC1CbeniW2Hkqr/PH16SgvGuf2/uzNMBwJA/njxizGPtSyAf7EziJCMGRDRdhoAC4PL1A/SrKQMAAQkEfpJAcRQdrBJ7gNwjSpJsdwK+CANBkqa1LgQB4IicV9nYUct7gaxuDJUErQIiEAiMxLVOFlKzIktPpT0ggpdpC/8YAHnxbgkUY4tAAFSR7AAXNyAAWHJrA/kHGjzg5nleuwFO7Nd/IoDw4Pm58+4jNLmYG0wRA5bErc2Mr3Y+dXTDW1VvwqbJkzMCHQ4S1GTCBOIgUHJrGdEwqzR+jAp/o2qAZelUDoQnruEEdDclJI6576AlNVfc+22XN/+Y1vnJD0Yind6UpEEvn/Hqq15EYjCW7jZCJEpnNvDgkyelDjs106kuux2AAXCSobULOWP8mLhYlpoDMK4qAFXJGk+grtH8YXVz5KJblqaG1+VUdTc0I290bmUQAriGITRbdQnom0aoFj8kx1+wMD2ifncAXUQE4SkDqN1hE0jEophs1SUwZAOhUAiMCLwRtamtTZtbbmZErSAUHbSysaoEmnrsakiMiUAURi283gN6wans9oX8rOCrj7/JP35DFD+iQ7Au/K2KE1jzx6ujjUnXFH9KjEq6ZlhsTBICrNLJf47Pv/pkHzvup1w4dmUbEei0+bcXRqJuh5kVARQ8byyYxOwNGr7A87xh1tp8sGT+uMInrwi++Xj7TQz2d27NvwEkrOflAFQGIDA5khASBCGdO2/Z/MnLPwYfv5TFhjW7QhVKAB6afwe2LpFlFsCnlQEosgQgDsdOG1/LKeNqJS4JCSPJ/i+TakwEARor7gER1Iva5JmPOJK0RUqmoPnnlzFCtmIAhAAQEIQRgDaiYPIauNXcnDlRIrWNFY3hm7PG9YRqr7IV7HrCgAC17befjEvRq2nGhAHtBqDpOuI/I1diUUAMYIxEdyejBJqLnNoszGZtfiX/CztGv2mq+sdaAAAAAElFTkSuQmCC">
<meta name="theme-color" content="#304ffe">
<link rel="stylesheet" href="styles/bundled.css">
</head>

<body>
<main>
<!-- Inject LHR here. -->
</main>

<script src="src/bundled.js"></script>

<!-- Google Analytics -->
<script>
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date(); a = s.createElement(o),
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');

ga('create', 'UA-85519014-2', 'auto');
ga('send', 'pageview');
</script>
</body>

</html>
78 changes: 78 additions & 0 deletions lighthouse-treemap/app/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

/* eslint-env browser */

/* globals webtreemap TreemapUtil */

/**
* Allows for saving the document and loading with data intact.
* @param {LH.Treemap.Options} options
*/
function injectOptions(options) {
if (window.__treemapOptions) return;

const scriptEl = document.createElement('script');
scriptEl.textContent = `
window.__treemapOptions = ${JSON.stringify(options)};
`;
document.head.append(scriptEl);
}

/**
* @param {LH.Treemap.Options} options
*/
function init(options) {
// ==== temporary
TreemapUtil.find('main').textContent = JSON.stringify(options.lhr);
// eslint-disable-next-line no-console
console.log({webtreemap});
// ==== temporary

injectOptions(options);

// eslint-disable-next-line no-console
console.log('window.__treemapOptions', window.__treemapOptions);
}

/**
* @param {string} message
*/
function showError(message) {
document.body.textContent = message;
}

async function main() {
if (window.__treemapOptions) {
// Prefer the hardcoded options from a saved HTML file above all.
init(window.__treemapOptions);
} else if (new URLSearchParams(window.location.search).has('debug')) {
const response = await fetch('debug.json');
init(await response.json());
} else {
window.addEventListener('message', e => {
if (e.source !== self.opener) return;

/** @type {LH.Treemap.Options} */
const options = e.data;
const {lhr} = options;
if (!lhr) return showError('Error: Invalid options');

const documentUrl = lhr.requestedUrl;
if (!documentUrl) return showError('Error: Invalid options');

init(options);
});
}

// If the page was opened as a popup, tell the opening window we're ready.
if (self.opener && !self.opener.closed) {
self.opener.postMessage({opened: true}, '*');
}
}

document.addEventListener('DOMContentLoaded', main);
72 changes: 72 additions & 0 deletions lighthouse-treemap/app/src/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

/* eslint-env browser */

/** @typedef {HTMLElementTagNameMap & {[id: string]: HTMLElement}} HTMLElementByTagName */

class TreemapUtil {
/**
* @template {string} T
* @param {T} name
* @param {string=} className
* @param {Object<string, (string|undefined)>=} attrs Attribute key/val pairs.
* Note: if an attribute key has an undefined value, this method does not
* set the attribute on the node.
* @return {HTMLElementByTagName[T]}
*/
static createElement(name, className, attrs = {}) {
const element = document.createElement(name);
if (className) {
element.className = className;
}
Object.keys(attrs).forEach(key => {
const value = attrs[key];
if (typeof value !== 'undefined') {
element.setAttribute(key, value);
}
});
return element;
}

/**
* @template {string} T
* @param {Element} parentElem
* @param {T} elementName
* @param {string=} className
* @param {Object<string, (string|undefined)>=} attrs Attribute key/val pairs.
* Note: if an attribute key has an undefined value, this method does not
* set the attribute on the node.
* @return {HTMLElementByTagName[T]}
*/
static createChildOf(parentElem, elementName, className, attrs) {
const element = this.createElement(elementName, className, attrs);
parentElem.appendChild(element);
return element;
}

/**
* Guaranteed context.querySelector. Always returns an element or throws if
* nothing matches query.
* @param {string} query
* @param {ParentNode=} context
* @return {HTMLElement}
*/
static find(query, context = document) {
/** @type {?HTMLElement} */
const result = context.querySelector(query);
if (result === null) {
throw new Error(`query ${query} not found`);
}
return result;
}
}

// node export for testing.
if (typeof module !== 'undefined' && module.exports) {
module.exports = TreemapUtil;
}
10 changes: 10 additions & 0 deletions lighthouse-treemap/app/styles/treemap.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/

body {
margin: 0;
overflow-y: hidden;
}
Loading

0 comments on commit fea7ffc

Please sign in to comment.