From 83aa4e6bd7e97c6a4b4fd6d9cf397c129538d8f0 Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Sun, 21 Jul 2024 19:26:46 +0200 Subject: [PATCH] improve comment handling --- .../loaders/__tests__/parseCss.test.ts | 222 +++++++++--------- packages/next-yak/loaders/babel-yak-plugin.ts | 7 +- packages/next-yak/loaders/lib/parseCss.ts | 76 +++--- 3 files changed, 158 insertions(+), 147 deletions(-) diff --git a/packages/next-yak/loaders/__tests__/parseCss.test.ts b/packages/next-yak/loaders/__tests__/parseCss.test.ts index ad920157..851f8f6e 100644 --- a/packages/next-yak/loaders/__tests__/parseCss.test.ts +++ b/packages/next-yak/loaders/__tests__/parseCss.test.ts @@ -9,32 +9,12 @@ test("parseCss inComplete css 1", () => { color: blue; `), ).toMatchInlineSnapshot(` - { - "declarations": [ - { - "closed": true, - "property": "color", - "scope": [ - { - "name": ".foo", - "type": "selector", - }, - { - "name": ".fancy", - "type": "selector", - }, - ], - "value": "blue", - }, - ], - "state": { - "currentDeclaration": { - "closed": false, - "property": "", - "scope": [], - "value": "", - }, - "currentScopes": [ + { + "declarations": [ + { + "closed": true, + "property": "color", + "scope": [ { "name": ".foo", "type": "selector", @@ -44,13 +24,33 @@ test("parseCss inComplete css 1", () => { "type": "selector", }, ], - "isInsideAtRule": false, - "isInsideComment": false, - "isInsidePropertyValue": false, - "isInsideString": false, + "value": "blue", }, - } - `); + ], + "state": { + "currentCommentState": false, + "currentDeclaration": { + "closed": false, + "property": "", + "scope": [], + "value": "", + }, + "currentScopes": [ + { + "name": ".foo", + "type": "selector", + }, + { + "name": ".fancy", + "type": "selector", + }, + ], + "isInsideAtRule": false, + "isInsidePropertyValue": false, + "isInsideString": false, + }, + } + `); }); test("parseCss inComplete css 1 ending inside a comment", () => { @@ -64,6 +64,7 @@ test("parseCss inComplete css 1 ending inside a comment", () => { { "declarations": [], "state": { + "currentCommentState": "/*", "currentDeclaration": { "closed": false, "property": "", @@ -90,7 +91,6 @@ test("parseCss inComplete css 1 ending inside a comment", () => { }, ], "isInsideAtRule": false, - "isInsideComment": true, "isInsidePropertyValue": false, "isInsideString": false, }, @@ -126,6 +126,7 @@ test("parseCss inComplete css 1 ending inside a string", () => { }, ], "state": { + "currentCommentState": false, "currentDeclaration": { "closed": false, "property": "background", @@ -153,7 +154,6 @@ test("parseCss inComplete css 1 ending inside a string", () => { }, ], "isInsideAtRule": false, - "isInsideComment": false, "isInsidePropertyValue": true, "isInsideString": "'", }, @@ -189,6 +189,7 @@ test("parseCss inComplete css 1 ending inside a double quote string", () => { }, ], "state": { + "currentCommentState": false, "currentDeclaration": { "closed": false, "property": "background", @@ -216,7 +217,6 @@ test("parseCss inComplete css 1 ending inside a double quote string", () => { }, ], "isInsideAtRule": false, - "isInsideComment": false, "isInsidePropertyValue": true, "isInsideString": "\\"", }, @@ -253,6 +253,7 @@ test("parseCss inComplete css 1 ending outside a comment", () => { }, ], "state": { + "currentCommentState": false, "currentDeclaration": { "closed": false, "property": "color", @@ -280,7 +281,6 @@ test("parseCss inComplete css 1 ending outside a comment", () => { }, ], "isInsideAtRule": false, - "isInsideComment": false, "isInsidePropertyValue": true, "isInsideString": false, }, @@ -303,79 +303,79 @@ test("parseCss inComplete css 1 with @media rule", () => { `), ).toMatchInlineSnapshot(` - { - "declarations": [ - { - "closed": true, - "property": "background", - "scope": [ - { - "name": ".foo", - "type": "selector", - }, - { - "name": ".fancy", - "type": "selector", - }, - ], - "value": "url('https://example.com')", - }, - { - "closed": true, - "property": "color", - "scope": [ - { - "name": ".foo", - "type": "selector", - }, - { - "name": ".fancy", - "type": "selector", - }, - { - "name": "@media (max-width: 600px)", - "type": "at-rule", - }, - { - "name": ".baz", - "type": "selector", - }, - ], - "value": "red", - }, - ], - "state": { - "currentDeclaration": { - "closed": false, - "property": "", - "scope": [], - "value": "", - }, - "currentScopes": [ - { - "name": ".foo", - "type": "selector", - }, - { - "name": ".fancy", - "type": "selector", - }, - { - "name": "@media (max-width: 600px)", - "type": "at-rule", - }, - { - "name": ".baz", - "type": "selector", - }, - ], - "isInsideAtRule": false, - "isInsideComment": false, - "isInsidePropertyValue": false, - "isInsideString": false, + { + "declarations": [ + { + "closed": true, + "property": "background", + "scope": [ + { + "name": ".foo", + "type": "selector", }, - } - `); + { + "name": ".fancy", + "type": "selector", + }, + ], + "value": "url('https://example.com')", + }, + { + "closed": true, + "property": "color", + "scope": [ + { + "name": ".foo", + "type": "selector", + }, + { + "name": ".fancy", + "type": "selector", + }, + { + "name": "@media (max-width: 600px)", + "type": "at-rule", + }, + { + "name": ".baz", + "type": "selector", + }, + ], + "value": "red", + }, + ], + "state": { + "currentCommentState": false, + "currentDeclaration": { + "closed": false, + "property": "", + "scope": [], + "value": "", + }, + "currentScopes": [ + { + "name": ".foo", + "type": "selector", + }, + { + "name": ".fancy", + "type": "selector", + }, + { + "name": "@media (max-width: 600px)", + "type": "at-rule", + }, + { + "name": ".baz", + "type": "selector", + }, + ], + "isInsideAtRule": false, + "isInsidePropertyValue": false, + "isInsideString": false, + }, + } + `); }); test("parseCss complete css with @media rule", () => { @@ -466,6 +466,7 @@ test("parseCss complete css with @media rule", () => { }, ], "state": { + "currentCommentState": false, "currentDeclaration": { "closed": false, "property": "", @@ -474,7 +475,6 @@ test("parseCss complete css with @media rule", () => { }, "currentScopes": [], "isInsideAtRule": false, - "isInsideComment": false, "isInsidePropertyValue": false, "isInsideString": false, }, @@ -550,6 +550,7 @@ test("parseCss inComplete css 1 with @keyframes rule", () => { }, ], "state": { + "currentCommentState": false, "currentDeclaration": { "closed": false, "property": "", @@ -558,7 +559,6 @@ test("parseCss inComplete css 1 with @keyframes rule", () => { }, "currentScopes": [], "isInsideAtRule": false, - "isInsideComment": false, "isInsidePropertyValue": false, "isInsideString": false, }, @@ -595,6 +595,7 @@ test("parseCss inComplete css 1 ending outside a // comment", () => { }, ], "state": { + "currentCommentState": false, "currentDeclaration": { "closed": false, "property": "color", @@ -622,7 +623,6 @@ test("parseCss inComplete css 1 ending outside a // comment", () => { }, ], "isInsideAtRule": false, - "isInsideComment": false, "isInsidePropertyValue": true, "isInsideString": false, }, diff --git a/packages/next-yak/loaders/babel-yak-plugin.ts b/packages/next-yak/loaders/babel-yak-plugin.ts index 6c4417bf..65cf7911 100644 --- a/packages/next-yak/loaders/babel-yak-plugin.ts +++ b/packages/next-yak/loaders/babel-yak-plugin.ts @@ -543,12 +543,13 @@ function transformYakExpressions( for (let i = 0; i < expression.cssPartQuasis.length; i++) { let cssChunk = expression.cssPartQuasis[i].replace(/\\\\/g, "\\"); const quasiExpression = expression.path.node.quasi.expressions[i]; + const isInSideComment = currentCssParserState.currentCommentState !== false; // Merge Component References directly into the css code before parsing // e.g.: // const Icon = styled.div`` // const Button = styled.button`&:${Icon} { color: red; }` - if (babelTypes.isIdentifier(quasiExpression)) { + if (babelTypes.isIdentifier(quasiExpression) && !isInSideComment) { let replaceValue: string | null = null; // Component References if (componentTypeMapping[quasiExpression.name]) { @@ -631,6 +632,10 @@ function transformYakExpressions( const parsedCss = parseCss(cssChunk, currentCssParserState); currentCssParserState = parsedCss.state; + if (parsedCss.state.currentCommentState) { + continue; + } + if (babelTypes.isTSType(quasiExpression)) { throw new Error( "Type annotations are not allowed in css template literals", diff --git a/packages/next-yak/loaders/lib/parseCss.ts b/packages/next-yak/loaders/lib/parseCss.ts index 0f07021b..e3969028 100644 --- a/packages/next-yak/loaders/lib/parseCss.ts +++ b/packages/next-yak/loaders/lib/parseCss.ts @@ -1,6 +1,6 @@ export interface ParserState { isInsideString: "'" | '"' | false; - isInsideComment: boolean; + currentCommentState: "/*" | "//" | boolean; isInsidePropertyValue: boolean; isInsideAtRule: boolean; currentScopes: CssScope[]; @@ -34,7 +34,7 @@ export function parseCss( cssString: string, initialState: ParserState = { isInsideString: false, - isInsideComment: false, + currentCommentState: false, isInsidePropertyValue: false, isInsideAtRule: false, currentScopes: [], @@ -42,7 +42,7 @@ export function parseCss( }, ): { state: ParserState; declarations: Declaration[] } { let isInsideString: "'" | '"' | false = initialState.isInsideString; - let isInsideComment = initialState.isInsideComment; + let currentCommentState = initialState.currentCommentState; let isInsidePropertyValue = initialState.isInsidePropertyValue; let currentScopes = [...initialState.currentScopes]; let currentCode = ""; @@ -56,6 +56,39 @@ export function parseCss( // Iterate over the CSS string character by character for (let index = 0; index < cssString.length; index++) { + + + // Iterate until the end of the comment + if (currentCommentState === "/*") { + // Iterate over comment + for (; index < cssString.length; index++) { + // Find end of comment + if (cssString[index] === "*" && cssString[index + 1] === "/") { + currentCommentState = false; + if (cssString[index + 2] === "\n") { + index++; + } else if (cssString[index + 2] + cssString[index + 3] === "\r\n") { + index += 2; + } + break; + } + } + // Resume iteration over CSS string from the end of the comment + index++; + continue; + } else if (currentCommentState === "//") { + // Iterate over comment + for (; index < cssString.length; index++) { + // Find end of comment + if (cssString[index] === "\n") { + currentCommentState = false; + break; + } + } + // Resume iteration over CSS string from the end of the comment + continue; + } + let previousBackSlashes = backSlashes; const currentCharacter = cssString[index]; if (currentCharacter === "\\") { @@ -82,41 +115,14 @@ export function parseCss( } // Find beginning of `/*` type comment else if (currentCharacter === "/" && cssString[index + 1] === "*") { - let index2 = index + 2; - isInsideComment = true; - - // Iterate over comment - for (; index2 < cssString.length; index2++) { - // Find end of comment - if (cssString[index2] === "*" && cssString[index2 + 1] === "/") { - isInsideComment = false; - if (cssString[index2 + 2] === "\n") { - index2++; - } else if (cssString[index2 + 2] + cssString[index2 + 3] === "\r\n") { - index2 += 2; - } - break; - } - } - // Resume iteration over CSS string from the end of the comment - index = index2 + 1; + index += 2; + currentCommentState = "/*"; continue; } // find beginning of `//` type comment else if (currentCharacter === "/" && cssString[index + 1] === "/") { - let index2 = index + 2; - isInsideComment = true; - - // Iterate over comment - for (; index2 < cssString.length; index2++) { - // Find end of comment - if (cssString[index2] === "\n") { - isInsideComment = false; - break; - } - } - // Resume iteration over CSS string from the end of the comment - index = index2 + 1; + index += 2; + currentCommentState = "//"; continue; } // check if the current selector got closed @@ -215,7 +221,7 @@ export function parseCss( return { state: { isInsideString, - isInsideComment, + currentCommentState, isInsidePropertyValue, isInsideAtRule, currentDeclaration,