flow like the river
This commit is contained in:
commit
013fe673f3
42435 changed files with 5764238 additions and 0 deletions
173
BACK_BACK/node_modules/uncss/src/jsdom.js
generated
vendored
Executable file
173
BACK_BACK/node_modules/uncss/src/jsdom.js
generated
vendored
Executable file
|
|
@ -0,0 +1,173 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs'),
|
||||
isHTML = require('is-html'),
|
||||
isURL = require('is-absolute-url'),
|
||||
{ JSDOM, ResourceLoader, VirtualConsole } = require('jsdom'),
|
||||
path = require('path'),
|
||||
{ Console } = require('console'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Jsdom expects promises returned by ResourceLoader.fetch to have an 'abort' method.
|
||||
* @param {Promise} promise The promise to augment.
|
||||
*/
|
||||
function makeResourcePromise(promise) {
|
||||
promise.abort = () => { /* noop */ };
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
class CustomResourcesLoader extends ResourceLoader {
|
||||
constructor(htmlroot, strictSSL, userAgent) {
|
||||
super({
|
||||
strictSSL,
|
||||
userAgent
|
||||
});
|
||||
|
||||
// The htmlroot option allows root-relative URLs (starting with a slash)
|
||||
// to be used for all resources. Without it, root-relative URLs are
|
||||
// looked up relative to file://, so will not be found.
|
||||
this.htmlroot = htmlroot || '';
|
||||
}
|
||||
|
||||
fetch(originalUrl, options) {
|
||||
const element = options && options.element;
|
||||
if (!element) {
|
||||
// HTTP request?
|
||||
return super.fetch(originalUrl, options);
|
||||
}
|
||||
|
||||
if (!element || element.nodeName !== 'SCRIPT') {
|
||||
// Only scripts need to be fetched. Stylesheets are read later by uncss.
|
||||
return makeResourcePromise(Promise.resolve(Buffer.from('')));
|
||||
}
|
||||
|
||||
// See whether raw attribute value is root-relative.
|
||||
const src = element.getAttribute('src');
|
||||
if (src && path.isAbsolute(src)) {
|
||||
const url = path.join(this.htmlroot, src);
|
||||
|
||||
return makeResourcePromise(new Promise((resolve, reject) => {
|
||||
try {
|
||||
const buffer = fs.readFileSync(url);
|
||||
|
||||
resolve(buffer);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return super.fetch(originalUrl, options);
|
||||
}
|
||||
}
|
||||
|
||||
function defaultOptions() {
|
||||
return {
|
||||
features: {
|
||||
FetchExternalResources: ['script'],
|
||||
ProcessExternalResources: ['script']
|
||||
},
|
||||
runScripts: 'dangerously',
|
||||
userAgent: 'uncss',
|
||||
virtualConsole: new VirtualConsole().sendTo(new Console(process.stderr))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a page.
|
||||
* @param {String} src
|
||||
* @param {Object} options
|
||||
* @return {Promise<JSDOM>}
|
||||
*/
|
||||
function fromSource(src, options) {
|
||||
const config = _.cloneDeep(options.jsdom);
|
||||
|
||||
config.resources = new CustomResourcesLoader(options.htmlroot, options.strictSSL, options.userAgent);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let pagePromise;
|
||||
if (isURL(src)) {
|
||||
pagePromise = JSDOM.fromURL(src, config);
|
||||
} else if (isHTML(src)) {
|
||||
pagePromise = Promise.resolve(new JSDOM(src, config));
|
||||
} else {
|
||||
pagePromise = JSDOM.fromFile(src, config);
|
||||
}
|
||||
|
||||
return pagePromise.then((page) => {
|
||||
if (options.inject) {
|
||||
if (typeof options.inject === 'function') {
|
||||
options.inject(page.window);
|
||||
} else {
|
||||
require(path.join(__dirname, options.inject))(page.window);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => resolve(page), options.timeout);
|
||||
}).catch((e) => {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract stylesheets' hrefs from dom
|
||||
* @param {Object} window A jsdom window
|
||||
* @param {Object} options Options, as passed to UnCSS
|
||||
* @return {Array}
|
||||
*/
|
||||
function getStylesheets(window, options) {
|
||||
if (Array.isArray(options.media) === false) {
|
||||
options.media = [options.media];
|
||||
}
|
||||
|
||||
const media = _.union(['', 'all', 'screen'], options.media);
|
||||
const elements = window.document.querySelectorAll('link[rel="stylesheet"]');
|
||||
|
||||
return Array.prototype.map
|
||||
.call(elements, (link) => ({
|
||||
href: link.getAttribute('href'),
|
||||
media: link.getAttribute('media') || ''
|
||||
}))
|
||||
.filter((sheet) => media.indexOf(sheet.media) !== -1)
|
||||
.map((sheet) => sheet.href);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter unused selectors.
|
||||
* @param {Object} window A jsdom window
|
||||
* @param {Array} sels List of selectors to be filtered
|
||||
* @return {Array}
|
||||
*/
|
||||
function findAll(window, sels) {
|
||||
const document = window.document;
|
||||
|
||||
// Unwrap noscript elements.
|
||||
const elements = document.getElementsByTagName('noscript');
|
||||
Array.prototype.forEach.call(elements, (ns) => {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.innerHTML = ns.textContent;
|
||||
// Insert each child of the <noscript> as its sibling
|
||||
Array.prototype.forEach.call(wrapper.children, (child) => {
|
||||
ns.parentNode.insertBefore(child, ns);
|
||||
});
|
||||
});
|
||||
|
||||
// Do the filtering.
|
||||
return sels.filter((selector) => {
|
||||
try {
|
||||
return document.querySelector(selector);
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
defaultOptions,
|
||||
fromSource,
|
||||
findAll,
|
||||
getStylesheets
|
||||
};
|
||||
255
BACK_BACK/node_modules/uncss/src/lib.js
generated
vendored
Executable file
255
BACK_BACK/node_modules/uncss/src/lib.js
generated
vendored
Executable file
|
|
@ -0,0 +1,255 @@
|
|||
'use strict';
|
||||
|
||||
const jsdom = require('./jsdom.js'),
|
||||
postcss = require('postcss'),
|
||||
postcssSelectorParser = require('postcss-selector-parser'),
|
||||
_ = require('lodash');
|
||||
/* Some styles are applied only with user interaction, and therefore its
|
||||
* selectors cannot be used with querySelectorAll.
|
||||
* http://www.w3.org/TR/2001/CR-css3-selectors-20011113/
|
||||
*/
|
||||
const dePseudify = (function () {
|
||||
const ignoredPseudos = [
|
||||
/* link */
|
||||
':link', ':visited',
|
||||
/* user action */
|
||||
':hover', ':active', ':focus', ':focus-within',
|
||||
/* UI element states */
|
||||
':enabled', ':disabled', ':checked', ':indeterminate',
|
||||
/* form validation */
|
||||
':required', ':invalid', ':valid',
|
||||
/* pseudo elements */
|
||||
'::first-line', '::first-letter', '::selection', '::before', '::after',
|
||||
/* pseudo classes */
|
||||
':target',
|
||||
/* CSS2 pseudo elements */
|
||||
':before', ':after',
|
||||
/* Vendor-specific pseudo-elements:
|
||||
* https://developer.mozilla.org/ja/docs/Glossary/Vendor_Prefix
|
||||
*/
|
||||
'::?-(?:moz|ms|webkit|o)-[a-z0-9-]+'
|
||||
],
|
||||
// Actual regex is of the format: /^(:hover|:focus|...)$/i
|
||||
pseudosRegex = new RegExp('^(' + ignoredPseudos.join('|') + ')$', 'i');
|
||||
|
||||
function transform (selectors) {
|
||||
selectors.walkPseudos((selector) => {
|
||||
if (pseudosRegex.test(selector.value)) {
|
||||
selector.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const processor = postcssSelectorParser(transform);
|
||||
|
||||
return function (selector) {
|
||||
return processor.processSync(selector);
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Private function used in filterUnusedRules.
|
||||
* @param {Array} selectors CSS selectors created by the CSS parser
|
||||
* @param {Array} ignore List of selectors to be ignored
|
||||
* @param {Array} usedSelectors List of Selectors found in the jsdom pages
|
||||
* @return {Array} The selectors matched in the DOMs
|
||||
*/
|
||||
function filterUnusedSelectors(selectors, ignore, usedSelectors) {
|
||||
/* There are some selectors not supported for matching, like
|
||||
* :before, :after
|
||||
* They should be removed only if the parent is not found.
|
||||
* Example: '.clearfix:before' should be removed only if there
|
||||
* is no '.clearfix'
|
||||
*/
|
||||
return selectors.filter((selector) => {
|
||||
selector = dePseudify(selector);
|
||||
/* TODO: process @-rules */
|
||||
if (selector[0] === '@') {
|
||||
return true;
|
||||
}
|
||||
for (let i = 0, len = ignore.length; i < len; ++i) {
|
||||
if (_.isRegExp(ignore[i]) && ignore[i].test(selector)) {
|
||||
return true;
|
||||
}
|
||||
if (/:\w+/.test(ignore[i])) {
|
||||
const ignored = dePseudify(ignore[i]);
|
||||
if (ignored === selector) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (ignore[i] === selector) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return usedSelectors.indexOf(selector) !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter @keyframes that are not used
|
||||
* @param {Object} css The postcss.Root node
|
||||
* @param {Array} animations
|
||||
* @param {Array} unusedRules
|
||||
* @return {Array}
|
||||
*/
|
||||
function filterKeyframes(css, unusedRules) {
|
||||
const usedAnimations = [];
|
||||
css.walkDecls((decl) => {
|
||||
if (_.endsWith(decl.prop, 'animation-name')) {
|
||||
/* Multiple animations, separated by comma */
|
||||
usedAnimations.push(...postcss.list.comma(decl.value));
|
||||
} else if (_.endsWith(decl.prop, 'animation')) {
|
||||
/* Support multiple animations */
|
||||
postcss.list.comma(decl.value).forEach((anim) => {
|
||||
/* If declared as animation, name can be anywhere in the string; so we include all the properties */
|
||||
usedAnimations.push(...postcss.list.space(anim));
|
||||
});
|
||||
}
|
||||
});
|
||||
const usedAnimationsSet = new Set(usedAnimations);
|
||||
css.walkAtRules(/keyframes$/, (atRule) => {
|
||||
if (!usedAnimationsSet.has(atRule.params)) {
|
||||
unusedRules.push(atRule);
|
||||
atRule.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter rules with no selectors remaining
|
||||
* @param {Object} css The postcss.Root node
|
||||
* @return {Array}
|
||||
*/
|
||||
function filterEmptyAtRules(css) {
|
||||
/* Filter media queries with no remaining rules */
|
||||
css.walkAtRules((atRule) => {
|
||||
if (atRule.name === 'media' && atRule.nodes.length === 0) {
|
||||
atRule.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find which selectors are used in {pages}
|
||||
* @param {Array} page List of jsdom pages
|
||||
* @param {Object} css The postcss.Root node
|
||||
* @return {Promise}
|
||||
*/
|
||||
function getUsedSelectors(page, css) {
|
||||
let usedSelectors = [];
|
||||
css.walkRules((rule) => {
|
||||
usedSelectors = _.concat(usedSelectors, rule.selectors.map(dePseudify));
|
||||
});
|
||||
|
||||
return jsdom.findAll(page.window, usedSelectors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the selectors mentioned in {css}
|
||||
* @param {Object} css The postcss.Root node
|
||||
* @return {Array}
|
||||
*/
|
||||
function getAllSelectors(css) {
|
||||
let selectors = [];
|
||||
css.walkRules((rule) => {
|
||||
selectors = _.concat(selectors, rule.selector);
|
||||
});
|
||||
return selectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove css rules not used in the dom
|
||||
* @param {Array} pages List of jsdom pages
|
||||
* @param {Object} css The postcss.Root node
|
||||
* @param {Array} ignore List of selectors to be ignored
|
||||
* @param {Array} usedSelectors List of selectors that are found in {pages}
|
||||
* @return {Object} A css_parse-compatible stylesheet
|
||||
*/
|
||||
function filterUnusedRules(css, ignore, usedSelectors) {
|
||||
let ignoreNextRule = false,
|
||||
ignoreNextRulesStart = false,
|
||||
unusedRules = [],
|
||||
unusedRuleSelectors,
|
||||
usedRuleSelectors;
|
||||
/* Rule format:
|
||||
* { selectors: [ '...', '...' ],
|
||||
* declarations: [ { property: '...', value: '...' } ]
|
||||
* },.
|
||||
* Two steps: filter the unused selectors for each rule,
|
||||
* filter the rules with no selectors
|
||||
*/
|
||||
ignoreNextRule = false;
|
||||
css.walk((rule) => {
|
||||
if (rule.type === 'comment') {
|
||||
if (/^!?\s?uncss:ignore start\s?$/.test(rule.text)) { // ignore next rules while using comment `/* uncss:ignore start */`
|
||||
ignoreNextRulesStart = true;
|
||||
} else if (/^!?\s?uncss:ignore end\s?$/.test(rule.text)) { // until `/* uncss:ignore end */` was found
|
||||
ignoreNextRulesStart = false;
|
||||
} else if (/^!?\s?uncss:ignore\s?$/.test(rule.text)) { // ignore next rule while using comment `/* uncss:ignore */`
|
||||
ignoreNextRule = true;
|
||||
}
|
||||
} else if (rule.type === 'rule') {
|
||||
if (rule.parent.type === 'atrule' && _.endsWith(rule.parent.name, 'keyframes')) {
|
||||
// Don't remove animation keyframes that have selector names of '30%' or 'to'
|
||||
return;
|
||||
}
|
||||
if (ignoreNextRulesStart) {
|
||||
ignore = ignore.concat(rule.selectors);
|
||||
} else if (ignoreNextRule) {
|
||||
ignoreNextRule = false;
|
||||
ignore = ignore.concat(rule.selectors);
|
||||
}
|
||||
|
||||
usedRuleSelectors = filterUnusedSelectors(
|
||||
rule.selectors,
|
||||
ignore,
|
||||
usedSelectors
|
||||
);
|
||||
unusedRuleSelectors = rule.selectors.filter((selector) => usedRuleSelectors.indexOf(selector) < 0);
|
||||
if (unusedRuleSelectors && unusedRuleSelectors.length) {
|
||||
unusedRules.push({
|
||||
type: 'rule',
|
||||
selectors: unusedRuleSelectors,
|
||||
position: rule.source
|
||||
});
|
||||
}
|
||||
if (usedRuleSelectors.length === 0) {
|
||||
rule.remove();
|
||||
} else {
|
||||
rule.selectors = usedRuleSelectors;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Filter the @media rules with no rules */
|
||||
filterEmptyAtRules(css);
|
||||
|
||||
/* Filter unused @keyframes */
|
||||
filterKeyframes(css, unusedRules);
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main exposed function
|
||||
* @param {Array} pages List of jsdom pages
|
||||
* @param {Object} css The postcss.Root node
|
||||
* @param {Array} ignore List of selectors to be ignored
|
||||
* @return {Promise}
|
||||
*/
|
||||
module.exports = function uncss(pages, css, ignore) {
|
||||
return Promise.all(pages.map((page) => getUsedSelectors(page, css)))
|
||||
.then((usedSelectors) => {
|
||||
usedSelectors = _.flatten(usedSelectors);
|
||||
const filteredCss = filterUnusedRules(css, ignore, usedSelectors);
|
||||
const allSelectors = getAllSelectors(css);
|
||||
return [filteredCss, {
|
||||
/* Get the selectors for the report */
|
||||
all: allSelectors,
|
||||
unused: _.difference(allSelectors, usedSelectors),
|
||||
used: usedSelectors
|
||||
}];
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.dePseudify = dePseudify;
|
||||
260
BACK_BACK/node_modules/uncss/src/uncss.js
generated
vendored
Executable file
260
BACK_BACK/node_modules/uncss/src/uncss.js
generated
vendored
Executable file
|
|
@ -0,0 +1,260 @@
|
|||
'use strict';
|
||||
|
||||
const glob = require('glob'),
|
||||
isHTML = require('is-html'),
|
||||
isURL = require('is-absolute-url'),
|
||||
jsdom = require('./jsdom.js'),
|
||||
postcss = require('postcss'),
|
||||
uncss = require('./lib.js'),
|
||||
utility = require('./utility.js'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Get the contents of HTML pages through jsdom.
|
||||
* @param {Array} files List of HTML files
|
||||
* @param {Object} options UnCSS options
|
||||
* @return {Array|Promise}
|
||||
*/
|
||||
function getHTML(files, options) {
|
||||
if (_.isString(files)) {
|
||||
files = [files];
|
||||
}
|
||||
|
||||
files = _.flatten(files.map((file) => {
|
||||
if (!isURL(file) && !isHTML(file)) {
|
||||
return glob.sync(file);
|
||||
}
|
||||
return file;
|
||||
}));
|
||||
|
||||
if (!files.length) {
|
||||
return Promise.reject(new Error('UnCSS: no HTML files found'));
|
||||
}
|
||||
|
||||
// Save files for later reference.
|
||||
options.files = files;
|
||||
return Promise.all(files.map((file) => jsdom.fromSource(file, options)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of CSS files.
|
||||
* @param {Array} files List of HTML files
|
||||
* @param {Object} options UnCSS options
|
||||
* @param {Array} pages Pages opened by jsdom
|
||||
* @return {Promise}
|
||||
*/
|
||||
function getStylesheets(files, options, pages) {
|
||||
if (options.stylesheets && options.stylesheets.length) {
|
||||
/* Simulate the behavior below */
|
||||
return Promise.resolve([files, options, pages, [options.stylesheets]]);
|
||||
}
|
||||
/* Extract the stylesheets from the HTML */
|
||||
return Promise.all(pages.map((page) => jsdom.getStylesheets(page.window, options)))
|
||||
.then((stylesheets) => [files, options, pages, stylesheets]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of CSS files.
|
||||
* @param {Array} files List of HTML files
|
||||
* @param {Object} options UnCSS options
|
||||
* @param {Array} pages Pages opened by jsdom
|
||||
* @param {Array} stylesheets List of CSS files
|
||||
* @return {Array}
|
||||
*/
|
||||
function getCSS([files, options, pages, stylesheets]) {
|
||||
/* Ignore specified stylesheets */
|
||||
if (options.ignoreSheets.length) {
|
||||
stylesheets = stylesheets
|
||||
.map((arr) => {
|
||||
return arr.filter((sheet) => {
|
||||
return _.every(options.ignoreSheets, (ignore) => {
|
||||
if (_.isRegExp(ignore)) {
|
||||
return !ignore.test(sheet);
|
||||
}
|
||||
return sheet !== ignore;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (_.flatten(stylesheets).length) {
|
||||
/* Only run this if we found links to stylesheets (there may be none...)
|
||||
* files = ['some_file.html', 'some_other_file.html']
|
||||
* stylesheets = [['relative_css_path.css', ...],
|
||||
* ['maybe_a_duplicate.css', ...]]
|
||||
* We need to - make the stylesheets' paths relative to the HTML files,
|
||||
* - flatten the array,
|
||||
* - remove duplicates
|
||||
*/
|
||||
stylesheets =
|
||||
_.chain(stylesheets)
|
||||
.map((sheets, i) => utility.parsePaths(files[i], sheets, options))
|
||||
.flatten()
|
||||
.uniq()
|
||||
.value();
|
||||
} else {
|
||||
/* Reset the array if we didn't find any link tags */
|
||||
stylesheets = [];
|
||||
}
|
||||
return Promise.all([options, pages, utility.readStylesheets(stylesheets, options.banner)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the actual work
|
||||
* @param {Array} files List of HTML files
|
||||
* @param {Object} options UnCSS options
|
||||
* @param {Array} pages Pages opened by jsdom
|
||||
* @param {Array} stylesheets List of CSS files
|
||||
* @return {Promise}
|
||||
*/
|
||||
function processWithTextApi([options, pages, stylesheets]) {
|
||||
/* If we specified a raw string of CSS, add it to the stylesheets array */
|
||||
if (options.raw) {
|
||||
if (_.isString(options.raw)) {
|
||||
stylesheets.push(options.raw);
|
||||
} else {
|
||||
throw new Error('UnCSS: options.raw - expected a string');
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, there isn't any point in running the rest of the task if:
|
||||
* - We didn't specify any stylesheet links in the options object
|
||||
* - We couldn't find any stylesheet links in the HTML itself
|
||||
* - We weren't passed a string of raw CSS in addition to, or to replace
|
||||
* either of the above
|
||||
*/
|
||||
if (!_.flatten(stylesheets).length) {
|
||||
throw new Error('UnCSS: no stylesheets found');
|
||||
}
|
||||
|
||||
/* OK, so we have some CSS to work with!
|
||||
* Three steps:
|
||||
* - Parse the CSS
|
||||
* - Remove the unused rules
|
||||
* - Return the optimized CSS as a string
|
||||
*/
|
||||
const cssStr = stylesheets.join(' \n');
|
||||
let pcss,
|
||||
report;
|
||||
|
||||
try {
|
||||
pcss = postcss.parse(cssStr);
|
||||
} catch (err) {
|
||||
/* Try and construct a helpful error message */
|
||||
throw utility.parseErrorMessage(err, cssStr);
|
||||
}
|
||||
return uncss(pages, pcss, options.ignore).then(([css, rep]) => {
|
||||
let newCssStr = '';
|
||||
postcss.stringify(css, (result) => {
|
||||
newCssStr += result;
|
||||
});
|
||||
|
||||
if (options.report) {
|
||||
report = {
|
||||
original: cssStr,
|
||||
selectors: rep
|
||||
};
|
||||
}
|
||||
return [newCssStr, report];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Main exposed function.
|
||||
* Here we check the options and callback, then run the files through jsdom.
|
||||
* @param {Array} files Array of filenames
|
||||
* @param {Object} [options] options
|
||||
* @param {Function} callback(Error, String, Object)
|
||||
*/
|
||||
function init(files, options, callback) {
|
||||
|
||||
if (_.isFunction(options)) {
|
||||
/* There were no options, this argument is actually the callback */
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (!_.isFunction(callback)) {
|
||||
throw new TypeError('UnCSS: expected a callback');
|
||||
}
|
||||
|
||||
/* Try and read options from the specified uncssrc file */
|
||||
if (options.uncssrc) {
|
||||
try {
|
||||
/* Manually-specified options take precedence over uncssrc options */
|
||||
options = _.merge(utility.parseUncssrc(options.uncssrc), options);
|
||||
} catch (err) {
|
||||
if (err instanceof SyntaxError) {
|
||||
callback(new SyntaxError('UnCSS: uncssrc file is invalid JSON.'));
|
||||
return;
|
||||
}
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assign default values to options, unless specified */
|
||||
options = _.merge({
|
||||
banner: true,
|
||||
csspath: '',
|
||||
html: files,
|
||||
htmlRoot: null,
|
||||
ignore: [],
|
||||
ignoreSheets: [],
|
||||
inject: null,
|
||||
jsdom: jsdom.defaultOptions(),
|
||||
media: [],
|
||||
raw: null,
|
||||
report: false,
|
||||
stylesheets: null,
|
||||
timeout: 0,
|
||||
uncssrc: null,
|
||||
userAgent: 'uncss'
|
||||
}, options);
|
||||
|
||||
process(options).then(([css, report]) => callback(null, css, report), callback);
|
||||
}
|
||||
|
||||
function processAsPostCss(options, pages) {
|
||||
return uncss(pages, options.rawPostCss, options.ignore);
|
||||
}
|
||||
|
||||
function process(opts) {
|
||||
return getHTML(opts.html, opts).then((pages) => {
|
||||
function cleanup (result) {
|
||||
pages.forEach((page) => page.window.close());
|
||||
return result;
|
||||
}
|
||||
|
||||
if (opts.usePostCssInternal) {
|
||||
return processAsPostCss(opts, pages)
|
||||
.then(cleanup);
|
||||
}
|
||||
|
||||
return getStylesheets(opts.files, opts, pages)
|
||||
.then(getCSS)
|
||||
.then(processWithTextApi)
|
||||
.then(cleanup);
|
||||
});
|
||||
}
|
||||
|
||||
const postcssPlugin = postcss.plugin('uncss', (opts) => {
|
||||
let options = _.merge({
|
||||
usePostCssInternal: true,
|
||||
// Ignore stylesheets in the HTML files; only use those from the stream
|
||||
ignoreSheets: [/\s*/],
|
||||
html: [],
|
||||
ignore: [],
|
||||
jsdom: jsdom.defaultOptions()
|
||||
}, opts);
|
||||
|
||||
return function (css, result) { // eslint-disable-line no-unused-vars
|
||||
options = _.merge(options, {
|
||||
// This is used to pass the css object in to processAsPostCSS
|
||||
rawPostCss: css
|
||||
});
|
||||
|
||||
return process(options);
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = init;
|
||||
module.exports.postcssPlugin = postcssPlugin;
|
||||
205
BACK_BACK/node_modules/uncss/src/utility.js
generated
vendored
Executable file
205
BACK_BACK/node_modules/uncss/src/utility.js
generated
vendored
Executable file
|
|
@ -0,0 +1,205 @@
|
|||
'use strict';
|
||||
|
||||
const isHTML = require('is-html'),
|
||||
isURL = require('is-absolute-url'),
|
||||
request = require('request'),
|
||||
fs = require('fs'),
|
||||
os = require('os'),
|
||||
path = require('path'),
|
||||
url = require('url');
|
||||
|
||||
function isWindows() {
|
||||
return os.platform() === 'win32';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the supplied string might be a RegExp and, if so, return the corresponding RegExp.
|
||||
* @param {String} str The regex to transform.
|
||||
* @return {RegExp|String} The final RegExp
|
||||
*/
|
||||
function strToRegExp(str) {
|
||||
if (str[0] === '/') {
|
||||
return new RegExp(str.replace(/^\/|\/$/g, ''));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a given uncssrc file.
|
||||
* @param {String} filename The location of the uncssrc file
|
||||
* @return {Object} The options object
|
||||
*/
|
||||
function parseUncssrc(filename) {
|
||||
let options = JSON.parse(fs.readFileSync(filename, 'utf-8'));
|
||||
|
||||
/* RegExps can't be stored as JSON, therefore we need to parse them manually.
|
||||
* A string is a RegExp if it starts with '/', since that wouldn't be a valid CSS selector.
|
||||
*/
|
||||
options.ignore = options.ignore ? options.ignore.map(strToRegExp) : undefined;
|
||||
options.ignoreSheets = options.ignoreSheets ? options.ignoreSheets.map(strToRegExp) : [];
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse paths relatives to a source.
|
||||
* @param {String} source Where the paths originate from
|
||||
* @param {Array} stylesheets List of paths
|
||||
* @param {Object} options Options, as passed to UnCSS
|
||||
* @return {Array} List of paths
|
||||
*/
|
||||
function parsePaths(source, stylesheets, options) {
|
||||
return stylesheets.map((sheet) => {
|
||||
let sourceProtocol;
|
||||
const isLocalFile = sheet.substr(0, 5) === 'file:';
|
||||
|
||||
if (sheet.substr(0, 4) === 'http') {
|
||||
/* No need to parse, it's already a valid path */
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/* Check if we are fetching over http(s) */
|
||||
if (isURL(source) && !isLocalFile) {
|
||||
sourceProtocol = url.parse(source).protocol;
|
||||
|
||||
if (sheet.substr(0, 2) === '//') {
|
||||
/* Use the same protocol we used for fetching this page.
|
||||
* Default to http.
|
||||
*/
|
||||
return sourceProtocol ? sourceProtocol + sheet : 'http:' + sheet;
|
||||
}
|
||||
return url.resolve(source, sheet);
|
||||
}
|
||||
|
||||
/* We are fetching local files
|
||||
* Should probably report an error if we find an absolute path and
|
||||
* have no htmlroot specified.
|
||||
*/
|
||||
/* Fix the case when there is a query string or hash */
|
||||
sheet = sheet.split('?')[0].split('#')[0];
|
||||
|
||||
/* Path already parsed by jsdom or user supplied local file */
|
||||
if (isLocalFile) {
|
||||
sheet = url.parse(sheet).path.replace('%20', ' ');
|
||||
/* If on windows, remove first '/' */
|
||||
sheet = isWindows() ? sheet.substring(1) : sheet;
|
||||
|
||||
if (options.htmlroot) {
|
||||
return path.join(options.htmlroot, sheet);
|
||||
}
|
||||
sheet = path.relative(path.join(path.dirname(source)), sheet);
|
||||
}
|
||||
|
||||
if (sheet[0] === '/' && options.htmlroot) {
|
||||
return path.join(options.htmlroot, sheet);
|
||||
} else if (isHTML(source)) {
|
||||
return path.join(options.csspath, sheet);
|
||||
}
|
||||
return path.join(path.dirname(source), options.csspath, sheet);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an UTF8 string, return a version of this string without the first byte if it corresponds
|
||||
* to the Byte Order Mark
|
||||
* @param {String} utf8String the string to strip
|
||||
* @return {String}
|
||||
*/
|
||||
function stripBom(utf8String) {
|
||||
if (utf8String.charCodeAt(0) === 0xFEFF) {
|
||||
return utf8String.substr(1);
|
||||
}
|
||||
return utf8String;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of filenames, return an array of the files' contents,
|
||||
* only if the filename matches a regex
|
||||
* @param {Array} files An array of the filenames to read
|
||||
* @return {Promise}
|
||||
*/
|
||||
function readStylesheets(files, outputBanner) {
|
||||
return Promise.all(files.map((filename) => {
|
||||
if (isURL(filename)) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request({
|
||||
url: filename,
|
||||
headers: { 'User-Agent': 'UnCSS' }
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve(body);
|
||||
});
|
||||
});
|
||||
} else if (fs.existsSync(filename)) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(filename, 'utf-8', (err, contents) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve(stripBom(contents));
|
||||
});
|
||||
});
|
||||
}
|
||||
throw new Error(`UnCSS: could not open ${path.join(process.cwd(), filename)}`);
|
||||
})).then((res) => {
|
||||
// res is an array of the content of each file in files (in the same order)
|
||||
if (outputBanner) {
|
||||
for (let i = 0, len = files.length; i < len; i++) {
|
||||
// We append a small banner to keep track of which file we are currently processing
|
||||
// super helpful for debugging
|
||||
const banner = `/*** uncss> filename: ${files[i].replace(/\\/g, '/')} ***/\n`;
|
||||
res[i] = banner + res[i];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
function parseErrorMessage(error, cssStr) {
|
||||
/* TODO: FIXME */
|
||||
/* Base line for conveying the line number in the error message */
|
||||
let zeroLine = 0;
|
||||
|
||||
if (error.line) {
|
||||
const lines = cssStr.split('\n');
|
||||
if (lines.length) {
|
||||
/* We get the filename of the css file that contains the error */
|
||||
let i = error.line - 1;
|
||||
while (i >= 0 && !error.filename) {
|
||||
if (lines[i].substr(0, 21) === '/*** uncss> filename:') {
|
||||
error.filename = lines[i].substring(22, lines[i].length - 4);
|
||||
zeroLine = i;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
for (let j = error.line - 6; j < error.line + 5; j++) {
|
||||
if (j - zeroLine < 0 || j >= lines.length) {
|
||||
continue;
|
||||
}
|
||||
let line = lines[j];
|
||||
/* It could be minified CSS */
|
||||
if (line.length > 120 && error.column) {
|
||||
line = line.substring(error.column - 40, error.column);
|
||||
}
|
||||
error.message += '\n\t' + (j + 1 - zeroLine) + ': ';
|
||||
error.message += j === error.line - 1 ? ' -> ' : ' ';
|
||||
error.message += line;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (zeroLine > 0) {
|
||||
error.message = error.message.replace(/[0-9]+:/, error.line - zeroLine + ':');
|
||||
}
|
||||
error.message = `uncss/node_modules/css: unable to parse ${error.filename}:\n${error.message}\n`;
|
||||
return error;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isWindows,
|
||||
parseUncssrc,
|
||||
parseErrorMessage,
|
||||
parsePaths,
|
||||
readStylesheets
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue