Skip to content

Commit

Permalink
When available, use resolve data already present in most modules (#114)
Browse files Browse the repository at this point in the history
* When available, use resolve data already present in most modules

* remove one check

* Use throwIfNoEntry in backward compatible way
  • Loading branch information
markjm committed Jan 22, 2022
1 parent 81ee591 commit 55dd3f8
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 64 deletions.
2 changes: 2 additions & 0 deletions src/FileHandler.ts
Expand Up @@ -4,6 +4,8 @@ interface FileHandler {
getModule(
filename: string | null | undefined
): LicenseIdentifiedModule | null;
isBuildRoot(filename: string): boolean;
isInModuleDirectory(filename: string): boolean;
}

export { FileHandler };
6 changes: 3 additions & 3 deletions src/LicenseIdentifiedModule.ts
Expand Up @@ -2,9 +2,9 @@ import { Module } from './Module';
import { PackageJson } from './PackageJson';

interface LicenseIdentifiedModule extends Module {
packageJson: PackageJson;
licenseId: string | null;
licenseText: string | null;
packageJson?: PackageJson;
licenseId?: string | null;
licenseText?: string | null;
}

export { LicenseIdentifiedModule };
73 changes: 53 additions & 20 deletions src/PluginChunkReadHandler.ts
@@ -1,23 +1,23 @@
import { WebpackChunkHandler } from './WebpackChunkHandler';
import { WebpackChunk } from './WebpackChunk';
import { WebpackChunkModule } from './WebpackChunkModule';
import { WebpackChunkModuleIterator } from './WebpackChunkModuleIterator';
import { WebpackModuleFileIterator } from './WebpackModuleFileIterator';
import { WebpackInnerModuleIterator } from './WebpackInnerModuleIterator';
import { FileHandler } from './FileHandler';
import { LicenseTypeIdentifier } from './LicenseTypeIdentifier';
import { FileSystem } from './FileSystem';
import { PackageJson } from './PackageJson';
import { LicenseTextReader } from './LicenseTextReader';
import { ModuleCache } from './ModuleCache';
import { LicensePolicy } from './LicensePolicy';
import { Module } from './Module';
import { LicenseIdentifiedModule } from './LicenseIdentifiedModule';
import { WebpackCompilation } from './WebpackCompilation';
import { Logger } from './Logger';
import { WebpackStats } from './WebpackStats';

class PluginChunkReadHandler implements WebpackChunkHandler {
private moduleIterator = new WebpackChunkModuleIterator();
private fileIterator = new WebpackModuleFileIterator(require.resolve);
private innerModuleIterator = new WebpackInnerModuleIterator(require.resolve);

constructor(
private logger: Logger,
Expand All @@ -34,15 +34,51 @@ class PluginChunkReadHandler implements WebpackChunkHandler {
moduleCache: ModuleCache,
stats: WebpackStats | undefined
) {
this.moduleIterator.iterateModules(compilation, chunk, stats, module => {
this.fileIterator.iterateFiles(
module,
(filename: string | null | undefined) => {
const module = this.fileHandler.getModule(filename);
this.processModule(compilation, chunk, moduleCache, module);
}
);
});
this.moduleIterator.iterateModules(
compilation,
chunk,
stats,
chunkModule => {
this.innerModuleIterator.iterateModules(
chunkModule,
(module: WebpackChunkModule) => {
const identifiedModule =
this.extractIdentifiedModule(module) ||
this.fileHandler.getModule(module.resource);
if (identifiedModule) {
this.processModule(
compilation,
chunk,
moduleCache,
identifiedModule
);
}
}
);
}
);
}

private extractIdentifiedModule(
module: WebpackChunkModule
): LicenseIdentifiedModule | undefined {
const resolved = module.resourceResolveData;
if (!resolved) return undefined;
const {
descriptionFileRoot: directory,
descriptionFileData: packageJson
} = resolved;
if (
this.fileHandler.isInModuleDirectory(directory) &&
!this.fileHandler.isBuildRoot(directory)
) {
return {
directory,
packageJson,
name: packageJson.name
};
}
return undefined;
}

private getPackageJson(directory: string): PackageJson {
Expand All @@ -54,20 +90,17 @@ class PluginChunkReadHandler implements WebpackChunkHandler {
compilation: WebpackCompilation,
chunk: WebpackChunk,
moduleCache: ModuleCache,
module: Module | LicenseIdentifiedModule | null
module: LicenseIdentifiedModule
) {
if (module && !moduleCache.alreadySeenForChunk(chunk.name, module.name)) {
if (!moduleCache.alreadySeenForChunk(chunk.name, module.name)) {
const alreadyIncludedModule = moduleCache.getModule(module.name);
if (alreadyIncludedModule !== null) {
moduleCache.registerModule(chunk.name, alreadyIncludedModule);
} else {
// module not yet in cache
const packageJson: PackageJson =
(<LicenseIdentifiedModule>module).packageJson ??
this.getPackageJson(module.directory);
const licenseType:
| string
| null = this.licenseTypeIdentifier.findLicenseIdentifier(
const packageJson =
module.packageJson ?? this.getPackageJson(module.directory);
const licenseType = this.licenseTypeIdentifier.findLicenseIdentifier(
compilation,
module.name,
packageJson
Expand Down
42 changes: 22 additions & 20 deletions src/PluginFileHandler.ts
Expand Up @@ -20,28 +20,30 @@ class PluginFileHandler implements FileHandler {
: this.cache[filename];
}

isInModuleDirectory(filename: string) {
if (this.modulesDirectories === null) return true;
if (filename === null || filename === undefined) return false;
let foundInModuleDirectory = false;
const resolvedPath = this.fileSystem.resolvePath(filename);
for (const modulesDirectory of this.modulesDirectories) {
if (
resolvedPath.startsWith(this.fileSystem.resolvePath(modulesDirectory))
) {
foundInModuleDirectory = true;
}
}
return foundInModuleDirectory;
}

isBuildRoot(filename: string) {
return this.buildRoot === filename;
}

private getModuleInternal(
filename: string
): Partial<LicenseIdentifiedModule> | null {
if (filename === null || filename === undefined) {
return null;
}

if (this.modulesDirectories !== null) {
let foundInModuleDirectory = false;
for (const modulesDirectory of this.modulesDirectories) {
if (
this.fileSystem
.resolvePath(filename)
.startsWith(this.fileSystem.resolvePath(modulesDirectory))
) {
foundInModuleDirectory = true;
}
}
if (!foundInModuleDirectory) {
return null;
}
}
if (filename === null || filename === undefined) return null;
if (!this.isInModuleDirectory(filename)) return null;

const module = this.findModuleDir(filename);

Expand Down Expand Up @@ -78,7 +80,7 @@ class PluginFileHandler implements FileHandler {
}
}

if (this.buildRoot === dirOfModule) {
if (this.isBuildRoot(dirOfModule)) {
return null;
}

Expand Down
4 changes: 2 additions & 2 deletions src/WebpackChunkHandler.ts
@@ -1,6 +1,6 @@
import { WebpackChunk } from './WebpackChunk';
import { ModuleCache } from './ModuleCache';
import { Module } from './Module';
import { LicenseIdentifiedModule } from './LicenseIdentifiedModule';
import { WebpackCompilation } from './WebpackCompilation';
import { WebpackStats } from './WebpackStats';

Expand All @@ -15,7 +15,7 @@ interface WebpackChunkHandler {
compilation: WebpackCompilation,
chunk: WebpackChunk,
moduleCache: ModuleCache,
module: Module
module: LicenseIdentifiedModule
): void;
}

Expand Down
8 changes: 7 additions & 1 deletion src/WebpackChunkModule.ts
@@ -1,5 +1,7 @@
import { PackageJson } from './PackageJson';

export interface WebpackChunkModule {
resource: string;
resource?: string;
rootModule?: {
resource?: string;
};
Expand All @@ -8,4 +10,8 @@ export interface WebpackChunkModule {
};
fileDependencies?: string[];
dependencies?: WebpackChunkModule[];
resourceResolveData?: {
descriptionFileRoot: string;
descriptionFileData: PackageJson;
};
}
6 changes: 3 additions & 3 deletions src/WebpackFileSystem.ts
Expand Up @@ -18,8 +18,8 @@ class WebpackFileSystem implements FileSystem {

pathExists(filename: string): boolean {
try {
this.fs.statSync(filename);
return true;
const stat = this.fs.statSync(filename, { throwIfNoEntry: false });
return !!stat;
} catch (e) {
return false;
}
Expand All @@ -44,7 +44,7 @@ class WebpackFileSystem implements FileSystem {
isDirectory(dir: string): boolean {
let isDir = false;
try {
isDir = this.fs.statSync(dir).isDirectory();
isDir = this.fs.statSync(dir, { throwIfNoEntry: false }).isDirectory();
} catch (e) {}
return isDir;
}
Expand Down
@@ -1,35 +1,37 @@
import { WebpackChunkModule } from './WebpackChunkModule';

class WebpackModuleFileIterator {
class WebpackInnerModuleIterator {
constructor(private requireResolve: RequireResolve) {}

iterateFiles(
iterateModules(
chunkModule: WebpackChunkModule,
callback: (filename: string | null | undefined) => void
callback: (module: WebpackChunkModule) => void
) {
const internalCallback = this.internalCallback.bind(this, callback);
internalCallback(
chunkModule.resource ||
(chunkModule.rootModule && chunkModule.rootModule.resource)
chunkModule.resource ? chunkModule : chunkModule.rootModule
);
if (Array.isArray(chunkModule.fileDependencies)) {
const fileDependencies: string[] = chunkModule.fileDependencies;
fileDependencies.forEach(internalCallback);
fileDependencies.forEach(fileDependency =>
internalCallback({ resource: fileDependency })
);
}
if (Array.isArray(chunkModule.dependencies)) {
chunkModule.dependencies.forEach(module =>
internalCallback(module.originModule && module.originModule.resource)
internalCallback(module.originModule)
);
}
}

private internalCallback(
callback: (filename: string | null | undefined) => void,
filename: string | null | undefined
callback: (module: WebpackChunkModule) => void,
module: WebpackChunkModule | undefined
): void {
const actualFileName = this.getActualFilename(filename);
if (!module) return;
const actualFileName = this.getActualFilename(module.resource);
if (actualFileName) {
callback(actualFileName);
callback({ ...module, resource: actualFileName });
}
}

Expand Down Expand Up @@ -69,4 +71,4 @@ class WebpackModuleFileIterator {
}
}

export { WebpackModuleFileIterator };
export { WebpackInnerModuleIterator };
@@ -1,12 +1,12 @@
import { WebpackModuleFileIterator } from '../WebpackModuleFileIterator';
import { WebpackInnerModuleIterator } from '../WebpackInnerModuleIterator';
import {
fakeRequireResolve,
FAKE_REQUIRE_RESOLVE_OUTPUT
} from './FakeRequireResolve';

const iterator = new WebpackModuleFileIterator(fakeRequireResolve);
const iterator = new WebpackInnerModuleIterator(fakeRequireResolve);

describe('WebpackModuleFileIterator', () => {
describe('WebpackInnerModuleIterator', () => {
it('returns null for falsy filename', () => {
expect(iterator.getActualFilename('')).toBeNull();
expect(iterator.getActualFilename(null)).toBeNull();
Expand Down

0 comments on commit 55dd3f8

Please sign in to comment.