webpack-bundle-analyzer 4.4.2 → 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +3 -2
- package/lib/BundleAnalyzerPlugin.js +3 -1
- package/lib/bin/analyzer.js +6 -5
- package/lib/utils.js +8 -0
- package/lib/viewer.js +7 -2
- package/package.json +30 -33
- package/public/viewer.js +4 -3
- package/public/viewer.js.LICENSE.txt +6 -1
- package/public/viewer.js.map +1 -1
- package/src/BundleAnalyzerPlugin.js +0 -154
- package/src/Logger.js +0 -51
- package/src/analyzer.js +0 -213
- package/src/bin/analyzer.js +0 -168
- package/src/index.js +0 -6
- package/src/parseUtils.js +0 -361
- package/src/statsUtils.js +0 -82
- package/src/template.js +0 -65
- package/src/tree/BaseFolder.js +0 -118
- package/src/tree/ConcatenatedModule.js +0 -72
- package/src/tree/ContentFolder.js +0 -35
- package/src/tree/ContentModule.js +0 -33
- package/src/tree/Folder.js +0 -64
- package/src/tree/Module.js +0 -63
- package/src/tree/Node.js +0 -24
- package/src/tree/utils.js +0 -21
- package/src/utils.js +0 -65
- package/src/viewer.js +0 -189
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import BaseFolder from './BaseFolder';
|
|
2
|
-
|
|
3
|
-
export default class ContentFolder extends BaseFolder {
|
|
4
|
-
|
|
5
|
-
constructor(name, ownerModule, parent) {
|
|
6
|
-
super(name, parent);
|
|
7
|
-
this.ownerModule = ownerModule;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
get parsedSize() {
|
|
11
|
-
return this.getSize('parsedSize');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
get gzipSize() {
|
|
15
|
-
return this.getSize('gzipSize');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
getSize(sizeType) {
|
|
19
|
-
const ownerModuleSize = this.ownerModule[sizeType];
|
|
20
|
-
|
|
21
|
-
if (ownerModuleSize !== undefined) {
|
|
22
|
-
return Math.floor((this.size / this.ownerModule.size) * ownerModuleSize);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
toChartData() {
|
|
27
|
-
return {
|
|
28
|
-
...super.toChartData(),
|
|
29
|
-
parsedSize: this.parsedSize,
|
|
30
|
-
gzipSize: this.gzipSize,
|
|
31
|
-
inaccurateSizes: true
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import Module from './Module';
|
|
2
|
-
|
|
3
|
-
export default class ContentModule extends Module {
|
|
4
|
-
|
|
5
|
-
constructor(name, data, ownerModule, parent) {
|
|
6
|
-
super(name, data, parent);
|
|
7
|
-
this.ownerModule = ownerModule;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
get parsedSize() {
|
|
11
|
-
return this.getSize('parsedSize');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
get gzipSize() {
|
|
15
|
-
return this.getSize('gzipSize');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
getSize(sizeType) {
|
|
19
|
-
const ownerModuleSize = this.ownerModule[sizeType];
|
|
20
|
-
|
|
21
|
-
if (ownerModuleSize !== undefined) {
|
|
22
|
-
return Math.floor((this.size / this.ownerModule.size) * ownerModuleSize);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
toChartData() {
|
|
27
|
-
return {
|
|
28
|
-
...super.toChartData(),
|
|
29
|
-
inaccurateSizes: true
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
};
|
package/src/tree/Folder.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
import gzipSize from 'gzip-size';
|
|
3
|
-
|
|
4
|
-
import Module from './Module';
|
|
5
|
-
import BaseFolder from './BaseFolder';
|
|
6
|
-
import ConcatenatedModule from './ConcatenatedModule';
|
|
7
|
-
import {getModulePathParts} from './utils';
|
|
8
|
-
|
|
9
|
-
export default class Folder extends BaseFolder {
|
|
10
|
-
|
|
11
|
-
get parsedSize() {
|
|
12
|
-
return this.src ? this.src.length : 0;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
get gzipSize() {
|
|
16
|
-
if (!_.has(this, '_gzipSize')) {
|
|
17
|
-
this._gzipSize = this.src ? gzipSize.sync(this.src) : 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return this._gzipSize;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
addModule(moduleData) {
|
|
24
|
-
const pathParts = getModulePathParts(moduleData);
|
|
25
|
-
|
|
26
|
-
if (!pathParts) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const [folders, fileName] = [pathParts.slice(0, -1), _.last(pathParts)];
|
|
31
|
-
let currentFolder = this;
|
|
32
|
-
|
|
33
|
-
folders.forEach(folderName => {
|
|
34
|
-
let childNode = currentFolder.getChild(folderName);
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
// Folder is not created yet
|
|
38
|
-
!childNode ||
|
|
39
|
-
// In some situations (invalid usage of dynamic `require()`) webpack generates a module with empty require
|
|
40
|
-
// context, but it's moduleId points to a directory in filesystem.
|
|
41
|
-
// In this case we replace this `File` node with `Folder`.
|
|
42
|
-
// See `test/stats/with-invalid-dynamic-require.json` as an example.
|
|
43
|
-
!(childNode instanceof Folder)
|
|
44
|
-
) {
|
|
45
|
-
childNode = currentFolder.addChildFolder(new Folder(folderName));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
currentFolder = childNode;
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const ModuleConstructor = moduleData.modules ? ConcatenatedModule : Module;
|
|
52
|
-
const module = new ModuleConstructor(fileName, moduleData, this);
|
|
53
|
-
currentFolder.addChildModule(module);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
toChartData() {
|
|
57
|
-
return {
|
|
58
|
-
...super.toChartData(),
|
|
59
|
-
parsedSize: this.parsedSize,
|
|
60
|
-
gzipSize: this.gzipSize
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
};
|
package/src/tree/Module.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
import gzipSize from 'gzip-size';
|
|
3
|
-
|
|
4
|
-
import Node from './Node';
|
|
5
|
-
|
|
6
|
-
export default class Module extends Node {
|
|
7
|
-
|
|
8
|
-
constructor(name, data, parent) {
|
|
9
|
-
super(name, parent);
|
|
10
|
-
this.data = data;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
get src() {
|
|
14
|
-
return this.data.parsedSrc;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
set src(value) {
|
|
18
|
-
this.data.parsedSrc = value;
|
|
19
|
-
delete this._gzipSize;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get size() {
|
|
23
|
-
return this.data.size;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
set size(value) {
|
|
27
|
-
this.data.size = value;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
get parsedSize() {
|
|
31
|
-
return this.src ? this.src.length : undefined;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get gzipSize() {
|
|
35
|
-
if (!_.has(this, '_gzipSize')) {
|
|
36
|
-
this._gzipSize = this.src ? gzipSize.sync(this.src) : undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return this._gzipSize;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
mergeData(data) {
|
|
43
|
-
if (data.size) {
|
|
44
|
-
this.size += data.size;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (data.parsedSrc) {
|
|
48
|
-
this.src = (this.src || '') + data.parsedSrc;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
toChartData() {
|
|
53
|
-
return {
|
|
54
|
-
id: this.data.id,
|
|
55
|
-
label: this.name,
|
|
56
|
-
path: this.path,
|
|
57
|
-
statSize: this.size,
|
|
58
|
-
parsedSize: this.parsedSize,
|
|
59
|
-
gzipSize: this.gzipSize
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
};
|
package/src/tree/Node.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export default class Node {
|
|
2
|
-
|
|
3
|
-
constructor(name, parent) {
|
|
4
|
-
this.name = name;
|
|
5
|
-
this.parent = parent;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
get path() {
|
|
9
|
-
const path = [];
|
|
10
|
-
let node = this;
|
|
11
|
-
|
|
12
|
-
while (node) {
|
|
13
|
-
path.push(node.name);
|
|
14
|
-
node = node.parent;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return path.reverse().join('/');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
get isRoot() {
|
|
21
|
-
return !this.parent;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
};
|
package/src/tree/utils.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
|
|
3
|
-
const MULTI_MODULE_REGEXP = /^multi /u;
|
|
4
|
-
|
|
5
|
-
export function getModulePathParts(moduleData) {
|
|
6
|
-
if (MULTI_MODULE_REGEXP.test(moduleData.identifier)) {
|
|
7
|
-
return [moduleData.identifier];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const parsedPath = _
|
|
11
|
-
// Removing loaders from module path: they're joined by `!` and the last part is a raw module path
|
|
12
|
-
.last(moduleData.name.split('!'))
|
|
13
|
-
// Splitting module path into parts
|
|
14
|
-
.split('/')
|
|
15
|
-
// Removing first `.`
|
|
16
|
-
.slice(1)
|
|
17
|
-
// Replacing `~` with `node_modules`
|
|
18
|
-
.map(part => (part === '~' ? 'node_modules' : part));
|
|
19
|
-
|
|
20
|
-
return parsedPath.length ? parsedPath : null;
|
|
21
|
-
}
|
package/src/utils.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const {inspect, types} = require('util');
|
|
2
|
-
const _ = require('lodash');
|
|
3
|
-
const opener = require('opener');
|
|
4
|
-
|
|
5
|
-
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
6
|
-
|
|
7
|
-
exports.createAssetsFilter = createAssetsFilter;
|
|
8
|
-
|
|
9
|
-
function createAssetsFilter(excludePatterns) {
|
|
10
|
-
const excludeFunctions = _(excludePatterns)
|
|
11
|
-
.castArray()
|
|
12
|
-
.compact()
|
|
13
|
-
.map(pattern => {
|
|
14
|
-
if (typeof pattern === 'string') {
|
|
15
|
-
pattern = new RegExp(pattern, 'u');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (types.isRegExp(pattern)) {
|
|
19
|
-
return (asset) => pattern.test(asset);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (typeof pattern !== 'function') {
|
|
23
|
-
throw new TypeError(
|
|
24
|
-
`Pattern should be either string, RegExp or a function, but "${inspect(pattern, {depth: 0})}" got.`
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return pattern;
|
|
29
|
-
})
|
|
30
|
-
.value();
|
|
31
|
-
|
|
32
|
-
if (excludeFunctions.length) {
|
|
33
|
-
return (asset) => excludeFunctions.every(fn => fn(asset) !== true);
|
|
34
|
-
} else {
|
|
35
|
-
return () => true;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @desc get string of current time
|
|
41
|
-
* format: dd/MMM HH:mm
|
|
42
|
-
* */
|
|
43
|
-
exports.defaultTitle = function () {
|
|
44
|
-
const time = new Date();
|
|
45
|
-
const year = time.getFullYear();
|
|
46
|
-
const month = MONTHS[time.getMonth()];
|
|
47
|
-
const day = time.getDate();
|
|
48
|
-
const hour = `0${time.getHours()}`.slice(-2);
|
|
49
|
-
const minute = `0${time.getMinutes()}`.slice(-2);
|
|
50
|
-
|
|
51
|
-
const currentTime = `${day} ${month} ${year} at ${hour}:${minute}`;
|
|
52
|
-
|
|
53
|
-
return `${process.env.npm_package_name || 'Webpack Bundle Analyzer'} [${currentTime}]`;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Calls opener on a URI, but silently try / catches it.
|
|
58
|
-
*/
|
|
59
|
-
exports.open = function (uri, logger) {
|
|
60
|
-
try {
|
|
61
|
-
opener(uri);
|
|
62
|
-
} catch (err) {
|
|
63
|
-
logger.debug(`Opener failed to open "${uri}":\n${err}`);
|
|
64
|
-
}
|
|
65
|
-
};
|
package/src/viewer.js
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const http = require('http');
|
|
4
|
-
|
|
5
|
-
const WebSocket = require('ws');
|
|
6
|
-
const sirv = require('sirv');
|
|
7
|
-
const _ = require('lodash');
|
|
8
|
-
const {bold} = require('chalk');
|
|
9
|
-
|
|
10
|
-
const Logger = require('./Logger');
|
|
11
|
-
const analyzer = require('./analyzer');
|
|
12
|
-
const {open} = require('./utils');
|
|
13
|
-
const {renderViewer} = require('./template');
|
|
14
|
-
|
|
15
|
-
const projectRoot = path.resolve(__dirname, '..');
|
|
16
|
-
|
|
17
|
-
function resolveTitle(reportTitle) {
|
|
18
|
-
if (typeof reportTitle === 'function') {
|
|
19
|
-
return reportTitle();
|
|
20
|
-
} else {
|
|
21
|
-
return reportTitle;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
module.exports = {
|
|
26
|
-
startServer,
|
|
27
|
-
generateReport,
|
|
28
|
-
generateJSONReport,
|
|
29
|
-
// deprecated
|
|
30
|
-
start: startServer
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
async function startServer(bundleStats, opts) {
|
|
34
|
-
const {
|
|
35
|
-
port = 8888,
|
|
36
|
-
host = '127.0.0.1',
|
|
37
|
-
openBrowser = true,
|
|
38
|
-
bundleDir = null,
|
|
39
|
-
logger = new Logger(),
|
|
40
|
-
defaultSizes = 'parsed',
|
|
41
|
-
excludeAssets = null,
|
|
42
|
-
reportTitle
|
|
43
|
-
} = opts || {};
|
|
44
|
-
|
|
45
|
-
const analyzerOpts = {logger, excludeAssets};
|
|
46
|
-
|
|
47
|
-
let chartData = getChartData(analyzerOpts, bundleStats, bundleDir);
|
|
48
|
-
|
|
49
|
-
if (!chartData) return;
|
|
50
|
-
|
|
51
|
-
const sirvMiddleware = sirv(`${projectRoot}/public`, {
|
|
52
|
-
// disables caching and traverse the file system on every request
|
|
53
|
-
dev: true
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const server = http.createServer((req, res) => {
|
|
57
|
-
if (req.method === 'GET' && req.url === '/') {
|
|
58
|
-
const html = renderViewer({
|
|
59
|
-
mode: 'server',
|
|
60
|
-
title: resolveTitle(reportTitle),
|
|
61
|
-
chartData,
|
|
62
|
-
defaultSizes,
|
|
63
|
-
enableWebSocket: true
|
|
64
|
-
});
|
|
65
|
-
res.writeHead(200, {'Content-Type': 'text/html'});
|
|
66
|
-
res.end(html);
|
|
67
|
-
} else {
|
|
68
|
-
sirvMiddleware(req, res);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
await new Promise(resolve => {
|
|
73
|
-
server.listen(port, host, () => {
|
|
74
|
-
resolve();
|
|
75
|
-
|
|
76
|
-
const url = `http://${host}:${server.address().port}`;
|
|
77
|
-
|
|
78
|
-
logger.info(
|
|
79
|
-
`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` +
|
|
80
|
-
`Use ${bold('Ctrl+C')} to close it`
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
if (openBrowser) {
|
|
84
|
-
open(url, logger);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const wss = new WebSocket.Server({server});
|
|
90
|
-
|
|
91
|
-
wss.on('connection', ws => {
|
|
92
|
-
ws.on('error', err => {
|
|
93
|
-
// Ignore network errors like `ECONNRESET`, `EPIPE`, etc.
|
|
94
|
-
if (err.errno) return;
|
|
95
|
-
|
|
96
|
-
logger.info(err.message);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
ws: wss,
|
|
102
|
-
http: server,
|
|
103
|
-
updateChartData
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
function updateChartData(bundleStats) {
|
|
107
|
-
const newChartData = getChartData(analyzerOpts, bundleStats, bundleDir);
|
|
108
|
-
|
|
109
|
-
if (!newChartData) return;
|
|
110
|
-
|
|
111
|
-
chartData = newChartData;
|
|
112
|
-
|
|
113
|
-
wss.clients.forEach(client => {
|
|
114
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
115
|
-
client.send(JSON.stringify({
|
|
116
|
-
event: 'chartDataUpdated',
|
|
117
|
-
data: newChartData
|
|
118
|
-
}));
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async function generateReport(bundleStats, opts) {
|
|
125
|
-
const {
|
|
126
|
-
openBrowser = true,
|
|
127
|
-
reportFilename,
|
|
128
|
-
reportTitle,
|
|
129
|
-
bundleDir = null,
|
|
130
|
-
logger = new Logger(),
|
|
131
|
-
defaultSizes = 'parsed',
|
|
132
|
-
excludeAssets = null
|
|
133
|
-
} = opts || {};
|
|
134
|
-
|
|
135
|
-
const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir);
|
|
136
|
-
|
|
137
|
-
if (!chartData) return;
|
|
138
|
-
|
|
139
|
-
const reportHtml = renderViewer({
|
|
140
|
-
mode: 'static',
|
|
141
|
-
title: resolveTitle(reportTitle),
|
|
142
|
-
chartData,
|
|
143
|
-
defaultSizes,
|
|
144
|
-
enableWebSocket: false
|
|
145
|
-
});
|
|
146
|
-
const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename);
|
|
147
|
-
|
|
148
|
-
fs.mkdirSync(path.dirname(reportFilepath), {recursive: true});
|
|
149
|
-
fs.writeFileSync(reportFilepath, reportHtml);
|
|
150
|
-
|
|
151
|
-
logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`);
|
|
152
|
-
|
|
153
|
-
if (openBrowser) {
|
|
154
|
-
open(`file://${reportFilepath}`, logger);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async function generateJSONReport(bundleStats, opts) {
|
|
159
|
-
const {reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null} = opts || {};
|
|
160
|
-
|
|
161
|
-
const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir);
|
|
162
|
-
|
|
163
|
-
if (!chartData) return;
|
|
164
|
-
|
|
165
|
-
await fs.promises.mkdir(path.dirname(reportFilename), {recursive: true});
|
|
166
|
-
await fs.promises.writeFile(reportFilename, JSON.stringify(chartData));
|
|
167
|
-
|
|
168
|
-
logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function getChartData(analyzerOpts, ...args) {
|
|
172
|
-
let chartData;
|
|
173
|
-
const {logger} = analyzerOpts;
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
chartData = analyzer.getViewerData(...args, analyzerOpts);
|
|
177
|
-
} catch (err) {
|
|
178
|
-
logger.error(`Could't analyze webpack bundle:\n${err}`);
|
|
179
|
-
logger.debug(err.stack);
|
|
180
|
-
chartData = null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (_.isPlainObject(chartData) && _.isEmpty(chartData)) {
|
|
184
|
-
logger.error("Could't find any javascript bundles in provided stats file");
|
|
185
|
-
chartData = null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return chartData;
|
|
189
|
-
}
|