1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.create = void 0;
|
4 | const chalk = require("chalk");
|
5 | const modules_1 = require("../interfaces/modules");
|
6 | const strings_1 = require("../util/strings");
|
7 | const base_1 = require("./base");
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | const modulesByBaseNameBySource = (mods) => {
|
15 |
|
16 | const modsMap = {};
|
17 |
|
18 | mods.forEach((mod) => {
|
19 | if (!mod.isNodeModules) {
|
20 | return;
|
21 | }
|
22 |
|
23 | if (mod.baseName === null) {
|
24 | throw new Error(`Encountered non-node_modules null baseName: ${JSON.stringify(mod)}`);
|
25 | }
|
26 | const base = modsMap[mod.baseName] = modsMap[mod.baseName] || {};
|
27 |
|
28 |
|
29 | const source = mod.isSynthetic ? modules_1.SYNTHETIC_SOURCE_TOKEN : mod.source;
|
30 | if (source === null) {
|
31 | throw new Error(`Encountered null source in non-synthetic module: ${JSON.stringify(mod)}`);
|
32 | }
|
33 | base[source] = (base[source] || []).concat(mod);
|
34 | });
|
35 |
|
36 | Object.keys(modsMap).forEach((baseName) => {
|
37 | const keys = Object.keys(modsMap[baseName]);
|
38 | if (keys.length === 1 && modsMap[baseName][keys[0]].length === 1) {
|
39 | delete modsMap[baseName];
|
40 | }
|
41 | });
|
42 | return modsMap;
|
43 | };
|
44 |
|
45 | const createEmptySummary = () => ({
|
46 | extraFiles: {
|
47 | num: 0,
|
48 | },
|
49 | extraSources: {
|
50 | bytes: 0,
|
51 | num: 0,
|
52 | },
|
53 | });
|
54 | class Duplicates extends base_1.Action {
|
55 | shouldBail() {
|
56 | return this.getData().then((data) => data.meta.extraFiles.num !== 0);
|
57 | }
|
58 | _getData() {
|
59 | return Promise.resolve()
|
60 | .then(() => {
|
61 | const { assets } = this;
|
62 | const assetNames = Object.keys(assets).sort(strings_1.sort);
|
63 |
|
64 | const assetDups = {};
|
65 | assetNames.forEach((name) => {
|
66 | const modsMap = modulesByBaseNameBySource(assets[name].mods);
|
67 | const files = {};
|
68 | Object.keys(modsMap).forEach((baseName) => {
|
69 | files[baseName] = {
|
70 | meta: createEmptySummary(),
|
71 | sources: Object
|
72 | .keys(modsMap[baseName])
|
73 | .sort(strings_1.sort)
|
74 | .map((source) => ({
|
75 | meta: createEmptySummary(),
|
76 | modules: modsMap[baseName][source].map((mod) => ({
|
77 | baseName: mod.baseName,
|
78 | fileName: mod.identifier,
|
79 | size: {
|
80 | full: mod.size,
|
81 | },
|
82 | })),
|
83 | })),
|
84 | };
|
85 | });
|
86 | assetDups[name] = {
|
87 | files,
|
88 | meta: createEmptySummary(),
|
89 | };
|
90 | });
|
91 |
|
92 |
|
93 | const data = {
|
94 | assets: assetDups,
|
95 | meta: createEmptySummary(),
|
96 | };
|
97 | assetNames.forEach((name) => {
|
98 | const assetDup = data.assets[name];
|
99 | Object.keys(assetDup.files).forEach((baseName) => {
|
100 | const { sources, meta } = assetDup.files[baseName];
|
101 | sources.forEach((sourceGroup) => {
|
102 |
|
103 | sourceGroup.meta = {
|
104 | extraFiles: {
|
105 | num: 1,
|
106 | },
|
107 | extraSources: {
|
108 | bytes: sourceGroup.modules.reduce((bytes, mod) => bytes + mod.size.full, 0),
|
109 | num: sourceGroup.modules.length,
|
110 | },
|
111 | };
|
112 |
|
113 | meta.extraFiles.num += 1;
|
114 | meta.extraSources.bytes += sourceGroup.meta.extraSources.bytes;
|
115 | meta.extraSources.num += sourceGroup.meta.extraSources.num;
|
116 |
|
117 | assetDup.meta.extraFiles.num += 1;
|
118 | assetDup.meta.extraSources.bytes += sourceGroup.meta.extraSources.bytes;
|
119 | assetDup.meta.extraSources.num += sourceGroup.meta.extraSources.num;
|
120 |
|
121 | data.meta.extraFiles.num += 1;
|
122 | data.meta.extraSources.bytes += sourceGroup.meta.extraSources.bytes;
|
123 | data.meta.extraSources.num += sourceGroup.meta.extraSources.num;
|
124 | });
|
125 | });
|
126 | });
|
127 | return data;
|
128 | });
|
129 | }
|
130 | _createTemplate() {
|
131 | return new DuplicatesTemplate({ action: this });
|
132 | }
|
133 | }
|
134 | class DuplicatesTemplate extends base_1.Template {
|
135 | text() {
|
136 | return Promise.resolve()
|
137 | .then(() => this.action.getData())
|
138 | .then(({ meta, assets }) => {
|
139 | const dupAsset = (name) => chalk `{gray ## \`${name}\`}`;
|
140 | const dupFiles = (name) => Object.keys(assets[name].files)
|
141 | .map((baseName) => {
|
142 | const { files } = assets[name];
|
143 | const base = files[baseName];
|
144 | const inlineMeta = (obj) => `Files ${strings_1.numF(obj.extraFiles.num)}, ` +
|
145 | `Sources ${strings_1.numF(obj.extraSources.num)}, ` +
|
146 | `Bytes ${strings_1.numF(obj.extraSources.bytes)}`;
|
147 | const sources = files[baseName].sources
|
148 | .map((sourceGroup, i) => this.trim(`
|
149 | ${i}. (${inlineMeta(sourceGroup.meta)})
|
150 | ${sourceGroup.modules
|
151 | .map((mod) => `(${mod.size.full}) ${chalk.gray(mod.fileName)}`)
|
152 | .join("\n ")}
|
153 | `, 14))
|
154 | .join("\n ");
|
155 | return this.trim(chalk `
|
156 | * {green ${baseName}}
|
157 | * Meta: ${inlineMeta(base.meta)}
|
158 | ${sources}
|
159 | `, 14);
|
160 | })
|
161 | .join("\n");
|
162 | const duplicates = (name) => `${dupAsset(name)}\n${dupFiles(name)}\n`;
|
163 | const report = this.trim(chalk `
|
164 | {cyan inspectpack --action=duplicates}
|
165 | {gray ===============================}
|
166 |
|
167 | {gray ## Summary}
|
168 | * Extra Files (unique): ${strings_1.numF(meta.extraFiles.num)}
|
169 | * Extra Sources (non-unique): ${strings_1.numF(meta.extraSources.num)}
|
170 | * Extra Bytes (non-unique): ${strings_1.numF(meta.extraSources.bytes)}
|
171 |
|
172 | ${Object.keys(assets)
|
173 | .filter((name) => Object.keys(assets[name].files).length)
|
174 | .map(duplicates).join("\n")}
|
175 | `, 10);
|
176 | return report;
|
177 | });
|
178 | }
|
179 | tsv() {
|
180 | return Promise.resolve()
|
181 | .then(() => this.action.getData())
|
182 | .then(({ assets }) => ["Asset\tFull Name\tShort Name\tGroup Index\tSize"]
|
183 | .concat(Object.keys(assets)
|
184 | .filter((name) => Object.keys(assets[name].files).length)
|
185 | .map((name) => Object.keys(assets[name].files)
|
186 | .map((baseName) => assets[name].files[baseName].sources
|
187 | .map((sourceGroup, i) => sourceGroup.modules
|
188 | .map((mod) => [
|
189 | name,
|
190 | mod.fileName,
|
191 | mod.baseName,
|
192 | i,
|
193 | mod.size.full,
|
194 | ].join("\t"))
|
195 | .join("\n"))
|
196 | .join("\n"))
|
197 | .join("\n"))
|
198 | .join("\n"))
|
199 | .join("\n"));
|
200 | }
|
201 | }
|
202 | const create = (opts) => {
|
203 | return new Duplicates(opts);
|
204 | };
|
205 | exports.create = create;
|