@@ -48906,7 +48906,7 @@ define("help/HelpCommandHandlers", function (require, exports, module) {
4890648906 */
4890748907
4890848908/*jslint regexp: true */
48909- /*global jsPromise*/
48909+ /*global jsPromise, catchToNull, path */
4891048910
4891148911/**
4891248912 * Set of utilities for simple parsing of CSS text.
@@ -48926,8 +48926,9 @@ define("language/CSSUtils", function (require, exports, module) {
4892648926 IndexingWorker = require("worker/IndexingWorker"),
4892748927 _ = require("thirdparty/lodash");
4892848928
48929+ const MAX_CONTENT_LENGTH = 10 * 1024 * 1024; // 10MB
4892948930 // Constants
48930- var SELECTOR = "selector",
48931+ const SELECTOR = "selector",
4893148932 PROP_NAME = "prop.name",
4893248933 PROP_VALUE = "prop.value",
4893348934 IMPORT_URL = "import.url";
@@ -50713,7 +50714,12 @@ define("language/CSSUtils", function (require, exports, module) {
5071350714 return selectors;
5071450715 }
5071550716
50716- const CSSSelectorCache = new Map();
50717+ const CSSSelectorCache = new Phoenix.libs.LRUCache({
50718+ maxSize: 100*1024*1024, // 100MB
50719+ sizeCalculation: (value) => {
50720+ return value.length;
50721+ }
50722+ });
5071750723
5071850724 function _projectFileChanged(_evt, entry) {
5071950725 if(!entry){
@@ -50733,7 +50739,7 @@ define("language/CSSUtils", function (require, exports, module) {
5073350739 }
5073450740 }
5073550741
50736- const MODE_MAP = {
50742+ const CSS_MODE_MAP = {
5073750743 css: "CSS",
5073850744 less: "LESS",
5073950745 scss: "SCSS"
@@ -50748,21 +50754,21 @@ define("language/CSSUtils", function (require, exports, module) {
5074850754 let selectors = new Set();
5074950755 const cachedSelectors = CSSSelectorCache.get(fullPath);
5075050756 if(cachedSelectors){
50751- resolve(cachedSelectors);
50757+ resolve(new Set(JSON.parse( cachedSelectors)) );
5075250758 return;
5075350759 }
5075450760 const langID = doc.getLanguage().getId();
50755- if(!MODE_MAP [langID]){
50761+ if(!CSS_MODE_MAP [langID]){
5075650762 console.log("Cannot parse CSS for mode :", langID, "ignoring", fullPath);
5075750763 resolve(selectors);
5075850764 return;
5075950765 }
5076050766 console.log("scanning file for css selector collation: ", fullPath);
5076150767 IndexingWorker.execPeer("css_getAllSymbols",
50762- {text: doc.getText(), cssMode: "CSS" , filePath: fullPath})
50768+ {text: doc.getText(), cssMode: CSS_MODE_MAP[langID] , filePath: fullPath})
5076350769 .then((selectorArray)=>{
5076450770 selectors = _extractSelectorSet(selectorArray);
50765- CSSSelectorCache.set(fullPath, selectors);
50771+ CSSSelectorCache.set(fullPath, JSON.stringify(Array.from( selectors)) );
5076650772 resolve(selectors);
5076750773 }).catch(err=>{
5076850774 console.warn("CSS language service unable to get selectors for" + fullPath, err);
@@ -50799,34 +50805,101 @@ define("language/CSSUtils", function (require, exports, module) {
5079950805 return (_htmlLikeFileExts.indexOf(LanguageManager.getLanguageForPath(fullPath).getId() || "") !== -1);
5080050806 }
5080150807
50802- function _getAllSelectorsInCurrentHTMLEditor() {
50803- return new Promise(resolve=>{
50804- let selectors = new Set();
50805- const htmlEditor = EditorManager.getCurrentFullEditor();
50806- if (!htmlEditor || !_isHtmlLike(htmlEditor) ) {
50807- resolve(selectors);
50808+ const cacheInProgress = new Set();
50809+ async function _precacheExternalStyleSheet(link) {
50810+ try {
50811+ if(cacheInProgress.has(link)){
50812+ return;
50813+ }
50814+ cacheInProgress.add(link);
50815+ const extension = path.extname(new URL(link).pathname).slice(1);
50816+ if (!extension || !CSS_MODE_MAP[extension]) {
50817+ console.log(`Not a valid stylesheet type ${extension}, ignoring`, link);
50818+ return;
50819+ }
50820+ const responseHead = await fetch(link, { method: 'HEAD' });
50821+ const contentLength = responseHead.headers.get('Content-Length');
50822+ if (!contentLength || contentLength > MAX_CONTENT_LENGTH) {
50823+ console.log(`Stylesheet is larger than ${MAX_CONTENT_LENGTH}bytes - ${contentLength}, ignoring`, link);
5080850824 return;
5080950825 }
5081050826
50811- // Find all <style> blocks in the HTML file
50812- const styleBlocks = HTMLUtils.findStyleBlocks(htmlEditor);
50813- let cssText = "";
50814-
50815- styleBlocks.forEach(function (styleBlockInfo) {
50816- // Search this one <style> block's content
50817- cssText += styleBlockInfo.text;
50818- });
50819- const fullPath = htmlEditor.document.file.fullPath;
50820- IndexingWorker.execPeer("css_getAllSymbols", {text: cssText, cssMode: "CSS", filePath: fullPath})
50827+ const response = await fetch(link);
50828+ const styleSheetText = await response.text();
50829+ IndexingWorker.execPeer("css_getAllSymbols",
50830+ {text: styleSheetText, cssMode: CSS_MODE_MAP[extension], filePath: link})
5082150831 .then((selectorArray)=>{
50822- selectors = _extractSelectorSet(selectorArray);
50823- CSSSelectorCache.set(fullPath, selectors);
50824- resolve(selectors);
50832+ const selectorJson = JSON.stringify(Array.from(_extractSelectorSet(selectorArray)));
50833+ CSSSelectorCache.set(link, selectorJson);
5082550834 }).catch(err=>{
50826- console.warn("CSS language service unable to get selectors for" + fullPath, err);
50827- resolve(selectors); // still resolve, so the overall result doesn't reject
50835+ console.warn("CSS language service unable to get selectors for link" + link, err);
5082850836 });
50829- });
50837+ } catch (e) {
50838+ console.error("Error pre caching externally linked style sheet ", link);
50839+ }
50840+ cacheInProgress.delete(link);
50841+ }
50842+
50843+ const MAX_ALLOWED_EXTERNAL_STYLE_SHEETS = 30;
50844+
50845+ /**
50846+ * html files may have embedded link style sheets to external CDN urls. We will parse them to get all selectors.
50847+ * @param htmlFileContent
50848+ * @param fileMode
50849+ * @param fullPath
50850+ * @return {Promise<void>}
50851+ * @private
50852+ */
50853+ async function _getLinkedCSSFileSelectors(htmlFileContent, fileMode, fullPath) {
50854+ const linkedFiles = await catchToNull(IndexingWorker.execPeer(
50855+ "html_getAllLinks", {text: htmlFileContent, htmlMode: fileMode, filePath: fullPath}),
50856+ "error extracting linked css files from"+ fullPath) || [];
50857+ let selectors = new Set();
50858+ let externalStyleSheetCount = 0;
50859+ for(const link of linkedFiles) {
50860+ if(externalStyleSheetCount >= MAX_ALLOWED_EXTERNAL_STYLE_SHEETS){
50861+ break;
50862+ }
50863+ if(link.startsWith("http://") || link.startsWith("https://")) {
50864+ externalStyleSheetCount ++;
50865+ let cachedSelectorsArray = CSSSelectorCache.get(link);
50866+ if(cachedSelectorsArray){
50867+ cachedSelectorsArray = JSON.parse(cachedSelectorsArray);
50868+ for(let value of cachedSelectorsArray){
50869+ selectors.add(value);
50870+ }
50871+ } else {
50872+ _precacheExternalStyleSheet(link);
50873+ }
50874+ }
50875+ }
50876+ return selectors;
50877+ }
50878+
50879+ async function _getAllSelectorsInCurrentHTMLEditor() {
50880+ let selectors = new Set();
50881+ const htmlEditor = EditorManager.getCurrentFullEditor();
50882+ if (!htmlEditor || !_isHtmlLike(htmlEditor) ) {
50883+ return selectors;
50884+ }
50885+
50886+ // Find all <style> blocks in the HTML file
50887+ const styleBlocks = HTMLUtils.findStyleBlocks(htmlEditor);
50888+ let cssText = "";
50889+
50890+ styleBlocks.forEach(function (styleBlockInfo) {
50891+ // Search this one <style> block's content
50892+ cssText += styleBlockInfo.text;
50893+ });
50894+ const fullPath = htmlEditor.document.file.fullPath;
50895+ const selectorsPromise = IndexingWorker.execPeer(
50896+ "css_getAllSymbols", {text: cssText, cssMode: "CSS", filePath: fullPath});
50897+ const htmlLanguageID = LanguageManager.getLanguageForPath(fullPath).getId();
50898+ const remoteLinkedSelectors = await _getLinkedCSSFileSelectors(htmlEditor.document.getText(),
50899+ htmlLanguageID.toUpperCase(), fullPath);
50900+ selectors = await catchToNull(selectorsPromise, "CSS language service unable to get selectors for" + fullPath);
50901+ selectors = selectors || new Set();
50902+ return new Set([...selectors, ...remoteLinkedSelectors]);
5083050903 }
5083150904
5083250905 let globalPrecacheRun = 0;
0 commit comments