summaryrefslogtreecommitdiff
path: root/node_modules/postcss-minify-font-values/src/lib/minify-family.js
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2023-10-14 15:05:55 +0800
committerlittleblack111 <littleblack11111@gmail.com>2023-10-14 15:05:55 +0800
commitc06f7ee0a40255214c477e13d9b956a7e07c2900 (patch)
treef5982a568ef8fab865d0142e71c7494fea3e4c1b /node_modules/postcss-minify-font-values/src/lib/minify-family.js
parent3fd9400d4dccc25aa29b5702216732e0b7542073 (diff)
postcss,(plugins): nanocss, postcss-preset-env
Diffstat (limited to 'node_modules/postcss-minify-font-values/src/lib/minify-family.js')
-rw-r--r--node_modules/postcss-minify-font-values/src/lib/minify-family.js248
1 files changed, 248 insertions, 0 deletions
diff --git a/node_modules/postcss-minify-font-values/src/lib/minify-family.js b/node_modules/postcss-minify-font-values/src/lib/minify-family.js
new file mode 100644
index 00000000..5d6c4851
--- /dev/null
+++ b/node_modules/postcss-minify-font-values/src/lib/minify-family.js
@@ -0,0 +1,248 @@
+'use strict';
+const { stringify } = require('postcss-value-parser');
+
+/**
+ * @param {string[]} list
+ * @return {string[]}
+ */
+function uniqueFontFamilies(list) {
+ return list.filter((item, i) => {
+ if (item.toLowerCase() === 'monospace') {
+ return true;
+ }
+ return i === list.indexOf(item);
+ });
+}
+
+const globalKeywords = ['inherit', 'initial', 'unset'];
+const genericFontFamilykeywords = new Set([
+ 'sans-serif',
+ 'serif',
+ 'fantasy',
+ 'cursive',
+ 'monospace',
+ 'system-ui',
+]);
+
+/**
+ * @param {string} value
+ * @param {number} length
+ * @return {string[]}
+ */
+function makeArray(value, length) {
+ let array = [];
+ while (length--) {
+ array[length] = value;
+ }
+ return array;
+}
+
+// eslint-disable-next-line no-useless-escape
+const regexSimpleEscapeCharacters = /[ !"#$%&'()*+,.\/;<=>?@\[\\\]^`{|}~]/;
+
+/**
+ * @param {string} string
+ * @param {boolean} escapeForString
+ * @return {string}
+ */
+function escape(string, escapeForString) {
+ let counter = 0;
+ let character;
+ let charCode;
+ let value;
+ let output = '';
+
+ while (counter < string.length) {
+ character = string.charAt(counter++);
+ charCode = character.charCodeAt(0);
+
+ // \r is already tokenized away at this point
+ // `:` can be escaped as `\:`, but that fails in IE < 8
+ if (!escapeForString && /[\t\n\v\f:]/.test(character)) {
+ value = '\\' + charCode.toString(16) + ' ';
+ } else if (
+ !escapeForString &&
+ regexSimpleEscapeCharacters.test(character)
+ ) {
+ value = '\\' + character;
+ } else {
+ value = character;
+ }
+
+ output += value;
+ }
+
+ if (!escapeForString) {
+ if (/^-[-\d]/.test(output)) {
+ output = '\\-' + output.slice(1);
+ }
+
+ const firstChar = string.charAt(0);
+
+ if (/\d/.test(firstChar)) {
+ output = '\\3' + firstChar + ' ' + output.slice(1);
+ }
+ }
+
+ return output;
+}
+
+const regexKeyword = new RegExp(
+ [...genericFontFamilykeywords].concat(globalKeywords).join('|'),
+ 'i'
+);
+const regexInvalidIdentifier = /^(-?\d|--)/;
+const regexSpaceAtStart = /^\x20/;
+const regexWhitespace = /[\t\n\f\r\x20]/g;
+const regexIdentifierCharacter = /^[a-zA-Z\d\xa0-\uffff_-]+$/;
+const regexConsecutiveSpaces = /(\\(?:[a-fA-F0-9]{1,6}\x20|\x20))?(\x20{2,})/g;
+const regexTrailingEscape = /\\[a-fA-F0-9]{0,6}\x20$/;
+const regexTrailingSpace = /\x20$/;
+
+/**
+ * @param {string} string
+ * @return {string}
+ */
+function escapeIdentifierSequence(string) {
+ let identifiers = string.split(regexWhitespace);
+ let index = 0;
+ /** @type {string[] | string} */
+ let result = [];
+ let escapeResult;
+
+ while (index < identifiers.length) {
+ let subString = identifiers[index++];
+
+ if (subString === '') {
+ result.push(subString);
+ continue;
+ }
+
+ escapeResult = escape(subString, false);
+
+ if (regexIdentifierCharacter.test(subString)) {
+ // the font family name part consists of allowed characters exclusively
+ if (regexInvalidIdentifier.test(subString)) {
+ // the font family name part starts with two hyphens, a digit, or a
+ // hyphen followed by a digit
+ if (index === 1) {
+ // if this is the first item
+ result.push(escapeResult);
+ } else {
+ // if it’s not the first item, we can simply escape the space
+ // between the two identifiers to merge them into a single
+ // identifier rather than escaping the start characters of the
+ // second identifier
+ result[index - 2] += '\\';
+ result.push(escape(subString, true));
+ }
+ } else {
+ // the font family name part doesn’t start with two hyphens, a digit,
+ // or a hyphen followed by a digit
+ result.push(escapeResult);
+ }
+ } else {
+ // the font family name part contains invalid identifier characters
+ result.push(escapeResult);
+ }
+ }
+
+ result = result.join(' ').replace(regexConsecutiveSpaces, ($0, $1, $2) => {
+ const spaceCount = $2.length;
+ const escapesNeeded = Math.floor(spaceCount / 2);
+ const array = makeArray('\\ ', escapesNeeded);
+
+ if (spaceCount % 2) {
+ array[escapesNeeded - 1] += '\\ ';
+ }
+
+ return ($1 || '') + ' ' + array.join(' ');
+ });
+
+ // Escape trailing spaces unless they’re already part of an escape
+ if (regexTrailingSpace.test(result) && !regexTrailingEscape.test(result)) {
+ result = result.replace(regexTrailingSpace, '\\ ');
+ }
+
+ if (regexSpaceAtStart.test(result)) {
+ result = '\\ ' + result.slice(1);
+ }
+
+ return result;
+}
+/**
+ * @param {import('postcss-value-parser').Node[]} nodes
+ * @param {import('../index').Options} opts
+ * @return {import('postcss-value-parser').WordNode[]}
+ */
+module.exports = function (nodes, opts) {
+ /** @type {import('postcss-value-parser').Node[]} */
+ const family = [];
+ /** @type {import('postcss-value-parser').WordNode | null} */
+ let last = null;
+ let i, max;
+
+ nodes.forEach((node, index, arr) => {
+ if (node.type === 'string' || node.type === 'function') {
+ family.push(node);
+ } else if (node.type === 'word') {
+ if (!last) {
+ last = /** @type {import('postcss-value-parser').WordNode} */ ({
+ type: 'word',
+ value: '',
+ });
+ family.push(last);
+ }
+
+ last.value += node.value;
+ } else if (node.type === 'space') {
+ if (last && index !== arr.length - 1) {
+ last.value += ' ';
+ }
+ } else {
+ last = null;
+ }
+ });
+
+ let normalizedFamilies = family.map((node) => {
+ if (node.type === 'string') {
+ const isKeyword = regexKeyword.test(node.value);
+
+ if (
+ !opts.removeQuotes ||
+ isKeyword ||
+ /[0-9]/.test(node.value.slice(0, 1))
+ ) {
+ return stringify(node);
+ }
+
+ let escaped = escapeIdentifierSequence(node.value);
+
+ if (escaped.length < node.value.length + 2) {
+ return escaped;
+ }
+ }
+
+ return stringify(node);
+ });
+
+ if (opts.removeAfterKeyword) {
+ for (i = 0, max = normalizedFamilies.length; i < max; i += 1) {
+ if (genericFontFamilykeywords.has(normalizedFamilies[i].toLowerCase())) {
+ normalizedFamilies = normalizedFamilies.slice(0, i + 1);
+ break;
+ }
+ }
+ }
+
+ if (opts.removeDuplicates) {
+ normalizedFamilies = uniqueFontFamilies(normalizedFamilies);
+ }
+
+ return [
+ /** @type {import('postcss-value-parser').WordNode} */ ({
+ type: 'word',
+ value: normalizedFamilies.join(),
+ }),
+ ];
+};