svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Preprocessing tries to process style string within script section

Open brunnerh opened this issue 3 years ago • 12 comments

Describe the bug I have a component which generates a small bit of HTML which is injected into an iframe. The HTML contains a style section which the preprocessing tries to transform, yielding a parser error because there are interpolated values in it.

(Accidentally opened this issue in svelte-preprocess first: https://github.com/sveltejs/svelte-preprocess/issues/225 The problem is in the wrapper code though.)

To Reproduce

A component like this will cause the error:

<script>
	function getItemHtml(html)
	{
		const style = window.getComputedStyle(document.documentElement);

		const background = style.getPropertyValue('--background');
		const foreground = style.getPropertyValue('--foreground');

		return html + /*html*/`<style>
			html
			{
				background: ${background};
				color: ${foreground};
			}
		</style>`;
	}
</script>

<style>
	:global(:root)
	{
		--background: #333;
		--foreground: #ddd;
	}
</style>

<div>
	HTML:
	<pre>
		{getItemHtml('Hello World')}
	</pre>
</div>

[Repository with Webpack config]

Expected behavior No attempted transform and thus no errors.

Stacktraces

Stack trace
ERROR in ./src/app.svelte
Module build failed (from ./node_modules/svelte-loader/index.js):
CssSyntaxError: .\src\app.svelte:4:19: Unknown word
    at Input.error (.\node_modules\postcss\lib\input.js:130:16)        
    at Parser.unknownWord (.\node_modules\postcss\lib\parser.js:563:22)
    at Parser.other (.\node_modules\postcss\lib\parser.js:168:12)      
    at Parser.parse (.\node_modules\postcss\lib\parser.js:77:16)       
    at parse (.\node_modules\postcss\lib\parse.js:17:12)
    at new LazyResult (.\node_modules\postcss\lib\lazy-result.js:60:16)
    at Processor.<anonymous> (.\node_modules\postcss\lib\processor.js:138:12)
    at Processor.process (.\node_modules\postcss\lib\processor.js:117:23)
    at transformer (.\node_modules\svelte-preprocess\dist\transformers\globalStyle.js:55:67)
    at Object.exports.runTransformer (.\node_modules\svelte-preprocess\dist\autoProcess.js:51:12)
    at async style (.\node_modules\svelte-preprocess\dist\autoProcess.js:171:33)
    at async .\node_modules\svelte\compiler.js:27016:32
    at async Promise.all (index 0)
    at async replace_async (.\node_modules\svelte\compiler.js:26971:52)
    at async preprocess (.\node_modules\svelte\compiler.js:27012:19)
 @ ./src/main.js 1:0-31 3:4-7
 @ multi ./src/main.js

Information about your project:

  • Your browser and the version: -
  • Your operating system: Windows 10 64bit
  • svelte version: 3.24.1
  • Webpack

Severity

Low; workarounds exist.

Workaround

Trick the Regex looking for <style>, e.g.:

return html + /*html*/`<${''}style>...</${''}style>`

brunnerh avatar Aug 19 '20 18:08 brunnerh

Preprocessing happens before any parsing of the component, and is entirely regex based. I guess I'm ambivalent about how I think something like this ought to be handled. Do we want to start searching for a <script> or a <style> (whichever comes first), and then find its matching </script>/</style> and then start the search again after that closed tag for the next <script> or <style>?

Conduitry avatar Aug 19 '20 20:08 Conduitry

I think I just ran into this obscure bug, here's the code:

<script type="text/typescript">
  function renderUserStyles(suppliedPalette) {
    if (!suppliedPalette)
      return "";

    let palette = "<style>:root {";
    let paletteCount = 0;

    suppliedPalette.map(color => {
      palette += `--user-color-${paletteCount++}: ${color};`;
    });

    palette += "}</style>";
  }
</script>

<!-- / stuff /-->

<svelte:head>
  {@html renderUserStyles(thisUser.palette)}
</svelte:head>

The error I get is [!] (plugin svelte) CssSyntaxError: ~/project/src/pages/[username]/status/[slug].svelte:1:8: Unknown word. Here is the error in full:

CssSyntaxError: ~/project/src/pages/[username]/status/[slug].svelte:1:8: Unknown word
    at Input.error (~/project/node_modules/postcss/lib/input.js:82:16)
    at Parser.unknownWord (~/project/node_modules/postcss/lib/parser.js:518:22)
    at Parser.other (~/project/node_modules/postcss/lib/parser.js:149:12)
    at Parser.parse (~/project/node_modules/postcss/lib/parser.js:59:16)
    at parse (~/project/node_modules/postcss/lib/parse.js:11:12)
    at new LazyResult (~/project/node_modules/postcss/lib/lazy-result.js:99:16)
    at Processor.process (~/project/node_modules/postcss/lib/processor.js:33:12)
    at transformer (~/project/node_modules/svelte-preprocess/dist/transformers/globalStyle.js:56:67)
    at Object.exports.transform (~/project/node_modules/svelte-preprocess/dist/autoProcess.js:37:12)
    at style (~/project/node_modules/svelte-preprocess/dist/autoProcess.js:161:33)

NetOpWibby avatar Jan 16 '21 00:01 NetOpWibby

Spent the morning today trying to figure out what's going on here. As a note for other victims of this bug, you can work around it by fooling the regex:

const styles = `<${''}style>:root { ${css} }</${''}style>`;

arggh avatar Mar 01 '21 08:03 arggh

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 26 '21 20:06 stale[bot]

This came up again in https://github.com/sveltejs/kit/issues/1796 with the slight variation that style was not nested in script but rather contained within {@html ..}.

An idea about how to approach this: First blank any script and style contents using the regex. Then try parsing the result with the Svelte parser. If that succeeds, pass on the results. If that fails, this means that the user uses markup preprocessors, too, at which point we can't do much more than falling back to the way it is now (purely regex based). This solution would be a little bit slower but more robust.

dummdidumm avatar Jul 02 '21 07:07 dummdidumm

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Dec 29 '21 07:12 stale[bot]

Had similar problems in prettier-plugin-svelte where I solved this by checking if a tag is inside another tag and remove the inner tag if so https://github.com/sveltejs/prettier-plugin-svelte/blob/master/src/lib/snipTagContent.ts

dummdidumm avatar Apr 26 '22 08:04 dummdidumm

I created https://github.com/sveltejs/svelte-preprocess/issues/507 which seems to be a duplicate of this. Wanted to highlight that this bug even occurs if the <style> string exists within a comment.

samijaber avatar Apr 26 '22 13:04 samijaber

Here is another one in the same vein:

image

Discovered this with a string I was passing to highlight.js

danawoodman avatar Sep 09 '22 00:09 danawoodman

Pasting svelte code into a template literal also causes some stuff to be added to the output string. I posted about it on Stackoverflow:

https://stackoverflow.com/questions/75223639/strange-error-with-template-literal-adding-to-string

bennymi avatar Jan 24 '23 16:01 bennymi

Got this error after updating to sveltekit v2 with this component:

<script lang="ts">
	import printCss from "$lib/styles/print.css?raw";

	export let innerHtml: string;

	const printContent = () => {
		const printWindow = window.open("", "_blank");
		if (printWindow) {
			printWindow.document.write(
				`<html><head><title>Print</title><style>${printCss}</style></head><body>`,
			);
			printWindow.document.write(innerHtml);
			printWindow.document.write("</body></html>");
			printWindow.document.close();
			printWindow.onload = () => {
				printWindow.print();
				printWindow.onafterprint = () => {};
				printWindow.close();
			};
		}
	};
</script>

<button on:click={printContent}>
	Print
</button>

@arggh 's solution worked to fix it.

rossrobino avatar Jan 05 '24 15:01 rossrobino

I also ran into this after upgrading to sveltekit 2. Followed @arggh 's workaround.

williamoverton avatar Jan 26 '24 10:01 williamoverton

Thanks @arggh still an issue for me. Is there an official solution?

andresgutgon avatar Feb 20 '24 17:02 andresgutgon

I have run into this when inlining a styled SVG into my component, which doesn't seem to be an implausible use-case. The workaround doesn't work in my case because for some reason the style isn't added as a document node if I inject it using a function. Come to think of it I probably just needed an @html here, but I've ended up taking a different approach.

I can have the style tag in my SVG, but I can't figure out how to pass it any parameters (so I can't change the fill or stroke colour, for example) without hitthing this error.

glenatron avatar Mar 18 '24 13:03 glenatron