UNPKG

@magento/pwa-buildpack

Version:

Build/Layout optimization tooling and Peregrine framework adapters for the Magento PWA

123 lines (109 loc) 3.94 kB
/** * Webpack loader for splicing source code as text, to do * simple and fast operations like prepending/appending. * * The power behind the "source" methods in [TargetableModule][]. */ const { inspect } = require('util'); class SpliceError extends Error { constructor(message, instruction) { super( `Invalid splice instruction:\n\n${inspect(instruction, { compact: false })}\n\n${message}` ); } } const spliceSource = (source, index, insert, remove) => { const left = source.slice(0, index); let right = source.slice(index); if (remove) { right = right.slice(remove); } if (insert) { right = insert + right; } return left + right; }; const isNonNegativeInt = num => Number.isSafeInteger(num) && num >= 0; const isNonEmptyString = str => typeof str === 'string' && str.length > 0; const instructionTypes = ['after', 'at', 'before']; function spliceSourceLoader(content) { return this.query.reduce((source, instr) => { const nope = msg => { this.emitError(new SpliceError(msg, instr)); return source; }; const spliceAt = pos => spliceSource(source, pos, instr.insert, instr.remove); const hasInsert = instr.hasOwnProperty('insert'); const hasRemove = instr.hasOwnProperty('remove'); if (!(hasInsert || hasRemove)) { return nope('Nothing to insert or remove.', instr); } if (hasInsert && typeof instr.insert !== 'string') { return nope('An "insert" property must be a string.'); } if (hasRemove && !isNonNegativeInt(instr.remove)) { return nope( 'A "remove" property must be an integer greater than or equal to 0.' ); } const found = instructionTypes.filter(type => instr.hasOwnProperty(type) ); if (found.length !== 1) { return nope( `Must have one and only one of the options ${inspect( instructionTypes )}.` ); } const [instructionType] = found; const value = instr[instructionType]; switch (instructionType) { case 'at': { if (!isNonNegativeInt(value)) { return nope( 'An "at" property must be an integer greater than or equal to 0.' ); } if (value > source.length) { return nope( `Out of range: the source is only ${ source.length } characters` ); } return spliceAt(value); } case 'before': { if (!isNonEmptyString(value)) { return nope( `A "before" property must be a non-empty string.` ); } const pos = source.indexOf(value); return pos === -1 ? nope(`The text "${value}" was not found.`) : spliceAt(pos); } case 'after': { if (!isNonEmptyString(value)) { return nope( `An "after" property must be a non-empty string.` ); } const pos = source.indexOf(value); return pos === -1 ? nope(`The text "${value}" was not found.`) : spliceAt(pos + value.length); } /** istanbul ignore next: to get here, it'll be one of the above */ default: /** istanbul ignore next: this too */ break; } }, content); } module.exports = spliceSourceLoader;