Source code handling classes for webpack and other build tools with optional source map support
—
Additional utilities for source compatibility and specialized use cases, including source-like object conversion and size-only sources.
Converts source-like objects into real Source instances, enabling compatibility with objects that implement the source interface but aren't actual Source instances.
/**
* Converts source-like objects to real Sources
* @param sourceLike - Object implementing source-like interface
*/
class CompatSource extends Source {
constructor(sourceLike: SourceLike);
/**
* Static factory method that returns Source unmodified if already a Source,
* otherwise wraps in CompatSource
* @param sourceLike - Source or source-like object
* @returns Real Source instance
*/
static from(sourceLike: SourceLike): Source;
}Usage Examples:
const { CompatSource } = require("webpack-sources");
// Source-like object (not a real Source)
const sourceLike = {
source() {
return "console.log('from source-like');";
},
size() {
return 29;
},
map() {
return null;
}
};
// Convert to real Source
const compatSource = new CompatSource(sourceLike);
console.log(compatSource.source()); // "console.log('from source-like');"
console.log(compatSource instanceof Source); // true
// Static factory method - preferred approach
const source1 = CompatSource.from(sourceLike); // Wraps in CompatSource
const source2 = CompatSource.from(compatSource); // Returns as-is (already Source)
console.log(source1 !== sourceLike); // true - wrapped
console.log(source2 === compatSource); // true - not re-wrapped
// Use with objects that have partial source interface
const minimalSourceLike = {
source: () => "minimal content"
// size, map, etc. will use defaults
};
const minimal = CompatSource.from(minimalSourceLike);
console.log(minimal.size()); // Calculated from source content
console.log(minimal.map()); // null (default)Source that only provides size information without actual content. Useful for tracking sizes without storing large content in memory.
/**
* Source that only provides size information
* @param size - Size in bytes
*/
class SizeOnlySource extends Source {
constructor(size: number);
}Usage Examples:
const { SizeOnlySource } = require("webpack-sources");
// Create size-only source
const sizeOnly = new SizeOnlySource(1024);
console.log(sizeOnly.size()); // 1024
// Other methods throw errors - no actual content available
try {
sizeOnly.source(); // Throws: "Content and Map of this Source is not available (only size() is supported)"
} catch (e) {
console.log("Cannot get source:", e.message);
}
try {
sizeOnly.buffer(); // Throws same error
} catch (e) {
console.log("Cannot get buffer:", e.message);
}
try {
sizeOnly.map(); // Throws same error
} catch (e) {
console.log("Cannot get map:", e.message);
}
// Useful for size calculations without content
const totalSize = sources.reduce((sum, source) => {
return sum + source.size();
}, 0);
// Memory-efficient size tracking
const fileSizes = files.map(file =>
new SizeOnlySource(file.stats.size)
);Interface definition for objects that can be converted to Sources via CompatSource.
/**
* Interface for objects that can be converted to Sources
*/
interface SourceLike {
/** Required: Returns source content */
source(): string | Buffer;
/** Optional: Returns content as Buffer (auto-generated if missing) */
buffer?(): Buffer;
/** Optional: Returns size in bytes (calculated if missing) */
size?(): number;
/** Optional: Returns source map (defaults to null) */
map?(options?: MapOptions): RawSourceMap | null;
/** Optional: Returns both source and map (auto-generated if missing) */
sourceAndMap?(options?: MapOptions): SourceAndMap;
/** Optional: Updates hash (defaults to abstract error if missing) */
updateHash?(hash: HashLike): void;
}Complete Implementation Example:
const { CompatSource } = require("webpack-sources");
// Full source-like implementation
class CustomSourceLike {
constructor(content, filename) {
this.content = content;
this.filename = filename;
}
source() {
return this.content;
}
buffer() {
return Buffer.from(this.content, 'utf8');
}
size() {
return this.content.length;
}
map(options) {
// Custom source map generation
return {
version: 3,
sources: [this.filename],
names: [],
mappings: "AAAA",
file: this.filename
};
}
sourceAndMap(options) {
return {
source: this.source(),
map: this.map(options)
};
}
updateHash(hash) {
hash.update("CustomSourceLike");
hash.update(this.content);
}
}
const custom = new CustomSourceLike("console.log('test');", "test.js");
const source = CompatSource.from(custom);
console.log(source.source()); // "console.log('test');"
console.log(source.map().sources); // ["test.js"]Working with Unknown Sources:
const { CompatSource, Source } = require("webpack-sources");
function processAnySource(input) {
// Ensure we have a real Source instance
const source = CompatSource.from(input);
// Now we can safely use all Source methods
console.log("Size:", source.size());
console.log("Content:", source.source());
return source;
}
// Works with real Sources
processAnySource(new RawSource("test"));
// Works with source-like objects
processAnySource({
source: () => "custom content",
size: () => 14
});Size Tracking Without Content:
const { SizeOnlySource, ConcatSource } = require("webpack-sources");
class BuildTracker {
constructor() {
this.chunks = [];
}
addChunk(name, size) {
// Track size without storing content
this.chunks.push({
name,
source: new SizeOnlySource(size)
});
}
getTotalSize() {
return this.chunks.reduce((sum, chunk) => {
return sum + chunk.source.size();
}, 0);
}
getSummary() {
return this.chunks.map(chunk => ({
name: chunk.name,
size: chunk.source.size()
}));
}
}
const tracker = new BuildTracker();
tracker.addChunk("main.js", 45000);
tracker.addChunk("vendor.js", 120000);
console.log("Total size:", tracker.getTotalSize()); // 165000
console.log("Summary:", tracker.getSummary());Compatibility Layer:
const { CompatSource } = require("webpack-sources");
// Legacy system that returns source-like objects
function legacySourceProvider(filename) {
return {
source: () => fs.readFileSync(filename, 'utf8'),
// Missing other methods - CompatSource will provide defaults
};
}
// Modern system that needs real Sources
function modernProcessor(source) {
// Ensure compatibility
const realSource = CompatSource.from(source);
// Use full Source API
const content = realSource.source();
const buffer = realSource.buffer();
const size = realSource.size();
// Process...
}
// Bridge legacy and modern systems
const legacySource = legacySourceProvider("input.js");
modernProcessor(legacySource); // Works seamlesslyInstall with Tessl CLI
npx tessl i tessl/npm-webpack-sources