This commit is contained in:
Kevin McIntyre
2025-06-18 01:00:00 -04:00
commit f84b511895
228 changed files with 42509 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
const { Transform } = require("stream");
const { parse } = require("acorn");
const { Replacement } = require("./replacement");
function astTransformStream(transformer) {
return new Transform({
objectMode: true,
transform(inputFile, _, fileDone) {
const input = inputFile.contents.toString();
const comments = [];
const ast = parse(input, {
ecmaVersion: 10,
sourceType: "module",
onComment: comments,
});
const replacement = new Replacement();
transformer(replacement, ast, comments, input);
const output = replacement.replace(input, inputFile.sourceMap);
inputFile.contents = Buffer.from(output.output);
if (inputFile.sourceMap) {
inputFile.sourceMap = output.sourceMap;
}
fileDone(null, inputFile);
}
});
}
module.exports = transformer => () => astTransformStream(transformer);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
const { Node } = require("acorn");
const astTransformStream = require("./ast-transform-stream");
const DOMPROPS = require("./domprops");
const RESERVED_NAMES = require("./reserved-keywords");
const CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
function visit(node, visitor) {
if (node instanceof Node) {
visitor(node);
}
for (const key in node) {
const value = node[key];
if (Array.isArray(value)) {
value.forEach(subNode => visit(subNode, visitor));
} else if (value instanceof Node) {
visit(value, visitor);
}
}
}
function generateIdentifier(seed) {
let identifier = "";
seed = Math.abs(Math.floor(seed));
do {
const mod = seed % CHARACTERS.length;
identifier += CHARACTERS[mod];
seed = (seed / CHARACTERS.length) | 0;
} while (seed);
return identifier;
}
function mangleProps(input, ast, replacement) {
const identifierNodes = [];
const longToShortName = new Map();
// Find identifiers
visit(ast, node => {
let identifier;
if (node.type === "MemberExpression" && !node.computed) {
// Matches x.y
// Not x["y"] (computed: true)
identifier = node.property;
} else if (node.type === "MethodDefinition") {
// Matches x() { }
identifier = node.key;
} else if (node.type === "Property") {
// Matches { x: y }
// Not { "x": y }
identifier = node.key;
}
if (identifier && identifier.type === "Identifier") {
identifierNodes.push(identifier);
}
});
// Collect usage statistics per name
const usageMap = new Map();
identifierNodes.forEach(node => {
if (node.name && !RESERVED_NAMES.has(node.name) && !DOMPROPS.has(node.name)) {
usageMap.set(node.name, (usageMap.get(node.name) || 0) + 1);
}
});
// Sort by usage in descending order
const usageStats = Array.from(usageMap).sort((a, b) => b[1] - a[1]);
// Allocate identifiers in order of usage statistics to ensure
// frequently used symbols get as short identifiers as possible.
let runningCounter = 0;
usageStats.forEach(identifier => {
const longName = identifier[0];
if (!longToShortName.has(longName)) {
let shortName;
do {
shortName = generateIdentifier(runningCounter++);
} while (RESERVED_NAMES.has(shortName) || DOMPROPS.has(shortName));
longToShortName.set(longName, shortName);
}
});
// Populate replacements
identifierNodes.forEach(node => {
const minifiedName = longToShortName.get(node.name);
if (minifiedName) {
replacement.addRange({
start: node.start,
end: node.end,
replacement: minifiedName + "/*" + node.name + "*/",
name: node.name,
});
}
});
return replacement;
}
function simplifyES5Class(input, ast, replacement) {
const prototypeMemberExpressions = [];
const duplicateNamedFunctions = [];
visit(ast, node => {
if (node.type === "MemberExpression" &&
!node.computed &&
node.object.type === "Identifier" &&
node.property.type === "Identifier" &&
node.property.name === "prototype"
) {
// Matches: xxx.prototype
prototypeMemberExpressions.push(node);
} else if (
node.type === "VariableDeclaration" &&
node.declarations.length === 1 &&
node.declarations[0].init &&
node.declarations[0].init.type === "FunctionExpression" &&
node.declarations[0].init.id &&
node.declarations[0].init.id.name === node.declarations[0].id.name
) {
// Matches: var xxx = function xxx ();
duplicateNamedFunctions.push(node);
}
});
duplicateNamedFunctions.forEach(duplicateNamedFunction => {
const functionName = duplicateNamedFunction.declarations[0].init.id.name;
// Remove: var xxx =
replacement.addRange({
start: duplicateNamedFunction.start,
end: duplicateNamedFunction.declarations[0].init.start,
replacement: "",
});
// Remove trailing semicolons
let semicolons = 0;
while (input[duplicateNamedFunction.end - semicolons - 1] === ";") semicolons++;
// Find prototype references
const refs = prototypeMemberExpressions.filter(node => node.object.name === functionName);
if (refs.length > 1) {
// Insert: var xx__prototype = xxx.prototype;
replacement.addRange({
start: duplicateNamedFunction.end - semicolons,
end: duplicateNamedFunction.end,
replacement: `\r\nvar ${functionName}__prototype = ${functionName}.prototype;`,
});
// Replace references
refs.forEach(ref => {
replacement.addRange({
start: ref.start,
end: ref.end,
replacement: `${functionName}__prototype`,
});
});
} else if (semicolons) {
replacement.addRange({
start: duplicateNamedFunction.end - semicolons,
end: duplicateNamedFunction.end,
replacement: "",
});
}
});
}
function browserConstants(input, ast, replacement) {
replacement.addText("Node.ELEMENT_NODE", "1");
}
const MINIFIERS = [simplifyES5Class, mangleProps, browserConstants];
module.exports = astTransformStream(function (replacement, ast, comments, input) {
MINIFIERS.forEach(minifier => minifier(input, ast, replacement));
});

