larowlan / vite-plugin-twig-drupal

Provides a vite plugin for rendering Drupal flavoured twig files with Storybook
MIT License
16 stars 6 forks source link

Include method doesn't work on Windows - transform removes backspaces in import path #24

Open McGern opened 1 week ago

McGern commented 1 week ago

OS: Windows 11 Storybook: 8.2.9 Vite: 5.4.1 vite-plugin-twig-drupal: 1.4.0

Trying to get a simple setup working with out of the box storybook (html), Vite and this package.

A simple twig file is working with a story, but when I put in a twig include, there is an error where it says it can't find the file e.g.

<!-- /src/components/button/button.twig -->
<button>button</button>
<!-- /src/components/card/card.twig -->
<div>
    <h1>{{title}}</h1>
    <p>{{message}}</p>
    {% include '@components/button/button' %}
</div>

Vite.config.js

export default defineConfig({
  plugins: [
    twig({
      namespaces: {
        components: join(__dirname, '/src/components')
      }
    })
  ],
  resolve: {
    preserveSymlinks: true
  }
});

The error looks like this image

Where it's saying it can't find the import file, but the path exists - so it seems that the namespace is working, but something else isn't.

Not sure if this is because of Vite, the plugin, storybook or Windows, but I tried to follow the examples but couldn't see what was wrong. Was wondering if Vite could resolve the twig extension?

Maybe an example of using an include would be good for noobs like me?

I don't have Drupal running, but assuming that I don't need it because it's using twigjs?

Have got a basic repo here https://github.com/McGern/sb-vite-twig. The card,twig file is working, but the hasInclude.twig isn't working.

Also tried to build the component, and it worked when using the basic card, but again not when using the included component.

Thanks.

larowlan commented 1 week ago

Can you try adding the file extension in the include call?

larowlan commented 1 week ago

I suspect this is windows related though

McGern commented 1 week ago

Thanks for the feedback.

Originally had the extension in there, but took it out to see if that was the issue.

What environment are people generally developing in - Is it MacOs or other linux based distributions?

McGern commented 1 week ago

Just a follow-up - I imported the repo into https://codesandbox.io/ and lo-and-behold it worked a treat, so it looks like Windows is the problem [insert joke about Windows always being the problem]

Sorry to keep annoying, but is this package only for specific operating systems, or do you have any pointers (or links to articles) to help with setup? I will also keep looking, because I really want to use this package, but not sure where to focus attention.

McGern commented 1 week ago

Follow-up to the follow-up

Had a better look at the error thrown, and what I thought was a quirk in the error message ended up being the issue

[vite] Internal server error: Failed to resolve import "C:GitHubsb-vite-twigsrccomponentuttoutton.twig" from "src/components/hasInclude/hasInclude.twig". Does the file exist?

Note the missing back-slashes on the import path (and some letters where the escape was a special character e.g. \b)

But the import line has the correct path

  File: C:/GitHub/sb-vite-twig/src/components/hasInclude/hasInclude.twig:7:17
  5  |
  6  |  
  7  |          import 'C:\GitHub\sb-vite-twig\src\components\button\button.twig';
     |                  ^
  8  |  
  9  |

Looking at the vite-plugin-twig-drupal\src\index.js file where the embed path is resolved through resolvePath, it is resolving as

import 'C:\\GitHub\\sb-vite-twig\\src\\components\\button\\button.twig';

So the path slashes are being escaped here, but somewhere between the generation of the string and the transformed output, this path is getting parsed twice so it ends up being

C:\\GitHub\\sb-vite-twig\\src\\components\\button\\button.twig => C:\GitHub\sb-vite-twig\src\components\button\button.twig => C:GitHubsb-vite-twigsrccomponentuttoutton.twig

On a whim, I hacked in this line to see what would happen after the embed path was calculated embed = embed.replaceAll('\\', '\\\\'); to force in another set of slashes, so by the time it was finally parsed it hopefully had the right amount of slashes. And that seemed to work.

Clearly this hack isn't a proper solution, because the package work on everything but Windows - and the issue is that Windows uses backslash for paths, but everything else uses a forward slash so the escaping isn't needed i.e. on codesandbox, "embed" is "import '/project/workspace/src/components/button/button.twig';"

UPDATE: just wondering if the hack might not be so bad after all, because these are the scenarios

???

larowlan commented 1 week ago

Personally, I use a linux environment.

I see in resolveNamespaceOrComponent I'm using a hard-coded / but that's for an SDC component. In resolveFile which is what the embeds go through I'm using NodeJS's resolve function.

I think its worth trying your approach. I wonder if we could also update the testing matrix in the GH action to have a windows runner too.

Out of interest, does the test suite pass on windows?

McGern commented 1 week ago

I needed to remove the "rm -rf dist &&" part in the build script in packages.json (because rm doesn't work in windows). Not sure if this is a windows thing as well, but vite (for me) automatically clears out the dist folder when building, so the rm step might be redundant.

I put in the hack to add the additional backspaces, and ran the tests. The ones with the snapshots failed, so I ran vitest -u and the differences seemed to be in the line spacing and some of the indenting e.g.

original

<section>
  <h1>Include</h1>
  <article>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit. At dignissimos fugiat inventore laborum maiores molestiae neque quia quo unde veniam?
      </article>
</section>

after vitest -u.

<section>
  <h1>Include</h1>
  <article>

      Lorem ipsum dolor sit amet, consectetur adipisicing elit. At dignissimos fugiat inventore laborum maiores molestiae neque quia quo unde veniam?

  </article>
</section>

It looks like the lines with the logic blocks, like {% block content %}, are being preserved after the twig file is parsed - don't know if that's a windows thing as well.

McGern commented 5 days ago

Hi.

Came across something while I was looking at this plugin https://www.npmjs.com/package/vite-plugin-static-copy (for a completely different purpose), and saw the note about the vite method "normalizePath", and had a shot at modifying the resolveFile to include this method, and it seems to work (tried in on double nested twig). Also tried it in codesandbox and also still works.


import {normalizePath} from 'vite';

const resolveFile = (directory, file) => {
  const filesToTry = [file, `${file}.twig`, `${file}.html.twig`]

  let resolvedFile = '';

  for (const ix in filesToTry) {
    const path = resolve(filesToTry[ix])
    if (existsSync(path)) {
      resolvedFile = path;
      break;
    }
    const withDir = resolve(directory, filesToTry[ix])
    if (existsSync(withDir)) {
      resolvedFile = withDir;
      break;
    }
  }

  return normalizePath(resolvedFile || resolve(directory, file));
}

Bit better than my original hack, should I try a PR?

larowlan commented 4 days ago

That sounds awesome, yes please to a pr 👌