"use strict";
const { RawSource, ReplaceSource } = require("webpack-sources");
class JavascriptGenerator {
generate(module, dependencyTemplates, runtimeTemplate) {
const originalSource = module.originalSource();
if (!originalSource) {
return new RawSource("throw new Error('No source available');");
}
const source = new ReplaceSource(originalSource);
this.sourceBlock(
module,
module,
[],
dependencyTemplates,
source,
runtimeTemplate
);
return source;
}
sourceBlock(
module,
block,
availableVars,
dependencyTemplates,
source,
runtimeTemplate
) {
for (const dependency of block.dependencies) {
this.sourceDependency(
dependency,
dependencyTemplates,
source,
runtimeTemplate
);
}
const vars = block.variables.reduce((result, value) => {
const variable = this.sourceVariables(
value,
availableVars,
dependencyTemplates,
runtimeTemplate
);
if (variable) {
result.push(variable);
}
return result;
}, []);
if (vars.length > 0) {
const injectionVariableChunks = this.splitVariablesInUniqueNamedChunks(
vars
);
const functionWrapperStarts = injectionVariableChunks.map(
variableChunk => {
return this.variableInjectionFunctionWrapperStartCode(
variableChunk.map(variable => variable.name)
);
}
);
const functionWrapperEnds = injectionVariableChunks.map(variableChunk => {
return this.variableInjectionFunctionWrapperEndCode(
module,
variableChunk.map(variable => variable.expression),
block
);
});
const varStartCode = functionWrapperStarts.join("");
const varEndCode = functionWrapperEnds.reverse().join("");
if (varStartCode && varEndCode) {
const start = block.range ? block.range[0] : -10;
const end = block.range
? block.range[1]
: module.originalSource().size() + 1;
source.insert(start + 0.5, varStartCode);
source.insert(end + 0.5, "\n/* WEBPACK VAR INJECTION */" + varEndCode);
}
}
for (const childBlock of block.blocks) {
this.sourceBlock(
module,
childBlock,
availableVars.concat(vars),
dependencyTemplates,
source,
runtimeTemplate
);
}
}
sourceDependency(dependency, dependencyTemplates, source, runtimeTemplate) {
const template = dependencyTemplates.get(dependency.constructor);
if (!template) {
throw new Error(
"No template for dependency: " + dependency.constructor.name
);
}
template.apply(dependency, source, runtimeTemplate, dependencyTemplates);
}
sourceVariables(
variable,
availableVars,
dependencyTemplates,
runtimeTemplate
) {
const name = variable.name;
const expr = variable.expressionSource(
dependencyTemplates,
runtimeTemplate
);
if (
availableVars.some(
v => v.name === name && v.expression.source() === expr.source()
)
) {
return;
}
return {
name: name,
expression: expr
};
}
variableInjectionFunctionWrapperStartCode(varNames) {
const args = varNames.join(", ");
return `/* WEBPACK VAR INJECTION */(function(${args}) {`;
}
contextArgument(module, block) {
if (this === block) {
return module.exportsArgument;
}
return "this";
}
variableInjectionFunctionWrapperEndCode(module, varExpressions, block) {
const firstParam = this.contextArgument(module, block);
const furtherParams = varExpressions.map(e => e.source()).join(", ");
return `}.call(${firstParam}, ${furtherParams}))`;
}
splitVariablesInUniqueNamedChunks(vars) {
const startState = [[]];
return vars.reduce((chunks, variable) => {
const current = chunks[chunks.length - 1];
const variableNameAlreadyExists = current.some(
v => v.name === variable.name
);
if (variableNameAlreadyExists) {
chunks.push([variable]);
} else {
current.push(variable);
}
return chunks;
}, startState);
}
}
module.exports = JavascriptGenerator;