388 lines
10 KiB
JavaScript
388 lines
10 KiB
JavaScript
/*
|
|
* Reuters Graphics browser port of:
|
|
* Copyright 2020 American Express Travel Related Services Company, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
* in compliance with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing permissions and limitations under
|
|
* the License.
|
|
*/
|
|
|
|
import css from 'css';
|
|
|
|
function _defineProperty(obj, key, value) {
|
|
if (key in obj) {
|
|
Object.defineProperty(obj, key, {
|
|
value,
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true,
|
|
});
|
|
} else {
|
|
obj[key] = value;
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
function ownKeys(object, enumerableOnly) {
|
|
const keys = Object.keys(object);
|
|
|
|
if (Object.getOwnPropertySymbols) {
|
|
let symbols = Object.getOwnPropertySymbols(object);
|
|
if (enumerableOnly) {
|
|
symbols = symbols.filter(function (sym) {
|
|
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
});
|
|
}
|
|
keys.push.apply(keys, symbols);
|
|
}
|
|
|
|
return keys;
|
|
}
|
|
|
|
function _objectSpread2(target) {
|
|
for (let i = 1; i < arguments.length; i++) {
|
|
var source = arguments[i] != null ? arguments[i] : {};
|
|
|
|
if (i % 2) {
|
|
ownKeys(Object(source), true).forEach(function (key) {
|
|
_defineProperty(target, key, source[key]);
|
|
});
|
|
} else if (Object.getOwnPropertyDescriptors) {
|
|
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
} else {
|
|
ownKeys(Object(source)).forEach(function (key) {
|
|
Object.defineProperty(
|
|
target,
|
|
key,
|
|
Object.getOwnPropertyDescriptor(source, key)
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
function _slicedToArray(arr, i) {
|
|
return (
|
|
_arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest()
|
|
);
|
|
}
|
|
|
|
function _arrayWithHoles(arr) {
|
|
if (Array.isArray(arr)) return arr;
|
|
}
|
|
|
|
function _iterableToArrayLimit(arr, i) {
|
|
if (
|
|
!(
|
|
Symbol.iterator in Object(arr) ||
|
|
Object.prototype.toString.call(arr) === '[object Arguments]'
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const _arr = [];
|
|
let _n = true;
|
|
let _d = false;
|
|
let _e;
|
|
|
|
try {
|
|
for (
|
|
var _i = arr[Symbol.iterator](), _s;
|
|
!(_n = (_s = _i.next()).done);
|
|
_n = true
|
|
) {
|
|
_arr.push(_s.value);
|
|
|
|
if (i && _arr.length === i) break;
|
|
}
|
|
} catch (err) {
|
|
_d = true;
|
|
_e = err;
|
|
} finally {
|
|
try {
|
|
if (!_n && _i.return != null) _i.return();
|
|
} finally {
|
|
// eslint-disable-next-line
|
|
if (_d) throw _e;
|
|
}
|
|
}
|
|
|
|
return _arr;
|
|
}
|
|
|
|
function _nonIterableRest() {
|
|
throw new TypeError('Invalid attempt to destructure non-iterable instance');
|
|
}
|
|
|
|
const addProperty = function addProperty(obj, key, value) {
|
|
const retObj = obj;
|
|
|
|
if (retObj[key]) {
|
|
retObj[key] = _objectSpread2({}, retObj[key], {}, value);
|
|
} else {
|
|
retObj[key] = value;
|
|
}
|
|
|
|
return retObj;
|
|
};
|
|
|
|
const camelize = function camelize(str) {
|
|
return str.replace(/-([a-z])/g, function (g) {
|
|
return g[1].toUpperCase();
|
|
});
|
|
};
|
|
|
|
const capitalize = function capitalize(str) {
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
};
|
|
|
|
const sanitize = function sanitize(name) {
|
|
return name
|
|
.replace(/\*/g, 'all-children')
|
|
.replace(/#/g, '$')
|
|
.replace(/\s\s+/g, ' ')
|
|
.replace(/(\d)\\\/(\d)/g, '$1~$2') // Added for escaped slashes like ".w-1/2"
|
|
.replace(/(\d)\\\.(\d)/g, '$1|$2') // Added for escaped dots like ".mb-0\.5"
|
|
.replace(/[^a-zA-Z0-9$|~]/g, '_')
|
|
.replace(/^_+/g, '')
|
|
.replace(/_+$/g, '')
|
|
.replace('|', '.') // Replace placeholder
|
|
.replace('~', '/'); // Replace placeholder
|
|
};
|
|
|
|
const fontface = function fontface(rule, result) {
|
|
let name = '';
|
|
let obj = {};
|
|
const fontObj = {};
|
|
rule.declarations.forEach(function (declaration) {
|
|
const cssProperty = camelize(declaration.property);
|
|
fontObj[cssProperty] = declaration.value;
|
|
name = capitalize(camelize(fontObj.fontFamily).replace(/"/g, ''));
|
|
obj = {
|
|
'@font-face': fontObj,
|
|
};
|
|
});
|
|
let dupeFlag = false;
|
|
Object.keys(result).forEach(function (key) {
|
|
if (key.split('_')[0] === name) {
|
|
if (JSON.stringify(result[key]) === JSON.stringify(obj)) {
|
|
dupeFlag = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
if (!dupeFlag) {
|
|
const numVar = Object.entries(result).filter(function (resObj) {
|
|
return resObj[0].split('_')[0] === name;
|
|
}).length;
|
|
|
|
if (numVar > 0) {
|
|
name = ''.concat(name, '_').concat(numVar + 1);
|
|
}
|
|
|
|
name = sanitize(name);
|
|
return [name, obj];
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
const keyframes = function keyframes(rule) {
|
|
const keyFrameObj = {};
|
|
rule.keyframes.forEach(function (keyframe) {
|
|
keyframe.declarations.forEach(function (decl) {
|
|
keyFrameObj[keyframe.values[0]] = _objectSpread2(
|
|
{},
|
|
keyFrameObj[keyframe.values[0]],
|
|
_defineProperty({}, decl.property, decl.value)
|
|
);
|
|
});
|
|
});
|
|
let name = camelize('keyframes-'.concat(rule.name));
|
|
const obj = {};
|
|
obj['@keyframes '.concat(rule.name)] = keyFrameObj;
|
|
name = sanitize(name);
|
|
return [name, obj];
|
|
};
|
|
|
|
const standard = function standard(rule, result) {
|
|
const obj = {};
|
|
let retObj = {};
|
|
rule.declarations.forEach(function (declaration) {
|
|
const cssProperty = camelize(declaration.property);
|
|
obj[cssProperty] = declaration.value;
|
|
});
|
|
rule.selectors.forEach(function (selector) {
|
|
let name; // Check if selector contains a pseudo selector
|
|
|
|
const pseudoSelectorIndex = selector.indexOf(':');
|
|
|
|
if (pseudoSelectorIndex !== -1) {
|
|
// Find end of pseudo selector
|
|
let endPseudoSelectorIndex = selector.indexOf(' ', pseudoSelectorIndex);
|
|
if (endPseudoSelectorIndex === -1)
|
|
endPseudoSelectorIndex = selector.length; // Split selector
|
|
|
|
const primarySelector = selector.slice(0, pseudoSelectorIndex);
|
|
const pseudoSelector = selector.slice(
|
|
pseudoSelectorIndex,
|
|
endPseudoSelectorIndex
|
|
);
|
|
const secondarySelector = selector.slice(
|
|
endPseudoSelectorIndex,
|
|
selector.length
|
|
);
|
|
const pseudoObj = {};
|
|
pseudoObj['&'.concat(pseudoSelector).concat(secondarySelector)] = obj;
|
|
name = sanitize(primarySelector.trim());
|
|
retObj = addProperty(result, name, pseudoObj);
|
|
} else {
|
|
name = sanitize(selector.trim());
|
|
retObj = addProperty(result, name, obj);
|
|
}
|
|
});
|
|
return retObj;
|
|
};
|
|
|
|
const convertRules = function convertRules(rules) {
|
|
const res =
|
|
arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
let result = res;
|
|
rules.forEach(function (rule) {
|
|
if (rule.type === 'media') {
|
|
// Convert @media rules
|
|
const name = '@media '.concat(rule.media);
|
|
result[name] = result[name] || {};
|
|
const media = result[name];
|
|
convertRules(rule.rules, media);
|
|
} else if (rule.type === 'font-face') {
|
|
// Convert @font-face rules
|
|
const fontProp = fontface(rule, result);
|
|
if (fontProp) result = addProperty(result, fontProp[0], fontProp[1]);
|
|
} else if (rule.type === 'keyframes') {
|
|
// Convert @keyframes rules
|
|
const keyProp = keyframes(rule);
|
|
result = addProperty(result, keyProp[0], keyProp[1]);
|
|
} else if (rule.type === 'rule') {
|
|
// Convert standard CSS rules
|
|
const standardProp = standard(rule, result);
|
|
Object.entries(standardProp).forEach(function (_ref) {
|
|
const _ref2 = _slicedToArray(_ref, 2);
|
|
const key = _ref2[0];
|
|
const value = _ref2[1];
|
|
|
|
result = addProperty(result, key, value);
|
|
});
|
|
}
|
|
});
|
|
return result;
|
|
};
|
|
|
|
const convertToJS = function convertToJS(input) {
|
|
// Parse CSS string into rules array
|
|
try {
|
|
const parsedCss = css.parse(input);
|
|
const rules = parsedCss.stylesheet.rules;
|
|
return convertRules(rules);
|
|
} catch (err) {
|
|
throw new Error('Invalid CSS input: '.concat(err));
|
|
}
|
|
};
|
|
|
|
const reverseMediaQueries = function reverseMediaQueries(inputData) {
|
|
const exportObject = {};
|
|
|
|
const moveMediaInsideClass = function moveMediaInsideClass(object) {
|
|
const media =
|
|
arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
Object.entries(object).forEach(function (_ref) {
|
|
const _ref2 = _slicedToArray(_ref, 2);
|
|
const key = _ref2[0];
|
|
const value = _ref2[1];
|
|
|
|
if (key.includes('@media')) {
|
|
moveMediaInsideClass(object[key], key);
|
|
} else if (media) {
|
|
const tempObj = {};
|
|
tempObj[media] = value;
|
|
|
|
if (exportObject[key]) {
|
|
exportObject[key] = _objectSpread2(
|
|
{},
|
|
exportObject[key],
|
|
{},
|
|
tempObj
|
|
);
|
|
} else {
|
|
exportObject[key] = tempObj;
|
|
}
|
|
} else if (exportObject[key]) {
|
|
exportObject[key] = _objectSpread2({}, exportObject[key], {}, value);
|
|
} else {
|
|
exportObject[key] = value;
|
|
}
|
|
});
|
|
};
|
|
|
|
moveMediaInsideClass(inputData);
|
|
return exportObject;
|
|
};
|
|
|
|
const convertStringToJson = function convertStringToJson(input, mediaReverse) {
|
|
let contents = convertToJS(input);
|
|
|
|
if (mediaReverse) {
|
|
contents = reverseMediaQueries(contents);
|
|
}
|
|
|
|
return {
|
|
contents,
|
|
};
|
|
};
|
|
|
|
const convert = function convert(input) {
|
|
const config =
|
|
arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
const outputType = config.outputType;
|
|
const _outputPath = config.outputPath;
|
|
let mediaReverse = config.mediaReverse;
|
|
let convertedCss;
|
|
|
|
if (outputType === 'splitFile' || outputType === 'shakeFile') {
|
|
mediaReverse = true;
|
|
} // If input is a String of CSS
|
|
|
|
convertedCss = convertStringToJson(input, mediaReverse);
|
|
|
|
if (!outputType) {
|
|
if (Array.isArray(convertedCss)) {
|
|
return convertedCss.map(function (obj) {
|
|
return obj.contents;
|
|
});
|
|
} else {
|
|
return convertedCss.contents;
|
|
}
|
|
} else {
|
|
const writeRecur = function writeRecur(input) {
|
|
if (Array.isArray(input)) {
|
|
input.forEach(function (obj) {
|
|
writeRecur(obj);
|
|
});
|
|
}
|
|
};
|
|
|
|
writeRecur(convertedCss);
|
|
}
|
|
};
|
|
|
|
export { convert };
|