Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import { DocumentOffset, DocumentRange, Range, SourceFilePosition, SourceFileRan
import { intersects, makeSourceFileRange } from "../../../util/range-util";
import { VirtualDocument } from "./virtual-document";

function getPartLength(part: Node): number {
const end = part.parent && tsModule.ts.isTemplateSpan(part.parent) ? part.parent.literal.getStart() : part.getEnd();
return end - part.getFullStart();
}
const marker = `lit$analyzer$`;

export class VirtualAstDocument implements VirtualDocument {
readonly fileName: string;
Expand All @@ -20,17 +17,13 @@ export class VirtualAstDocument implements VirtualDocument {
if (this._text == null) {
let str = "";

let prevPart = "";
this.parts.forEach((part, i) => {
const isLastPart = i >= this.parts.length - 1;

if (typeof part === "string") {
str += part.substring(i === 0 ? 0 : 1, part.length - (isLastPart ? 0 : 2));
prevPart = part;
} else {
const length = getPartLength(part) + 3;
const substitution = this.substituteExpression(length, part, prevPart, this.parts[i + 1] as string);
str += substitution;
str += marker;
}
});

Expand All @@ -56,11 +49,11 @@ export class VirtualAstDocument implements VirtualDocument {
const startPadding = i === 0 ? 0 : 1;
const endPadding = isLastPart ? 0 : 2;

offset += part.length;
offset += part.length - endPadding - startPadding;

const literalPartRange: Range = {
start: startOffset + startPadding,
end: offset - endPadding
start: startOffset,
end: offset
};

if (
Expand All @@ -75,7 +68,7 @@ export class VirtualAstDocument implements VirtualDocument {
resultParts.push(substr);
}
} else {
offset += getPartLength(part);
offset += marker.length;

const expressionPartRange: Range = {
start: startOffset,
Expand Down Expand Up @@ -128,10 +121,6 @@ export class VirtualAstDocument implements VirtualDocument {
this.fileName = this.fileName = astNodeOrParts.getSourceFile().fileName;
}
}

protected substituteExpression(length: number, expression: Expression, prev: string, next: string | undefined): string {
return "_".repeat(length);
}
}

function getPartsFromTaggedTemplate(astNode: TaggedTemplateExpression): { expressionParts: Expression[]; literalParts: Node[] } {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,3 @@
import { Expression } from "typescript";
import { VirtualAstDocument } from "./virtual-ast-document";

export class VirtualAstCssDocument extends VirtualAstDocument {
protected substituteExpression(length: number, expression: Expression, prev: string, next: string | undefined): string {
const hasLeftColon = prev.match(/:[^;{]*\${$/) != null;
const hasRightColon = next != null && next.match(/^}\s*:\s+/) != null;
const hasRightSemicolon = next != null && next.match(/^}\s*;/) != null;
const hasRightPercentage = next != null && next.match(/^}%/) != null;

// Inspired by https://github.com/Microsoft/typescript-styled-plugin/blob/909d4f17d61562fe77f24587ea443713b8da851d/src/_substituter.ts#L62
// If this substitution contains both a property and a key, replace it with "$_:_"
// Example:
// div {
// ${unsafeCSS("color: red)};
// }
if (hasRightSemicolon && !hasLeftColon) {
const prefix = "$_:_";
return `${prefix}${"_".repeat(Math.max(0, length - prefix.length))}`.slice(0, length);
}

// If there is "%" to the right of this substitution, replace with a number, because the parser expects a number unit
// Example:
// div {
// transform-origin: ${x}% ${y}%;
// }
else if (hasRightPercentage) {
return "0".repeat(length);
}

// If there is a ": " to the right of this substitution, replace it with an identifier
// Example:
// div {
// ${unsafeCSS("color")}: red
// }
else if (hasRightColon) {
return `$${"_".repeat(length - 1)}`;
}

// Else replace with an identifier "_"
return "_".repeat(length);
}
}
export class VirtualAstCssDocument extends VirtualAstDocument {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import { Expression } from "typescript";
import { VirtualAstDocument } from "./virtual-ast-document";

export class VirtualAstHtmlDocument extends VirtualAstDocument {
protected substituteExpression(length: number, expression: Expression): string {
return "_".repeat(length);
}
}
export class VirtualAstHtmlDocument extends VirtualAstDocument {}
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@ function isTemplateText(t: ExecutionContext, text: string, testFile: string) {
}

tsTest("Substitute for template followed by percent", t => {
isTemplateText(t, "{ div { transform-origin: 0000% 0000%; } }", "css`{ div { transform-origin: ${x}% ${y}%; } }`");
isTemplateText(t, "{ div { transform-origin: lit$analyzer$% lit$analyzer$%; } }", "css`{ div { transform-origin: ${x}% ${y}%; } }`");
});

tsTest("Substitute for template last in css list", t => {
isTemplateText(t, "{ div { border: 2px solid ________; } }", "css`{ div { border: 2px solid ${COLOR}; } }`");
isTemplateText(t, "{ div { border: 2px solid lit$analyzer$; } }", "css`{ div { border: 2px solid ${COLOR}; } }`");
});

tsTest("Substitute for template first in css list", t => {
isTemplateText(t, "{ div { border: ________ solid #ffffff; } }", "css`{ div { border: ${WIDTH} solid #ffffff; } }`");
isTemplateText(t, "{ div { border: lit$analyzer$ solid #ffffff; } }", "css`{ div { border: ${WIDTH} solid #ffffff; } }`");
});

tsTest("Substitute for template middle in css list", t => {
isTemplateText(t, "{ div { border: 2px ________ #ffffff; } }", "css`{ div { border: 2px ${STYLE} #ffffff; } }`");
isTemplateText(t, "{ div { border: 2px lit$analyzer$ #ffffff; } }", "css`{ div { border: 2px ${STYLE} #ffffff; } }`");
});

tsTest("Substitute for template css key-value pair", t => {
isTemplateText(t, "{ div { $_:_______________________; } }", "css`{ div { ${unsafeCSS('color: red')}; } }`");
isTemplateText(t, "{ div { lit$analyzer$; } }", "css`{ div { ${unsafeCSS('color: red')}; } }`");
});

tsTest("Substitute for template css value only", t => {
isTemplateText(t, "{ div { color: ___________________; } }", "css`{ div { color: ${unsafeCSS('red')}; } }`");
isTemplateText(t, "{ div { color: lit$analyzer$; } }", "css`{ div { color: ${unsafeCSS('red')}; } }`");
});

tsTest("Substitute for template css key only", t => {
isTemplateText(t, "{ div { $____________________: red; } }", "css`{ div { ${unsafeCSS('color')}: red; } }`");
isTemplateText(t, "{ div { lit$analyzer$: red; } }", "css`{ div { ${unsafeCSS('color')}: red; } }`");
});