pngwn / MDsveX

A markdown preprocessor for Svelte.
https://mdsvex.pngwn.io
MIT License
2.27k stars 96 forks source link

Cannot use custom (preprocessed) syntax in mdsvex files #116

Open TheComputerM opened 3 years ago

TheComputerM commented 3 years ago

I tried to use SCSS in my layout svelte file and it gave me the error:

(node:14140) UnhandledPromiseRejectionWarning: ParseError: Colon is expected
    at error$1 (D:\Mudit and Harshit\Projects\svelte-materialify\site\node_modules\svelte\compiler.js:15595:20)
    at Parser$1.error (D:\Mudit and Harshit\Projects\svelte-materialify\site\node_modules\svelte\compiler.js:15671:10)
    at Object.read_style [as read] (D:\Mudit and Harshit\Projects\svelte-materialify\site\node_modules\svelte\compiler.js:11997:21)
    at tag (D:\Mudit and Harshit\Projects\svelte-materialify\site\node_modules\svelte\compiler.js:14737:34)
    at new Parser$1 (D:\Mudit and Harshit\Projects\svelte-materialify\site\node_modules\svelte\compiler.js:15630:22)
    at Object.parse$3 [as parse] (D:\Mudit and Harshit\Projects\svelte-materialify\site\node_modules\svelte\compiler.js:15761:21)
    at process_layouts (D:\Mudit and Harshit\Projects\svelte-materialify\site\node_modules\mdsvex\dist\main.cjs.js:25708:26)
    at Object.mdsvex (D:\Mudit and Harshit\Projects\svelte-materialify\site\node_modules\mdsvex\dist\main.cjs.js:25799:12)
    at Object.<anonymous> (D:\Mudit and Harshit\Projects\svelte-materialify\site\rollup.config.js:32:10)
    at Module._compile (internal/modules/cjs/loader.js:1176:30)

But when I removed the lang attribute from my style tag and used CSS instead of SCSS, it showed no errors and worked fine.

pngwn commented 3 years ago

Do you have a reproduction I could take a look at?

TheComputerM commented 3 years ago

The project is quite large, I am sending a video to demonstrate instead.

TheComputerM commented 3 years ago

https://youtu.be/IBU5thnrtHU

My preprocess config:

const preprocess = [
  mdsvex({
    extension: '.svx',
    layout: './src/helpers/MDXLayout.svelte',
    remarkPlugins: [
      require('remark-sectionize'),
      [
        require('remark-class-names'),
        {
          classMap: {
            'heading[depth=1]': 'text-h2 mb-4',
            'heading[depth=2]': 'text-h3 mb-3',
            'heading[depth=3]': 'text-h4 mb-2',
          },
        },
      ],
      require('remark-external-links'),
      require('remark-slug'),
      [
        require('remark-autolink-headings'),
        {
          content: {
            type: 'element',
            tagName: 'i',
            properties: { className: ['mdi', 'mdi-pound'] },
          },
        },
      ],
    ],
    highlight: {
      highlighter: (code, lang) => {
        if (lang && Prism.languages[lang]) {
          const parsed = Prism.highlight(code, Prism.languages[lang]);
          const escaped = parsed
            .replace(/{/g, '&#123;')
            .replace(/}/g, '&#125;');
          const langTag = 'language-' + lang;
          const codeTag = `<code class=${langTag}>${escaped}</code>`;
          const pre = `<pre class=${langTag}>${codeTag}</pre>`;
          return `<Components.CodeBlock lang='${lang}'>${pre}</Components.CodeBlock>`;
        } else {
          const escaped = code.replace(/{/g, '&#123;').replace(/}/g, '&#125;');
          const pre = `<pre><code>${escaped}</code></pre>`;
          return `<Components.CodeBlock>${pre}</Components.CodeBlock>`;
        }
      },
    },
  }),
  sveltePreprocess({
    scss: {
      includePaths: ['theme'],
    },
    postcss: {
      plugins: [require('autoprefixer')],
    },
  }),
];

Sorry for the delay

TheComputerM commented 3 years ago

Interesting thing was that when I was first removed the lang tag, then ran the sapper dev then added the lang tag back in, there were no errors.

TheComputerM commented 3 years ago