View File

@@ -0,0 +1,52 @@
const astTransformStream = require("./ast-transform-stream");
function removeJSDocImports(comments, replacement) {
const REGEX = /[ \t]*\*[ \t]*@typedef\s+\{import.+\r?\n?|import\(.*?\)\.([a-zA-Z_]+)/g;
const JSDOC_COMMENT_OFFSET = 2;
comments.forEach(comment => {
if (comment.type === "Block" && comment.value[0] === "*") {
// JSDoc comment
const resultingComment = comment.value.replace(REGEX, (match, importName, matchIndex) => {
matchIndex += comment.start + JSDOC_COMMENT_OFFSET;
if (importName) {
// { import().xxx }
replacement.addRange({
start: matchIndex,
end: matchIndex + match.length,
replacement: importName,
});
return importName;
} else {
// @typedef
replacement.addRange({
start: matchIndex,
end: matchIndex + match.length,
replacement: "",
});
return "";
}
});
if (!/[^\s\*]/.test(resultingComment)) {
// Empty comment left
replacement.addRange({
start: comment.start,
end: comment.end,
replacement: "",
});
}
}
});
}
module.exports = astTransformStream(function (replacement, ast, comments, input) {
removeJSDocImports(comments, replacement);
});

View File

@@ -0,0 +1,54 @@
const { Transform } = require("stream");
const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
function removeMappedSourceStream(...sourceNames) {
const sourceNamesToRemove = new Set(sourceNames);
return new Transform({
objectMode: true,
transform(inputFile, _, fileDone) {
if (inputFile.sourceMap) {
let consumer = inputFile.sourceMap;
if (!(consumer instanceof SourceMapConsumer)) {
consumer = new SourceMapConsumer(consumer);
}
const generator = new SourceMapGenerator({
file: consumer.file,
sourceRoot: consumer.sourceRoot,
});
consumer.sources.forEach(sourceFile => {
const content = consumer.sourceContentFor(sourceFile);
if (content != null && !sourceNamesToRemove.has(sourceFile)) {
generator.setSourceContent(sourceFile, content);
}
});
consumer.eachMapping(mapping => {
if (!sourceNamesToRemove.has(mapping.source)) {
generator.addMapping({
original: {
line: mapping.originalLine,
column: mapping.originalColumn,
},
generated: {
line: mapping.generatedLine,
column: mapping.generatedColumn,
},
source: mapping.source,
name: mapping.name,
});
}
});
inputFile.sourceMap = generator.toJSON();
}
fileDone(null, inputFile);
}
});
}
module.exports = removeMappedSourceStream;

View File

@@ -0,0 +1,672 @@
/**
* Jdenticon
* https://github.com/dmester/jdenticon
* Copyright © Daniel Mester Pirttijärvi
*/
const { Transform } = require("stream");
const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
/**
* Finds substrings and replaces them with other strings, keeping any input source map up-to-date.
*
* @example
* const replacement = new Replacement([
* ["find this", "replace with this"],
* [/find this/gi, "replace with this"]
* ]);
* replacement.replace(input, inputSourceMap);
*
* @example
* const replacement = new Replacement("find this", "replace with this");
* replacement.replace(input, inputSourceMap);
*/
class Replacement {
constructor(...definition) {
/**
* @type {function(Array<OverwriteRange>, string): void}
*/
this._matchers = [];
/**
* @type {Array<OverwriteRange>}
*/
this._ranges = [];
this.add(definition);
}
/**
* @param {string} input
* @returns {Array<OverwriteRange>}
*/
matchAll(input) {
const ranges = [...this._ranges];
this._matchers.forEach(matcher => matcher(ranges, input));
let lastReplacement;
return ranges
.sort((a, b) => a.start - b.start)
.filter(replacement => {
if (!lastReplacement || lastReplacement.end <= replacement.start) {
lastReplacement = replacement;
return true;
}
});
}
/**
* @param {string} input
* @param {SourceMap=} inputSourceMap
* @returns {{ output: string, sourceMap: SourceMap }}
*/
replace(input, inputSourceMap) {
const ranges = this.matchAll(input);
const offset = new Offset();
const reader = new InputReader(input);
const sourceMap = new SourceMapSpooler(inputSourceMap);
const output = [];
if (sourceMap.isEmpty()) {
sourceMap.initEmpty(reader.lines);
}
ranges.forEach((range, rangeIndex) => {
output.push(reader.readTo(range.start));
output.push(range.replacement);
const inputStart = reader.pos;
const replacedText = reader.readTo(range.end);
if (replacedText === range.replacement) {
return; // Nothing to do
}
const inputEnd = reader.pos;
const replacementLines = range.replacement.split(/\n/g);
const lineDifference = replacementLines.length + inputStart.line - inputEnd.line - 1;
const outputStart = {
line: inputStart.line + offset.lineOffset,
column: inputStart.column + offset.getColumnOffset(inputStart.line)
};
const outputEnd = {
line: inputEnd.line + offset.lineOffset + lineDifference,
column: replacementLines.length > 1 ?
replacementLines[replacementLines.length - 1].length :
inputStart.column + offset.getColumnOffset(inputStart.line) +
range.replacement.length
}
sourceMap.spoolTo(inputStart.line, inputStart.column, offset);
offset.lineOffset += lineDifference;
offset.setColumnOffset(inputEnd.line, outputEnd.column - inputEnd.column);
if (range.name || replacementLines.length === 1 && range.replacement) {
const mappingBeforeStart = sourceMap.lastMapping();
const mappingAfterStart = sourceMap.nextMapping();
if (mappingAfterStart &&
mappingAfterStart.generatedLine === inputStart.line &&
mappingAfterStart.generatedColumn === inputStart.column
) {
sourceMap.addMapping({
original: {
line: mappingAfterStart.originalLine,
column: mappingAfterStart.originalColumn,
},
generated: {
line: outputStart.line,
column: outputStart.column
},
source: mappingAfterStart.source,
name: range.name,
});
} else if (mappingBeforeStart && mappingBeforeStart.generatedLine === inputStart.line) {
sourceMap.addMapping({
original: {
line: mappingBeforeStart.originalLine + inputStart.line - mappingBeforeStart.generatedLine,
column: mappingBeforeStart.originalColumn + inputStart.column - mappingBeforeStart.generatedColumn,
},
generated: {
line: outputStart.line,
column: outputStart.column
},
source: mappingBeforeStart.source,
name: range.name,
});
}
} else if (range.replacement) {
// Map longer replacements to a virtual file defined in the source map
const generatedSourceName = sourceMap.addSourceContent(replacedText, range.replacement);
for (var i = 0; i < replacementLines.length; i++) {
// Don't map empty lines
if (replacementLines[i]) {
sourceMap.addMapping({
original: {
line: i + 1,
column: 0,
},
generated: {
line: outputStart.line + i,
column: i ? 0 : outputStart.column,
},
source: generatedSourceName,
});
}
}
}
sourceMap.skipTo(inputEnd.line, inputEnd.column, offset);
// Add a source map node directly after the replacement to terminate the replacement
const mappingAfterEnd = sourceMap.nextMapping();
const mappingBeforeEnd = sourceMap.lastMapping();
if (mappingAfterEnd &&
mappingAfterEnd.generatedLine === inputEnd.line &&
mappingAfterEnd.generatedColumn === inputEnd.column
) {
// No extra source map node needed when the replacement is directly followed by another node
} else if (rangeIndex + 1 < ranges.length && range.end === ranges[rangeIndex + 1].start) {
// The next replacement range is adjacent to this one
} else if (reader.endOfLine()) {
// End of line, no point in adding a following node
} else if (!mappingBeforeEnd || mappingBeforeEnd.generatedLine !== inputEnd.line) {
// No applicable preceding node found
} else {
sourceMap.addMapping({
original: {
line: mappingBeforeEnd.originalLine + inputEnd.line - mappingBeforeEnd.generatedLine,
column: mappingBeforeEnd.originalColumn + inputEnd.column - mappingBeforeEnd.generatedColumn,
},
generated: {
line: outputEnd.line,
column: outputEnd.column
},
source: mappingBeforeEnd.source,
});
}
});
// Flush remaining input to output and source map
output.push(reader.readToEnd());
sourceMap.spoolToEnd(offset);
return {
output: output.join(""),
sourceMap: sourceMap.toJSON(),
};
}
add(value) {
const target = this;
function addRecursive(innerValue) {
if (innerValue != null) {
if (Array.isArray(innerValue)) {
const needle = innerValue[0];
if (typeof needle === "string") {
target.addText(...innerValue);
} else if (needle instanceof RegExp) {
target.addRegExp(...innerValue);
} else {
innerValue.forEach(addRecursive);
}
} else if (innerValue instanceof Replacement) {
target._matchers.push(innerValue._matchers);
target._ranges.push(innerValue._ranges);
} else if (typeof innerValue === "object") {
target.addRange(innerValue);
} else {
throw new Error("Unknown replacement argument specified.");
}
}
}
addRecursive(value);
}
/**
* @param {RegExp} re
* @param {string|function(string, ...):string} replacement
* @param {{ name: string }=} rangeOpts
*/
addRegExp(re, replacement, rangeOpts) {
const replacementFactory = this._createReplacementFactory(replacement);
this._matchers.push((ranges, input) => {
const isGlobalRegExp = /g/.test(re.flags);
let match;
let isFirstIteration = true;
while ((isFirstIteration || isGlobalRegExp) && (match = re.exec(input))) {
ranges.push(new OverwriteRange({
...rangeOpts,
start: match.index,
end: match.index + match[0].length,
replacement: replacementFactory(match, match.index, input),
}));
isFirstIteration = false;
}
});
}
/**
* @param {string} needle
* @param {string|function(string, ...):string} replacement
* @param {{ name: string }=} rangeOpts
*/
addText(needle, replacement, rangeOpts) {
const replacementFactory = this._createReplacementFactory(replacement);
this._matchers.push((ranges, input) => {
let index = -needle.length;
while ((index = input.indexOf(needle, index + needle.length)) >= 0) {
ranges.push(new OverwriteRange({
...rangeOpts,
start: index,
end: index + needle.length,
replacement: replacementFactory([needle], index, input),
}));
}
});
}
/**
* @param {OverwriteRange} range
*/
addRange(range) {
this._ranges.push(new OverwriteRange(range));
}
/**
* @param {string|function(string, ...):string} replacement
* @returns {function(Array<string>, number, string):string}
*/
_createReplacementFactory(replacement) {
if (typeof replacement === "function") {
return (match, index, input) => replacement(...match, index, input);
}
if (replacement == null) {
return () => "";
}
replacement = replacement.toString();
if (replacement.indexOf("$") < 0) {
return () => replacement;
}
return (match, index, input) =>
replacement.replace(/\$(\d+|[$&`'])/g, matchedPattern => {
if (matchedPattern === "$$") {
return "$";
}
if (matchedPattern === "$&") {
return match[0];
}
if (matchedPattern === "$`") {
return input.substring(0, index);
}
if (matchedPattern === "$'") {
return input.substring(index + match[0].length);
}
const matchArrayIndex = Number(matchedPattern.substring(1));
return match[matchArrayIndex];
});
}
}
class InputReader {
/**
* @param {string} input
*/
constructor (input) {
// Find index of all line breaks
const lineBreakIndexes = [];
let index = -1;
while ((index = input.indexOf("\n", index + 1)) >= 0) {
lineBreakIndexes.push(index);
}
this._input = input;
this._inputCursorExclusive = 0;
this._output = [];
this._lineBreakIndexes = lineBreakIndexes;
/**
* Number of lines in the input file.
* @type {number}
*/
this.lines = this._lineBreakIndexes.length + 1;
/**
* Position of the input cursor. Line number is one-based and column number is zero-based.
* @type {{ line: number, column: number }}
*/
this.pos = { line: 1, column: 0 };
}
readTo(exclusiveIndex) {
let result = "";
if (this._inputCursorExclusive < exclusiveIndex) {
result = this._input.substring(this._inputCursorExclusive, exclusiveIndex);
this._inputCursorExclusive = exclusiveIndex;
this._updatePos();
}
return result;
}
readToEnd() {
return this.readTo(this._input.length);
}
endOfLine() {
const nextChar = this._input[this._inputCursorExclusive];
return !nextChar || nextChar === "\r" || nextChar === "\n";
}
_updatePos() {
let line = this.pos.line;
while (
line - 1 < this._lineBreakIndexes.length &&
this._lineBreakIndexes[line - 1] < this._inputCursorExclusive
) {
line++;
}
const lineStartIndex = this._lineBreakIndexes[line - 2];
const column = this._inputCursorExclusive - (lineStartIndex || -1) - 1;
this.pos = { line, column };
}
}
class SourceMapSpooler {
/**
* @param {SourceMap=} inputSourceMap
*/
constructor(inputSourceMap) {
let generator;
let file;
let sources;
let mappings = [];
if (inputSourceMap) {
if (!(inputSourceMap instanceof SourceMapConsumer)) {
inputSourceMap = new SourceMapConsumer(inputSourceMap);
}
generator = new SourceMapGenerator({
file: inputSourceMap.file,
sourceRoot: inputSourceMap.sourceRoot,
});
inputSourceMap.sources.forEach(function(sourceFile) {
const content = inputSourceMap.sourceContentFor(sourceFile);
if (content != null) {
generator.setSourceContent(sourceFile, content);
}
});
inputSourceMap.eachMapping(mapping => {
mappings.push(mapping);
});
mappings.sort((a, b) => a.generatedLine == b.generatedLine
? a.generatedColumn - b.generatedColumn : a.generatedLine - b.generatedLine);
file = inputSourceMap.file;
sources = inputSourceMap.sources;
} else {
generator = new SourceMapGenerator();
file = "input";
sources = [];
}
this._generator = generator;
this._sources = new Set(sources);
this._file = file;
this._mappingsCursor = 0;
this._mappings = mappings;
this._contents = new Map();
}
lastMapping() {
return this._mappings[this._mappingsCursor - 1];
}
nextMapping() {
return this._mappings[this._mappingsCursor];
}
addMapping(mapping) {
this._generator.addMapping(mapping);
}
isEmpty() {
return this._mappings.length === 0;
}
initEmpty(lines) {
this._mappings = [];
for (var i = 0; i < lines; i++) {
this._mappings.push({
originalLine: i + 1,
originalColumn: 0,
generatedLine: i + 1,
generatedColumn: 0,
source: this._file
});
}
}
addSourceContent(replacedText, content) {
let sourceName = this._contents.get(content);
if (!sourceName) {
const PREFIX = "replacement/";
let sourceNameWithoutNumber = PREFIX;
sourceName = sourceNameWithoutNumber + "1";
if (replacedText.length > 0 && replacedText.length < 25) {
replacedText = replacedText
.replace(/^[^0-9a-z-_]+|[^0-9a-z-_]+$/ig, "")
.replace(/\s+/g, "-")
.replace(/[^0-9a-z-_]/ig, "");
if (replacedText) {
sourceNameWithoutNumber = PREFIX + replacedText + "-";
sourceName = PREFIX + replacedText;
}
}
let counter = 2;
while (this._sources.has(sourceName)) {
sourceName = sourceNameWithoutNumber + counter++;
}
this._sources.add(sourceName);
this._contents.set(content, sourceName);
this._generator.setSourceContent(sourceName, content);
}
return sourceName;
}
/**
* Copies source map info from input to output up to but not including the specified position.
* @param {number} line
* @param {number} column
* @param {Offset} offset
*/
spoolTo(line, column, offset) {
this._consume(line, column, offset, true);
}
/**
* Copies remaining source map info from input to output.
* @param {number} line
* @param {number} column
* @param {Offset} offset
*/
spoolToEnd(offset) {
this._consume(null, null, offset, true);
}
/**
* Discards source map info from input up to but not including the specified position.
* @param {number} line
* @param {number} column
* @param {Offset} offset
*/
skipTo(line, column, offset) {
this._consume(line, column, offset, false);
}
toJSON() {
return this._generator.toJSON();
}
_consume(line, column, offset, keep) {
let mapping;
while (
(mapping = this._mappings[this._mappingsCursor]) &&
(
line == null ||
mapping.generatedLine < line ||
mapping.generatedLine == line && mapping.generatedColumn < column
)
) {
if (keep) {
this._generator.addMapping({
original: {
line: mapping.originalLine,
column: mapping.originalColumn,
},
generated: {
line: mapping.generatedLine + offset.lineOffset,
column: mapping.generatedColumn + offset.getColumnOffset(mapping.generatedLine),
},
source: mapping.source,
name: mapping.name,
});
}
this._mappingsCursor++;
}
}
}
class Offset {
constructor() {
this.lineOffset = 0;
this._columnOffset = 0;
this._columnOffsetForLine = 0;
}
setColumnOffset(lineNumber, columnOffset) {
this._columnOffsetForLine = lineNumber;
this._columnOffset = columnOffset;
}
getColumnOffset(lineNumber) {
return this._columnOffsetForLine === lineNumber ?
this._columnOffset : 0;
}
}
class OverwriteRange {
constructor(options) {
if (!isFinite(options.start)) {
throw new Error("A replacement start index is required.");
}
if (!isFinite(options.end)) {
throw new Error("A replacement end index is required.");
}
if (options.end < options.start) {
throw new Error("Replacement end index cannot precede its start index.");
}
/**
* Inclusive start index.
* @type {number}
*/
this.start = options.start;
/**
* Exclusive start index.
* @type {number}
*/
this.end = options.end;
/**
* The replacement interval will be replaced with this string.
* @type string
*/
this.replacement = "" + options.replacement;
/**
* Optional name that will be mapped in the source map.
* @type string
*/
this.name = options.name;
}
}
function gulp(replacements) {
if (typeof replacements === "string" || replacements instanceof RegExp) {
replacements = Array.from(arguments);
}
const replacer = new Replacement(replacements);
return new Transform({
objectMode: true,
transform(inputFile, _, fileDone) {
const input = inputFile.contents.toString();
const output = replacer.replace(input, inputFile.sourceMap);
inputFile.contents = Buffer.from(output.output);
if (inputFile.sourceMap) {
inputFile.sourceMap = output.sourceMap;
}
fileDone(null, inputFile);
}
});
}
module.exports = { Replacement, gulp };

View File

@@ -0,0 +1,70 @@
module.exports = new Set([
"async",
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"finally",
"for",
"function",
"if",
"import",
"in",
"of",
"instanceof",
"new",
"return",
"super",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"yield",
"enum",
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"await",
"abstract",
"boolean",
"byte",
"char",
"double",
"final",
"float",
"goto",
"int",
"long",
"native",
"short",
"synchronized",
"throws",
"transient",
"volatile",
"arguments",
"get",
"set",
"null",
"undefined",
"exports",
"module",
]);

View File

@@ -0,0 +1,56 @@
/**
* Jdenticon
* https://github.com/dmester/jdenticon
* Copyright © Daniel Mester Pirttijärvi
*/
const { rollup } = require("rollup");
const Vinyl = require("vinyl");
const applySourceMap = require("vinyl-sourcemaps-apply");
const { Transform } = require("stream");
function rollupStream(options) {
return new Transform({
objectMode: true,
transform(inputFile, _, fileDone) {
const inputOptions = {
onwarn: warn => console.warn(warn.toString()),
...options,
input: inputFile.path,
};
delete inputOptions.output;
rollup(inputOptions).then(bundle => {
return bundle.generate({
...options.output,
sourcemap: !!inputFile.sourceMap
});
}).then(outputs => {
for (const output of outputs.output) {
if (output.type === "chunk") {
const outputFile = new Vinyl({
cwd: inputFile.cwd,
base: inputFile.base,
path: inputFile.path,
contents: Buffer.from(output.code),
});
if (inputFile.sourceMap) {
applySourceMap(outputFile, output.map);
}
this.push(outputFile);
}
}
fileDone();
}, err => fileDone(err));
}
});
}
module.exports = rollupStream;

View File

@@ -0,0 +1,26 @@
const fs = require("fs");
const replace = require("./replacement").gulp;
function wrapTemplate(templatePath, variables) {
let template = fs.readFileSync(templatePath).toString();
if (variables) {
variables.forEach(variable => {
template = template.replace(variable[0], variable[1]);
});
}
template = template.split(/\/\*content\*\//);
const replacements = [];
if (template[0]) {
replacements.push([/^/, template[0]]);
}
if (template[1]) {
replacements.push([/$/, template[1]]);
}
return replace(replacements);
}
module.exports = wrapTemplate;