From 668e049a5e0466055d1235bf4a89e27b7b9065a9 Mon Sep 17 00:00:00 2001
From: Ika
Date: Thu, 15 Nov 2018 12:35:28 +0800
Subject: [PATCH] feat(html): support ie conditional start/end comment (#5470)
---
src/language-html/conditional-comment.js | 80 +++++
src/language-html/parser-html.js | 45 +--
src/language-html/preprocess.js | 65 ++++
src/language-html/printer-html.js | 19 ++
.../__snapshots__/jsfmt.spec.js.snap | 302 ++++++++++++++++++
tests/html_comments/conditional.html | 29 ++
6 files changed, 496 insertions(+), 44 deletions(-)
create mode 100644 src/language-html/conditional-comment.js
diff --git a/src/language-html/conditional-comment.js b/src/language-html/conditional-comment.js
new file mode 100644
index 000000000000..3071d310de28
--- /dev/null
+++ b/src/language-html/conditional-comment.js
@@ -0,0 +1,80 @@
+"use strict";
+
+// https://css-tricks.com/how-to-create-an-ie-only-stylesheet
+
+//
+const IE_CONDITIONAL_START_END_COMMENT_REGEX = /^(\[if([^\]]*?)\]>)([\s\S]*?)
+const IE_CONDITIONAL_START_COMMENT_REGEX = /^\[if([^\]]*?)\]>
+const IE_CONDITIONAL_END_COMMENT_REGEX = /^ {
+ try {
+ return [true, parseHtml(data, contentStartSpan).children];
+ } catch (e) {
+ const text = {
+ type: "text",
+ value: data,
+ sourceSpan: new ParseSourceSpan(contentStartSpan, contentEndSpan)
+ };
+ return [false, [text]];
+ }
+ })();
+ return {
+ type: "ieConditionalComment",
+ complete,
+ children,
+ condition: condition.trim().replace(/\s+/g, " "),
+ sourceSpan: node.sourceSpan,
+ startSourceSpan: new ParseSourceSpan(
+ node.sourceSpan.start,
+ contentStartSpan
+ ),
+ endSourceSpan: new ParseSourceSpan(contentEndSpan, node.sourceSpan.end)
+ };
+}
+
+function parseIeConditionalStartComment(node, parseHtml, match) {
+ const [, condition] = match;
+ return {
+ type: "ieConditionalStartComment",
+ condition: condition.trim().replace(/\s+/g, " "),
+ sourceSpan: node.sourceSpan
+ };
+}
+
+function parseIeConditionalEndComment(node /*, parseHtml, match */) {
+ return {
+ type: "ieConditionalEndComment",
+ sourceSpan: node.sourceSpan
+ };
+}
+
+module.exports = {
+ parseIeConditionalComment
+};
diff --git a/src/language-html/parser-html.js b/src/language-html/parser-html.js
index 972088eb4954..cfdcf09ed488 100644
--- a/src/language-html/parser-html.js
+++ b/src/language-html/parser-html.js
@@ -5,6 +5,7 @@ const { HTML_ELEMENT_ATTRIBUTES, HTML_TAGS } = require("./utils");
const { hasPragma } = require("./pragma");
const createError = require("../common/parser-create-error");
const { Node } = require("./ast");
+const { parseIeConditionalComment } = require("./conditional-comment");
function ngHtmlParser(input, canSelfClose) {
const parser = require("angular-html-parser");
@@ -240,50 +241,6 @@ function _parse(
});
}
-function parseIeConditionalComment(node, parseHtml) {
- if (!node.value) {
- return null;
- }
-
- const match = node.value.match(
- /^(\[if([^\]]*?)\]>)([\s\S]*?) {
- try {
- return [true, parseHtml(data, contentStartSpan).children];
- } catch (e) {
- const text = {
- type: "text",
- value: data,
- sourceSpan: new ParseSourceSpan(contentStartSpan, contentEndSpan)
- };
- return [false, [text]];
- }
- })();
- return {
- type: "ieConditionalComment",
- complete,
- children,
- condition: condition.trim().replace(/\s+/g, " "),
- sourceSpan: node.sourceSpan,
- startSourceSpan: new ParseSourceSpan(
- node.sourceSpan.start,
- contentStartSpan
- ),
- endSourceSpan: new ParseSourceSpan(contentEndSpan, node.sourceSpan.end)
- };
-}
-
function locStart(node) {
return node.sourceSpan.start.offset;
}
diff --git a/src/language-html/preprocess.js b/src/language-html/preprocess.js
index c94ed7450e92..5a645e657ce9 100644
--- a/src/language-html/preprocess.js
+++ b/src/language-html/preprocess.js
@@ -12,6 +12,7 @@ const {
const PREPROCESS_PIPELINE = [
removeIgnorableFirstLf,
+ mergeIeConditonalStartEndCommentIntoElementOpeningTag,
mergeCdataIntoText,
extractInterpolation,
extractWhitespaces,
@@ -52,6 +53,70 @@ function removeIgnorableFirstLf(ast /*, options */) {
});
}
+function mergeIeConditonalStartEndCommentIntoElementOpeningTag(
+ ast /*, options */
+) {
+ /**
+ *
+ */
+ const isTarget = node =>
+ node.type === "element" &&
+ node.prev &&
+ node.prev.type === "ieConditionalStartComment" &&
+ node.prev.sourceSpan.end.offset === node.startSourceSpan.start.offset &&
+ node.firstChild &&
+ node.firstChild.type === "ieConditionalEndComment" &&
+ node.firstChild.sourceSpan.start.offset === node.startSourceSpan.end.offset;
+ return ast.map(node => {
+ if (node.children) {
+ const isTargetResults = node.children.map(isTarget);
+ if (isTargetResults.some(Boolean)) {
+ const newChildren = [];
+
+ for (let i = 0; i < node.children.length; i++) {
+ const child = node.children[i];
+
+ if (isTargetResults[i + 1]) {
+ // ieConditionalStartComment
+ continue;
+ }
+
+ if (isTargetResults[i]) {
+ const ieConditionalStartComment = child.prev;
+ const ieConditionalEndComment = child.firstChild;
+
+ const ParseSourceSpan = child.sourceSpan.constructor;
+ const startSourceSpan = new ParseSourceSpan(
+ ieConditionalStartComment.sourceSpan.start,
+ ieConditionalEndComment.sourceSpan.end
+ );
+ const sourceSpan = new ParseSourceSpan(
+ startSourceSpan.start,
+ child.sourceSpan.end
+ );
+
+ newChildren.push(
+ child.clone({
+ condition: ieConditionalStartComment.condition,
+ sourceSpan,
+ startSourceSpan,
+ children: child.children.slice(1)
+ })
+ );
+
+ continue;
+ }
+
+ newChildren.push(child);
+ }
+
+ return node.clone({ children: newChildren });
+ }
+ }
+ return node;
+ });
+}
+
function mergeNodeIntoText(ast, shouldMerge, getValue) {
return ast.map(node => {
if (node.children) {
diff --git a/src/language-html/printer-html.js b/src/language-html/printer-html.js
index 032823125a3f..c849780ecea2 100644
--- a/src/language-html/printer-html.js
+++ b/src/language-html/printer-html.js
@@ -248,6 +248,9 @@ function genericPrint(path, options, print) {
printClosingTag(node)
]);
}
+ case "ieConditionalStartComment":
+ case "ieConditionalEndComment":
+ return concat([printOpeningTagStart(node), printClosingTagEnd(node)]);
case "interpolation":
return concat([
printOpeningTagStart(node),
@@ -765,11 +768,19 @@ function printOpeningTagStartMarker(node) {
case "comment":
return "<${node.rawName}`;
+ }
+ // fall through
default:
return `<${node.rawName}`;
}
@@ -780,6 +791,11 @@ function printOpeningTagEndMarker(node) {
switch (node.type) {
case "ieConditionalComment":
return "]>";
+ case "element":
+ if (node.condition) {
+ return `>`;
+ }
+ // fall through
default:
return `>`;
}
@@ -806,7 +822,10 @@ function printClosingTagEndMarker(node) {
case "comment":
return "-->";
case "ieConditionalComment":
+ case "ieConditionalEndComment":
return `[endif]-->`;
+ case "ieConditionalStartComment":
+ return `]>`;
case "interpolation":
return "}}";
case "element":
diff --git a/tests/html_comments/__snapshots__/jsfmt.spec.js.snap b/tests/html_comments/__snapshots__/jsfmt.spec.js.snap
index 5e796d0c0f9c..5fb7e9745d84 100644
--- a/tests/html_comments/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/html_comments/__snapshots__/jsfmt.spec.js.snap
@@ -99,6 +99,35 @@ exports[`conditional.html - html-verify 1`] = `