babel-plugin-styled-components
Version:
Improve the debugging experience and add server-side rendering support to styled-components
119 lines (111 loc) • 4.39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.stripLineComment = exports.minifyRawValues = exports.minifyRaw = exports.minifyCookedValues = exports.minifyCooked = exports.compressSymbols = void 0;
var _placeholderUtils = require("../css/placeholderUtils");
const difference = (a, b) => {
if (!a) return [];
const set = new Set(b);
return a.filter(x => !set.has(x));
};
const injectUniquePlaceholders = strArr => {
let i = 0;
return strArr.reduce((str, val, index, arr) => {
return str + val + (index < arr.length - 1 ? (0, _placeholderUtils.makePlaceholder)(i++) : '');
}, '');
};
const makeMultilineCommentRegex = newlinePattern => new RegExp('\\/\\*[^!](.|' + newlinePattern + ')*?\\*\\/', 'g');
const symbolRegex = /(\s*[;:{},]\s*)/g;
// Returns the quote character still open at the end of str, given the one open
// at its start ('' when outside a string). A backslash escapes the next character
const quoteOpenAfter = (str, quote) => {
for (let i = 0; i < str.length; i++) {
const char = str[i];
if (char === '\\') {
i++;
} else if (quote) {
if (char === quote) {
quote = '';
}
} else if (char === '"' || char === "'") {
quote = char;
}
}
return quote;
};
// Cuts the line at the first `//` the styled-components runtime treats as a
// comment: outside strings and parentheses, and not directly after a colon
// (`https://`). A `)` without a match on the line closes a value opened on a
// previous line (e.g. `);` after `minmax(`), so it's ignored
const stripLineComment = line => {
if (line.indexOf('//') === -1) {
return line;
}
let depth = 0;
let quote = '';
for (let i = 0; i < line.length; i++) {
const char = line[i];
if (char === '\\') {
i++;
} else if (quote) {
if (char === quote) {
quote = '';
}
} else if (char === '"' || char === "'") {
quote = char;
} else if (char === '(') {
depth++;
} else if (char === ')') {
if (depth > 0) {
depth--;
}
} else if (char === '/' && line[i + 1] === '/' && depth === 0 && line[i - 1] !== ':') {
return line.slice(0, i);
}
}
return line;
};
exports.stripLineComment = stripLineComment;
const compressSymbols = code => {
let quote = '';
return code.split(symbolRegex).reduce((str, fragment, index) => {
// Even-indices are non-symbol fragments
if (index % 2 === 0) {
quote = quoteOpenAfter(fragment, quote);
return str + fragment;
}
// Only manipulate symbols outside of strings
if (quote) {
return str + fragment;
}
// Preserve whitespace preceding colon, to avoid joining selectors.
if (/^\s+:/.test(fragment)) {
return str + ' ' + fragment.trim();
}
return str + fragment.trim();
}, '');
};
// Detects lines that are exclusively line comments
exports.compressSymbols = compressSymbols;
const isLineComment = line => line.trim().startsWith('//');
// Creates a minifier with a certain linebreak pattern
const minify = linebreakPattern => {
const linebreakRegex = new RegExp(linebreakPattern + '\\s*', 'g');
const multilineCommentRegex = makeMultilineCommentRegex(linebreakPattern);
return code => {
const newCode = code.replace(multilineCommentRegex, '\n') // Remove multiline comments
.split(linebreakRegex) // Split at newlines
.filter(line => line.length > 0 && !isLineComment(line)) // Removes lines containing only line comments
.map(stripLineComment) // Remove line comments inside text
.join(' '); // Rejoin all lines
const eliminatedExpressionIndices = difference(code.match(_placeholderUtils.placeholderRegex), newCode.match(_placeholderUtils.placeholderRegex)).map(x => parseInt(x.match(/\d+/)[0], 10));
return [compressSymbols(newCode), eliminatedExpressionIndices];
};
};
const minifyRaw = exports.minifyRaw = minify('(?:\\\\r|\\\\n|\\r|\\n)');
const minifyCooked = exports.minifyCooked = minify('[\\r\\n]');
const minifyRawValues = rawValues => (0, _placeholderUtils.splitByPlaceholders)(minifyRaw(injectUniquePlaceholders(rawValues)), false);
exports.minifyRawValues = minifyRawValues;
const minifyCookedValues = cookedValues => (0, _placeholderUtils.splitByPlaceholders)(minifyCooked(injectUniquePlaceholders(cookedValues)), false);
exports.minifyCookedValues = minifyCookedValues;