I tried moving the sveltePreprocess to the start of the array but the outcome was still the same

pngwn commented 3 years ago

This is the same issue as #51 which was closed but never actually went away.

I have traced this one down and the issue here is not that preprocessors don't run but that mdsvex is mangling the output. mdsvex uses the svelte parser internally to get some semantics about the document, sadly, if there is non-standard syntax (especially relevant to style and script contents) then this parse will fail. mdsvex handles this case without erroring but since there is no information about that chunk of the source then the AST transform can sometimes fail to hoist script and style elements properly when wrapping the document in a layout.

Although this issue has surfaced when using scss, I think using typescript in the script element would also cause the same issue.

I will be refactoring some of the internals significantly to parse the markup itself fully, building a complete AST with no raw html parts, giving me complete information about the document. This will not use the svelte compiler which requires standard javascript and css but will be a custom parser that is completely language agnostic. This agnosticism will apply to the contents of all script and style tags and the content of any expressions in the markup itself. It will also perform no validation on block names, directive names, number of script and style tags, etc. Hopefully this will make the parser flexible enough to be used in most contexts with most preprocessors, even if they do pretty weird stuff. This will introduce one or two limitations that the svelte parser does not have but I think that is a worthwhile tradeoff and these limitations will be clearly documented.

ryanfiller commented 3 years ago

@TheComputerM I'm running into a similar issue, and I think I've got a messy-but-potential workaround. It looks like you're trying to use sveltePreprocess to do some css transforms. I was having issues with the same thing, specifically globalStyle and scss in my case. What I found is that those can be used in the project, just not directly in a file used as a layout. I was able to string this together to accomplish what I needed, maybe you can do something similar for now?

// rollup.config.js
import { mdsvex } from 'mdsvex'
import { globalStyle, scss } from 'svelte-preprocess'

const preprocess = [
    mdsvex({
        layout: {
            _: 'src/layouts/default.svelte',
    }),
    scss(),
    globalStyle()
]
// layouts/default.svelte

<script>
  import Styles from './styles.svelte'
</script>

<Styles />

<div>
  <slot />
</div>
// style.svelte

<style global type='text/scss'>
  $red: red;
  div {
    color: $red;
  }

  @import './test.scss';
</style>
// test.scss

$blue: blue; 

div {
  border: 1px solid green;
}

This works for me, I'm able to import a variable, apply it in a style tag, and set that style tag to apply globally, then import that into my layout. This should work for you postcss plugin, I'm not sure about includePaths.

emme1444 commented 3 years ago

I will be refactoring some of the internals significantly

Hey @pngwn! How's this going? Just wonderin'

pngwn commented 3 years ago

Well I did almost all of the work (in the svelte-parse package in this repo) and now I need to do it all again because the remark APIs changed significantly.

I don't have an eta on this but I am about to start actively working on it. Situation completely sucks but it is what it is sadly.

emme1444 commented 3 years ago

Oh well, that does seem to suck... Keep us updated. And great work so far!

michaeloliverx commented 3 years ago

I hit an error while trying to use typescript in my layout files like this:

<script lang="ts">
  export let title: string;
</script>

<div class="mt-8 mx-auto prose">
  <h1>{title}</h1>
  <slot />
</div>
michaeloliverx commented 2 years ago

A workaround for the TypeScript issue is to use JSDoc types:

<script>
  /** @type {import("$lib/types/blog").Post} */
  const post = { ...$$restProps };
</script>
jfcieslak commented 2 years ago

A rather simple workaround to this that seems to do the job well, is to wrap your actual Layout in another "plain" svelte component, that only passes down frontmatter props. Apprently whatever is imported into layout file is still correctly preprocessed. E.g.:

<!-- WrappedLayout.svelte -->
<script lang="ts">
    import Layout from './Layout.svelte'
    export let someProp = ''
</script>

<Layout {someProp}>
    <slot />
</Layout>

The above will work just fine even if Layout.svelte is written in typescript or needs other preprocessors.

khaki32 commented 2 years ago

Thank you @jfcieslak! Until this is issue is fixed, it's a great workaround.

tillcarlos commented 1 year ago

Can confirm - works well for us. Thanks for pointing this out <3