martijnversluis / ChordSheetJS

A JavaScript library for parsing and formatting chords and chord sheets
https://github.com/users/martijnversluis/projects/4
GNU General Public License v2.0
318 stars 50 forks source link

Use of fs at runtime prevents use in browser #546

Closed Hasenn closed 2 years ago

Hasenn commented 2 years ago

In the context of a web app that reads various chordpro from different urls I would prefer having it parsed on the browser to have my app be distributed via a CDN than having to offer an API to proxy and parse the different songs.

But when doing that, i get this error

./node_modules/handlebars/lib/index.js:17:0
Module not found: Can't resolve 'fs'

Import trace for requested module:
./node_modules/chordsheetjs/lib/index.js
./lib/useChords.js
./pages/index.js

https://nextjs.org/docs/messages/module-not-found

my code just does this (client-side)

const parser = ChordSheetJS.ChordProParser();
const song = parser.parse(data);
const formatter = new ChordSheetJS.HtmlDivFormatter();
const disp = formatter.format(song);

Possible culprit is src/normalize_mappings/generate-suffix-normalize-mapping.ts which uses fs to read and write some things. I think this is part of some build-time codegen thing, in which case it shouldn't ask for fs at runtime maybe it gets bundled by mistake ?

Hasenn commented 2 years ago

also in bundle.js i get this

// Publish a Node.js require() handler for .handlebars and .hbs files
function extension(module, filename) {
  var fs = require('fs');
  var templateString = fs.readFileSync(filename, 'utf8');
  module.exports = handlebars.compile(templateString);
}
/* istanbul ignore else */
if (typeof require !== 'undefined' && require.extensions) {
  require.extensions['.handlebars'] = extension;
  require.extensions['.hbs'] = extension;
}
martijnversluis commented 2 years ago

Hey @Hasenn,

Thanks for reaching out. Indeed, the library as packaged node module is not suitable for use without build tool.

However, every recent release on GitHub includes a browserify'd JS bundle. Go to the latest ChordSheetJS release and download bundle.js. When including that file, it will expose a global variable that contains all functionality.

Below an example HTML file:

<html>
<head>
  <script src="bundle.js"></script>
</head>
<body>
  <script type="text/plain" id="chordSheet">
{title: Let it be}
{subtitle: ChordSheetJS example version}
{composer: John Lennon}
{composer: Paul McCartney}
{Chorus}

Written by: %{composer}

Let it [Am]be, let it [C/G]be, let it [F]be, let it [C]be
[C]Whisper words of [G]wisdom, let it [F]be [C/E] [Dm] [C]
  </script>
  <style>
    body {
      font-family: sans-serif;
    }
  </style>
  <script>
    const {
      ChordProParser,
      HtmlDivFormatter,
    } = ChordSheetJS;

    const parser = new ChordProParser();
    const chordpro = document.getElementById("chordSheet").innerText;
    const song = parser.parse(chordpro);
    const formatter = new HtmlDivFormatter();
    const disp = formatter.format(song);
    const css = HtmlDivFormatter.cssString('.chordSheetViewer');

    document.write(`<style>${css}</style>`);
    document.write(`<div class="chordSheetViewer">${disp}</div>`);
  </script>
</body>
</html>

This should result in something looking like this:

image

Please let me know if you need more info or experience any other issue.

Hasenn commented 2 years ago

Thanks for your answer, Its nice that there is a way, but this is not ideal for me (in a react app) and it was kind of surprising that it worked that way, is this some consequence of using PEG.js ? Maybe a tool like webpack be able to help having this library work on both server-side and browser ? I don't see a reason in what the library does to require server-side only capabilities like fs

martijnversluis commented 2 years ago

Ah sorry, I missed the part where you mentioned NextJS. For a NextJS (or any React) app, I would expect the build tool of your project to correctly handle this.

If I correctly read the stacktrace, the error occurs in the Handlebars module. Handlebars registers a handler for requireing .handlebars and .hbs files. However, that handler is not used by ChordSheetJS, those templates are compiled and baked into the single index.js.

It might be a Webpack issue, found some suggestions here: https://github.com/handlebars-lang/handlebars.js/issues/1174