Commit c80cc1cd authored by Paul Slaughter's avatar Paul Slaughter Committed by Nicolò Maria Mezzopera

Add generate:startup_css yarn script

parent deea032d
......@@ -33,6 +33,8 @@
"lint:stylelint:fix": "yarn run lint:stylelint --fix",
"lint:stylelint:staged": "scripts/frontend/execute-on-staged-files.sh stylelint '(css|scss)' -q",
"lint:stylelint:staged:fix": "yarn run lint:stylelint:staged --fix",
"generate:startup_css": "scripts/frontend/startup_css/setup.sh && node scripts/frontend/startup_css/main.js",
"generate:startup_css:full": "scripts/frontend/startup_css/setup.sh force && node scripts/frontend/startup_css/main.js",
"markdownlint": "markdownlint --config .markdownlint.yml",
"markdownlint:no-trailing-spaces": "markdownlint --config doc/.markdownlint/markdownlint-no-trailing-spaces.yml",
"markdownlint:no-trailing-spaces:fix": "yarn run markdownlint:no-trailing-spaces --fix",
......@@ -200,6 +202,7 @@
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-istanbul": "^6.0.0",
"chalk": "^2.4.1",
"cheerio": "^1.0.0-rc.9",
"commander": "^2.18.0",
"custom-jquery-matchers": "^2.1.0",
"docdash": "^1.0.2",
......@@ -241,6 +244,7 @@
"nodemon": "^2.0.4",
"postcss": "^7.0.14",
"prettier": "2.2.1",
"purgecss": "^4.0.3",
"readdir-enhanced": "^2.2.4",
"sass": "^1.32.12",
"timezone-mock": "^1.0.8",
......
const { memoize, isString, isRegExp } = require('lodash');
const { parse } = require('postcss');
const { CSS_TO_REMOVE } = require('./constants');
const getSelectorRemoveTesters = memoize(() =>
CSS_TO_REMOVE.map((x) => {
if (isString(x)) {
return (selector) => x === selector;
}
if (isRegExp(x)) {
return (selector) => x.test(selector);
}
throw new Error(`Unexpected type in CSS_TO_REMOVE content "${x}". Expected String or RegExp.`);
}),
);
const getRemoveTesters = memoize(() => {
const selectorTesters = getSelectorRemoveTesters();
// These are mostly carried over from the previous project
// https://gitlab.com/gitlab-org/frontend/gitlab-css-statistics/-/blob/2aa00af25dba08fc71081c77206f45efe817ea4b/lib/gl_startup_extract.js
return [
(node) => node.type === 'comment',
(node) =>
node.type === 'atrule' &&
(node.params === 'print' ||
node.params === 'prefers-reduced-motion: reduce' ||
node.name === 'keyframe' ||
node.name === 'charset'),
(node) => node.selector && node.selectors && !node.selectors.length,
(node) => node.selector && selectorTesters.some((fn) => fn(node.selector)),
(node) =>
node.type === 'decl' &&
(node.prop === 'transition' ||
node.prop.indexOf('-webkit-') > -1 ||
node.prop.indexOf('-ms-') > -1),
];
});
const getNodesToRemove = (nodes) => {
const removeTesters = getRemoveTesters();
const remNodes = [];
nodes.forEach((node) => {
if (removeTesters.some((fn) => fn(node))) {
remNodes.push(node);
} else if (node.nodes?.length) {
remNodes.push(...getNodesToRemove(node.nodes));
}
});
return remNodes;
};
const getEmptyNodesToRemove = (nodes) =>
nodes
.filter((node) => node.nodes)
.reduce((acc, node) => {
if (node.nodes.length) {
acc.push(...getEmptyNodesToRemove(node.nodes));
} else {
acc.push(node);
}
return acc;
}, []);
const cleanCSS = (css) => {
const cssRoot = parse(css);
getNodesToRemove(cssRoot.nodes).forEach((node) => {
node.remove();
});
getEmptyNodesToRemove(cssRoot.nodes).forEach((node) => {
node.remove();
});
return cssRoot.toResult().css;
};
module.exports = { cleanCSS };
const path = require('path');
const IS_EE = require('../../../config/helpers/is_ee_env');
// controls --------------------------------------------------------------------
const HTML_TO_REMOVE = [
'style',
'script',
'link[rel="stylesheet"]',
'.content-wrapper',
'#js-peek',
'.modal',
'.feature-highlight',
// We don't want to capture all the children of a dropdown-menu
'.dropdown-menu',
];
const CSS_TO_REMOVE = [
'.tooltip',
'.tooltip.show',
'.fa',
'.gl-accessibility:focus',
'.toasted-container',
'body .toasted-container.bottom-left',
'.popover',
'.with-performance-bar .navbar-gitlab',
'.text-secondary',
/\.feature-highlight-popover-content/,
/\.commit/,
/\.md/,
/\.with-performance-bar/,
/\.identicon/,
];
const APPLICATION_CSS_PREFIX = 'application';
const APPLICATION_DARK_CSS_PREFIX = 'application_dark';
const UTILITIES_CSS_PREFIX = 'application_utilities';
const UTILITIES_DARK_CSS_PREFIX = 'application_utilities_dark';
// paths -----------------------------------------------------------------------
const ROOT = path.resolve(__dirname, '../../..');
const FIXTURES_FOLDER_NAME = IS_EE ? 'fixtures-ee' : 'fixtures';
const FIXTURES_ROOT = path.join(ROOT, 'tmp/tests/frontend', FIXTURES_FOLDER_NAME);
const PATH_SIGNIN_HTML = path.join(FIXTURES_ROOT, 'startup_css/sign-in.html');
const PATH_ASSETS = path.join(ROOT, 'tmp/startup_css_assets');
const PATH_STARTUP_SCSS = path.join(ROOT, 'app/assets/stylesheets/startup');
const OUTPUTS = [
{
outFile: 'startup-general',
htmlPaths: [
path.join(FIXTURES_ROOT, 'startup_css/project-general.html'),
path.join(FIXTURES_ROOT, 'startup_css/project-general-legacy-menu.html'),
path.join(FIXTURES_ROOT, 'startup_css/project-general-signed-out.html'),
],
cssKeys: [APPLICATION_CSS_PREFIX, UTILITIES_CSS_PREFIX],
// We want to include the root dropdown-menu style since it should be hidden by default
purgeOptions: {
safelist: {
standard: ['dropdown-menu'],
},
},
},
{
outFile: 'startup-dark',
htmlPaths: [
path.join(FIXTURES_ROOT, 'startup_css/project-dark.html'),
path.join(FIXTURES_ROOT, 'startup_css/project-dark-legacy-menu.html'),
path.join(FIXTURES_ROOT, 'startup_css/project-dark-signed-out.html'),
],
cssKeys: [APPLICATION_DARK_CSS_PREFIX, UTILITIES_DARK_CSS_PREFIX],
// We want to include the root dropdown-menu styles since it should be hidden by default
purgeOptions: {
safelist: {
standard: ['dropdown-menu'],
},
},
},
{
outFile: 'startup-signin',
htmlPaths: [PATH_SIGNIN_HTML],
cssKeys: [APPLICATION_CSS_PREFIX, UTILITIES_CSS_PREFIX],
purgeOptions: {
safelist: {
standard: ['fieldset'],
deep: [/login-page$/],
},
},
},
];
module.exports = {
HTML_TO_REMOVE,
CSS_TO_REMOVE,
APPLICATION_CSS_PREFIX,
APPLICATION_DARK_CSS_PREFIX,
UTILITIES_CSS_PREFIX,
UTILITIES_DARK_CSS_PREFIX,
ROOT,
PATH_ASSETS,
PATH_STARTUP_SCSS,
OUTPUTS,
};
const fs = require('fs');
const path = require('path');
const { memoize } = require('lodash');
const { PATH_ASSETS } = require('./constants');
const { die } = require('./utils');
const listAssetsDir = memoize(() => fs.readdirSync(PATH_ASSETS));
const getCSSPath = (prefix) => {
const matcher = new RegExp(`^${prefix}-[^-]+\\.css$`);
const cssPath = listAssetsDir().find((x) => matcher.test(x));
if (!cssPath) {
die(
`Could not find the CSS asset matching "${prefix}". Have you run "scripts/frontend/startup_css/setup.sh"?`,
);
}
return path.join(PATH_ASSETS, cssPath);
};
module.exports = { getCSSPath };
const fs = require('fs');
const cheerio = require('cheerio');
const { mergeWith, isArray } = require('lodash');
const { PurgeCSS } = require('purgecss');
const { cleanCSS } = require('./clean_css');
const { HTML_TO_REMOVE } = require('./constants');
const { die } = require('./utils');
const cleanHtml = (html) => {
const $ = cheerio.load(html);
HTML_TO_REMOVE.forEach((selector) => {
$(selector).remove();
});
return $.html();
};
const mergePurgeCSSOptions = (...options) =>
mergeWith(...options, (objValue, srcValue) => {
if (isArray(objValue)) {
return objValue.concat(srcValue);
}
return undefined;
});
const getStartupCSS = async ({ htmlPaths, cssPaths, purgeOptions }) => {
const content = htmlPaths.map((htmlPath) => {
if (!fs.existsSync(htmlPath)) {
die(`Could not find fixture "${htmlPath}". Have you run the fixtures?`);
}
const rawHtml = fs.readFileSync(htmlPath);
const html = cleanHtml(rawHtml);
return { raw: html, extension: 'html' };
});
const purgeCSSResult = await new PurgeCSS().purge({
content,
css: cssPaths,
...mergePurgeCSSOptions(
{
fontFace: true,
variables: true,
keyframes: true,
blocklist: [/:hover/, /:focus/, /-webkit-/, /-moz-focusring-/, /-ms-expand/],
safelist: {
standard: ['brand-header-logo'],
},
// By default, PurgeCSS ignores special characters, but our utilities use "!"
defaultExtractor: (x) => x.match(/[\w-!]+/g),
},
purgeOptions,
),
});
return purgeCSSResult.map(({ css }) => cleanCSS(css)).join('\n');
};
module.exports = { getStartupCSS };
const { memoize } = require('lodash');
const { OUTPUTS } = require('./constants');
const { getCSSPath } = require('./get_css_path');
const { getStartupCSS } = require('./get_startup_css');
const { log, die } = require('./utils');
const { writeStartupSCSS } = require('./write_startup_scss');
const memoizedCSSPath = memoize(getCSSPath);
const runTask = async ({ outFile, htmlPaths, cssKeys, purgeOptions = {} }) => {
try {
log(`Generating startup CSS for HTML files: ${htmlPaths}`);
const generalCSS = await getStartupCSS({
htmlPaths,
cssPaths: cssKeys.map(memoizedCSSPath),
purgeOptions,
});
log(`Writing to startup CSS...`);
const startupCSSPath = writeStartupSCSS(outFile, generalCSS);
log(`Finished writing to ${startupCSSPath}`);
return {
success: true,
outFile,
};
} catch (e) {
log(`ERROR! Unexpected error occurred while generating startup CSS for: ${outFile}`);
log(e);
return {
success: false,
outFile,
};
}
};
const main = async () => {
const result = await Promise.all(OUTPUTS.map(runTask));
const fullSuccess = result.every((x) => x.success);
log('RESULTS:');
log('--------');
result.forEach(({ success, outFile }) => {
const status = success ? '' : '';
log(`${status}: ${outFile}`);
});
log('--------');
if (fullSuccess) {
log('Done!');
} else {
die('Some tasks have failed');
}
};
main();
path_public_dir="public"
path_tmp="tmp"
path_dest="$path_tmp/startup_css_assets"
glob_css_dest="$path_dest/application*.css"
glob_css_src="$path_public_dir/assets/application*.css"
should_clean=false
should_force() {
$1=="force"
}
has_dest_already() {
find $glob_css_dest -quit
}
has_src_already() {
find $glob_css_src -quit
}
compile_assets() {
# We need to build the same test bundle that is built in CI
RAILS_ENV=test bundle exec rake rake:assets:precompile
}
clean_assets() {
bundle exec rake rake:assets:clobber
}
copy_assets() {
rm -rf $path_dest
mkdir $path_dest
cp $glob_css_src $path_dest
}
echo "-----------------------------------------------------------"
echo "If you are run into any issues with Startup CSS generation,"
echo "please check out the feedback issue:"
echo ""
echo "https://gitlab.com/gitlab-org/gitlab/-/issues/331812"
echo "-----------------------------------------------------------"
if [ ! -e $path_public_dir ]; then
echo "Could not find '$path_public_dir/'. This script must be run in the root directory of the gitlab project."
exit 1
fi
if [ ! -e $path_tmp ]; then
echo "Could not find '$path_tmp/'. This script must be run in the root directory of the gitlab project."
exit 1
fi
if [ "$1" != "force" ] && has_dest_already; then
echo "Already found assets for '$glob_css_dest'. Did you want to run this script with 'force' argument?"
exit 0
fi
# If we are in CI, don't recompile things...
if [ -n "$CI" ]; then
if ! has_src_already; then
echo "Could not find '$glob_css_src'. Expected these artifacts to be generated by CI pipeline."
exit 1
fi
elif has_src_already; then
echo "Found '$glob_css_src'. Skipping compile assets..."
else
echo "Starting compile assets process..."
compile_assets
should_clean=true
fi
copy_assets
if $should_clean; then
echo "Starting cleanup..."
clean_assets
fi
const die = (message) => {
console.log(message);
process.exit(1);
};
const log = (message) => console.error(`[gitlab.startup_css] ${message}`);
module.exports = { die, log };
const { writeFileSync } = require('fs');
const path = require('path');
const prettier = require('prettier');
const { PATH_STARTUP_SCSS } = require('./constants');
const buildFinalContent = (raw) => {
const content = `// DO NOT EDIT! This is auto-generated from "yarn run generate:startup_css"
// Please see the feedback issue for more details and help:
// https://gitlab.com/gitlab-org/gitlab/-/issues/331812
@charset "UTF-8";
${raw}
@import 'cloaking';
@include cloak-startup-scss(none);
`;
// We run prettier so that there is more determinism with the generated file.
return prettier.format(content, { parser: 'scss' });
};
const writeStartupSCSS = (name, raw) => {
const fullPath = path.join(PATH_STARTUP_SCSS, `${name}.scss`);
writeFileSync(fullPath, buildFinalContent(raw));
return fullPath;
};
module.exports = { writeStartupSCSS };
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Startup CSS fixtures', type: :controller do
include JavaScriptFixturesHelpers
let(:use_full_html) { true }
render_views
before(:all) do
stub_feature_flags(combined_menu: true)
clean_frontend_fixtures('startup_css/')
end
shared_examples 'startup css project fixtures' do |type|
let(:user) { create(:user, :admin) }
let(:project) { create(:project, :public, :repository, description: 'Code and stuff', avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png'), creator: user) }
before do
sign_in(user)
end
it "startup_css/project-#{type}-legacy-menu.html" do
stub_feature_flags(combined_menu: false)
get :show, params: {
namespace_id: project.namespace.to_param,
id: project
}
expect(response).to be_successful
end
it "startup_css/project-#{type}.html" do
get :show, params: {
namespace_id: project.namespace.to_param,
id: project
}
expect(response).to be_successful
end
it "startup_css/project-#{type}-signed-out.html" do
sign_out(user)
get :show, params: {
namespace_id: project.namespace.to_param,
id: project
}
expect(response).to be_successful
end
end
describe ProjectsController, '(Startup CSS fixtures)', type: :controller do
it_behaves_like 'startup css project fixtures', 'general'
end
describe ProjectsController, '(Startup CSS fixtures)', type: :controller do
before do
user.update!(theme_id: 11)
end
it_behaves_like 'startup css project fixtures', 'dark'
end
describe RegistrationsController, '(Startup CSS fixtures)', type: :controller do
it 'startup_css/sign-in.html' do
get :new
expect(response).to be_successful
end
end
end
......@@ -66,6 +66,14 @@ module JavaScriptFixturesHelpers
File.write(full_fixture_path, fixture)
end
def parse_html(fixture)
if respond_to?(:use_full_html) && public_send(:use_full_html)
Nokogiri::HTML::Document.parse(fixture)
else
Nokogiri::HTML::DocumentFragment.parse(fixture)
end
end
# Private: Prepare a response object for use as a frontend fixture
#
# response - response object to prepare
......@@ -76,7 +84,7 @@ module JavaScriptFixturesHelpers
response_mime_type = Mime::Type.lookup(response.media_type)
if response_mime_type.html?
doc = Nokogiri::HTML::DocumentFragment.parse(fixture)
doc = parse_html(fixture)
link_tags = doc.css('link')
link_tags.remove
......
......@@ -2741,6 +2741,11 @@ bonjour@^3.5.0:
multicast-dns "^6.0.1"
multicast-dns-service-types "^1.1.0"
boolbase@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
bootstrap-vue@2.17.3:
version "2.17.3"
resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.17.3.tgz#3d78b7b4ff992a8ad69d2ed1c7413fcfdcefaec7"
......@@ -3140,6 +3145,30 @@ charenc@~0.0.1:
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
cheerio-select@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.4.0.tgz#3a16f21e37a2ef0f211d6d1aa4eff054bb22cdc9"
integrity sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew==
dependencies:
css-select "^4.1.2"
css-what "^5.0.0"
domelementtype "^2.2.0"
domhandler "^4.2.0"
domutils "^2.6.0"
cheerio@^1.0.0-rc.9:
version "1.0.0-rc.9"
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.9.tgz#a3ae6b7ce7af80675302ff836f628e7cb786a67f"
integrity sha512-QF6XVdrLONO6DXRF5iaolY+odmhj2CLj+xzNod7INPWMi/x9X4SOylH0S/vaPpX+AUU6t04s34SQNh7DbkuCng==
dependencies:
cheerio-select "^1.4.0"
dom-serializer "^1.3.1"
domhandler "^4.2.0"
htmlparser2 "^6.1.0"
parse5 "^6.0.1"
parse5-htmlparser2-tree-adapter "^6.0.1"
tslib "^2.2.0"
"chokidar@>=3.0.0 <4.0.0", chokidar@^2.1.8, chokidar@^3.0.0, chokidar@^3.2.2, chokidar@^3.4.0, chokidar@^3.4.1:
version "3.4.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8"
......@@ -3342,10 +3371,10 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
colorette@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
colorette@^1.2.1, colorette@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
colors@^1.1.0:
version "1.3.3"
......@@ -3748,6 +3777,17 @@ css-loader@^2.1.1:
postcss-value-parser "^3.3.0"
schema-utils "^1.0.0"
css-select@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.2.tgz#8b52b6714ed3a80d8221ec971c543f3b12653286"
integrity sha512-nu5ye2Hg/4ISq4XqdLY2bEatAcLIdt3OYGFc9Tm9n7VSlFBcfRv0gBNksHRgSdUDQGtN3XrZ94ztW+NfzkFSUw==
dependencies:
boolbase "^1.0.0"
css-what "^5.0.0"
domhandler "^4.2.0"
domutils "^2.6.0"
nth-check "^2.0.0"
css-selector-parser@^1.3:
version "1.3.0"
resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.3.0.tgz#5f1ad43e2d8eefbfdc304fcd39a521664943e3eb"
......@@ -3767,6 +3807,11 @@ css-values@^0.1.0:
ends-with "^0.2.0"
postcss-value-parser "^3.3.0"
css-what@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.0.tgz#f0bf4f8bac07582722346ab243f6a35b512cfc47"
integrity sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA==
css@^2.1.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
......@@ -4428,6 +4473,15 @@ dom-serializer@0:
domelementtype "^2.0.1"
entities "^2.0.0"
dom-serializer@^1.0.1, dom-serializer@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.1.tgz#d845a1565d7c041a95e5dab62184ab41e3a519be"
integrity sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.0.0"
entities "^2.0.0"
dom-walk@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
......@@ -4443,10 +4497,10 @@ domelementtype@1, domelementtype@^1.3.1:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
domelementtype@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e"
integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==
domelementtype@^2.0.1, domelementtype@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
domexception@^2.0.1:
version "2.0.1"
......@@ -4462,6 +4516,13 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
domhandler@^4.0.0, domhandler@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
dependencies:
domelementtype "^2.2.0"
dompurify@^2.2.8:
version "2.2.8"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.8.tgz#ce88e395f6d00b6dc53f80d6b2a6fdf5446873c6"
......@@ -4475,6 +4536,15 @@ domutils@^1.5.1:
dom-serializer "0"
domelementtype "1"
domutils@^2.5.2, domutils@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7"
integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
domhandler "^4.2.0"
dot-prop@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
......@@ -5674,10 +5744,10 @@ glob-to-regexp@^0.4.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
"glob@5 - 7", glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
"glob@5 - 7", glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
......@@ -6076,6 +6146,16 @@ htmlparser2@^3.10.0:
inherits "^2.0.1"
readable-stream "^3.1.1"
htmlparser2@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.0.0"
domutils "^2.5.2"
entities "^2.0.0"
http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
......@@ -8559,6 +8639,11 @@ multicast-dns@^6.0.1:
dns-packet "^1.0.1"
thunky "^0.1.0"
nanoid@^3.1.23:
version "3.1.23"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
nanomatch@^1.2.9:
version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
......@@ -8766,6 +8851,13 @@ npm-run-path@^4.0.0:
dependencies:
path-key "^3.0.0"
nth-check@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
dependencies:
boolbase "^1.0.0"
num2fraction@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
......@@ -9147,7 +9239,19 @@ parse-passwd@^1.0.0:
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
"parse5@5 - 6", parse5@5.1.1:
parse5-htmlparser2-tree-adapter@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
dependencies:
parse5 "^6.0.1"
"parse5@5 - 6", parse5@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
parse5@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
......@@ -9458,6 +9562,15 @@ postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@^8.2.1:
version "8.2.15"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65"
integrity sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==
dependencies:
colorette "^1.2.2"
nanoid "^3.1.23"
source-map "^0.6.1"
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
......@@ -9764,6 +9877,16 @@ pupa@^2.0.1:
dependencies:
escape-goat "^2.0.0"
purgecss@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-4.0.3.tgz#8147b429f9c09db719e05d64908ea8b672913742"
integrity sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==
dependencies:
commander "^6.0.0"
glob "^7.0.0"
postcss "^8.2.1"
postcss-selector-parser "^6.0.2"
qjobs@^1.1.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071"
......@@ -11580,6 +11703,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
tslib@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment