Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
History
=======

## UNRELEASED

- Add `fullPath` processed module field internally and to output reports.

## 4.1.2

- BUG: Use `name` field to better process `identifier` to remove things like
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ inspectpack

An inspection tool for Webpack frontend JavaScript bundles.

`inspectpack` provides insight into your webpack-built JS bundles and detailed analysis of opportunites to reduce module sizes, unneeded duplicates, etc. It can be used as a webpack **plugin** during your compliations or as an **offline CLI tool** to report on your previous builds.
`inspectpack` provides insight into your webpack-built JS bundles and detailed analysis of opportunites to reduce module sizes, unneeded duplicates, etc. It can be used as a webpack **plugin** during your compilations or as an **offline CLI tool** to report on your previous builds.

It is also the engine for the handy [`webpack-dashboard`](https://github.com/FormidableLabs/webpack-dashboard) plugin.

Expand Down
59 changes: 52 additions & 7 deletions src/lib/actions/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export const nodeModulesParts = (name: string) => toPosixPath(name).split(NM_RE)
// True if name is part of a `node_modules` path.
export const _isNodeModules = (name: string): boolean => nodeModulesParts(name).length > 1;

// Remove all relative higher-up paths (`./` or `../../../`).
const _removePrepath = (val: string) => val.replace(/^(\.+(\/|\\)+)+/g, "");

// Attempt to "unwind" webpack paths in `identifier` and `name` to remove
// prefixes and produce a normal, usable filepath.
//
Expand Down Expand Up @@ -75,15 +78,11 @@ export const _normalizeWebpackPath = (identifier: string, name?: string): string
// - v3: "/PATH/TO/ROOT/node_modules/pkg/index.js"
// - v4: "./node_modules/pkg/index.js"
if (name) {
name = name
// Expand `node_modules`, remove prefix `./`, `../`, etc.
name = _removePrepath(name)
.replace("/~/", "/node_modules/")
.replace("\\~\\", "\\node_modules\\");

if (name.startsWith("./") || name.startsWith(".\\")) {
// Remove dot-slash relative part.
name = name.slice(2);
}

// Now, truncate suffix of the candidate if name has less.
const nameLastIdx = candidate.lastIndexOf(name);
if (nameLastIdx > -1 && candidate.length !== nameLastIdx + name.length) {
Expand All @@ -101,7 +100,7 @@ export const _normalizeWebpackPath = (identifier: string, name?: string): string
// Normalizations:
// - Remove starting path if `./`
// - Switch Windows paths to Mac/Unix style.
export const _getBaseName = (name: string): string | null => {
export const _getBaseName = (name: string): string => {
// Slice to just after last occurrence of node_modules.
const parts = nodeModulesParts(name);
const lastName = parts[parts.length - 1];
Expand All @@ -128,6 +127,48 @@ export const _getBaseName = (name: string): string | null => {
return toPosixPath(candidate);
};

// Convert an identifier into a full path.
//
// Uses the (normalized) `name` field to assess that the (normalized) identifier
// is indeed a real file on disk.
export const _getFullPath = (identifier: string, name: string, TODO_REMOVE_OBJ: any): string => {
const posixIdentifier = toPosixPath(identifier);

// Start some normalization.
let posixName = _removePrepath(toPosixPath(name));
if (posixName.startsWith("./")) {
// Remove dot-slash relative part.
posixName = posixName.slice(2);
}

// If the name is not the end of the identifier, it probably is webpack v1-2
// with `~` instead of `node_modules`
const idxOfName = posixIdentifier.indexOf(posixName);
if (
// Direct match.
idxOfName === 0 ||
// Suffix match.
idxOfName === posixIdentifier.length - posixName.length
) {
return normalize(posixIdentifier);
}

if (identifier.lastIndexOf(name) !== identifier.length - name.length) {
console.log("TODO MISMATCH", JSON.stringify({
posixIdentifier,
posixName,
normalize: normalize(posixIdentifier),
TODO_REMOVE_OBJ: {
issuer: TODO_REMOVE_OBJ.issuer,
source: TODO_REMOVE_OBJ.source || "NO_SOURCE",
}
}, null, 2));
}

// TODO: HERE -- this stuff isn't even remotely done. Above or below :).
return "TODO";
};

export abstract class Action {
public stats: IWebpackStats;
private _data?: object;
Expand Down Expand Up @@ -218,9 +259,13 @@ export abstract class Action {
const isNodeModules = _isNodeModules(normalizedId);
const baseName = isNodeModules ? _getBaseName(normalizedId) : null;

// TODO(FULL_PATH): Add into data
const fullPath = _getFullPath(normalizedId, normalizedName, mod);

return list.concat([{
baseName,
chunks,
fullPath,
identifier,
isNodeModules,
isSynthetic,
Expand Down
1 change: 1 addition & 0 deletions src/lib/actions/duplicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class Duplicates extends Action {
modules: modsMap[baseName][source].map((mod) => ({
baseName: mod.baseName,
fileName: mod.identifier,
fullPath: mod.fullPath,
size: {
full: mod.size,
},
Expand Down
6 changes: 4 additions & 2 deletions src/lib/actions/sizes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class Sizes extends Action {
files: assets[name].mods.map((mod) => ({
baseName: mod.baseName,
fileName: mod.identifier,
fullPath: mod.fullPath,
size: {
full: mod.size,
},
Expand Down Expand Up @@ -73,7 +74,7 @@ class SizesTemplate extends Template {
.then(({ meta, assets }) => {
const files = (mods: IActionModule[]) => mods
.map((obj) => this.trim(chalk`
* {gray ${obj.fileName}}
* {gray ${obj.fullPath || obj.fileName}}
* Size: ${numF(obj.size.full)}
`, 12))
.join("\n");
Expand Down Expand Up @@ -103,12 +104,13 @@ class SizesTemplate extends Template {
public tsv(): Promise<string> {
return Promise.resolve()
.then(() => this.action.getData() as Promise<ISizesData>)
.then(({ assets }) => ["Asset\tFull Name\tShort Name\tSize"]
.then(({ assets }) => ["Asset\tFull Path\tFile Name\tShort Name\tSize"]
.concat(Object.keys(assets)
// Get items
.map((name) => assets[name].files
.map((obj) => [
name,
obj.fullPath,
obj.fileName,
obj.baseName === null ? "(source)" : obj.baseName,
obj.size.full,
Expand Down
1 change: 1 addition & 0 deletions src/lib/actions/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ const getAssetData = (
const modules = (modsToFilePath[filePath] || []).map((mod) => ({
baseName: mod.baseName,
fileName: mod.identifier,
fullPath: mod.fullPath,
size: {
full: mod.size,
},
Expand Down
8 changes: 7 additions & 1 deletion src/lib/interfaces/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ export interface IModule extends IWebpackStatsModuleBase {
// Is `null` if not a `node_modules` package module.
baseName: string | null;

// Inferred path to a real file on disk (app or `node_modules`).
// Is `null` if no real, single base file or in a loader/generated code
// context a "better" contender exists as "the original".
fullPath: string | null;

// Is a vendor module / is part of a `node_modules` path.
isNodeModules: boolean;

// Is a vendor module / is part of a `node_modules` path.
// Is a "made up" module without actual source.
isSynthetic: boolean;

// We **change** `source` to allow `null` for synthetic modules.
Expand All @@ -23,6 +28,7 @@ export const SYNTHETIC_SOURCE_TOKEN = "synthetic";
export interface IActionModule {
baseName: string | null;
fileName: string;
fullPath: string | null;
size: {
full: number,
};
Expand Down