Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I package an "external" text file into an elisp module?

I'm writing an elisp module that requires an external text file.

For the curious, the module integrates the interactive JS REPL for Cscript.exe with shell mode in emacs. It allows me to run an interactive javascript shell in emacs, on Windows.

This was motivated by js-comint.el, but is a separate implementation dependent upon Windows and cscript.exe.

It's currently working, but there are two distinct files: the .el file and the .js file. I'd prefer to have just one file.

The question I have is this: how can I package the external .js file which is a pre-requisite for this thing, into the .el file, so that I can have a one-file install?

I imagine I could just define a string variable with the (maybe minified) js file and insert that into the .el module. I suppose there will be a few string-escapement issues but this will work. Is that the best way? Any other suggestions?

like image 262
Cheeso Avatar asked Nov 14 '22 08:11

Cheeso


1 Answers

ok, I didn't find a better way to do this. But I did decide that embedding the Javascript code into the elisp module as a string... worked pretty well.

To make it happen, I wrote a short elisp function, which creates a new elisp module. It picks a new name based on the old name - xxxx-bundle.el instead of xxxx.el . Then it writes the content from the original .el file into a file with the new name. Then it writes a simple setq statement into the same file; the entire Javascript program is the literal string value in that statement. The thing that makes the setq easy is the pp-to-string function in elisp, which escapes all the Javascript code into a literal string suitable for use in emacs.

The fn to generate the bundle Looks like this:

(defun jsshell-produce-bundle (&optional jsshell-el bundle-el jsshell-js)
  "Produce a new .el file, which contains all the jsshell.el
function and also embeds the jsshell.js source as a string. The
resulting .el file will then be suitable for a one-file
distribution of JSShell.

JSShell depends on two pieces: jsshell.el and jsshell.js. Rather
than distributing and installing two distinct files, the bundle
embeds the .js file into the .el file, for a one-file
distribution option. This function produces that one file.

Most people will never need to use this function. It's useful only
after modifying either the original jsshell.el or the jsshell.js file,
when you want to produce a new distributable bundle. In other words, it's
useful for the developer of jsshell.el.

"
  (let ((jsshell-el (or jsshell-el
                        (concat (file-name-directory jsshell--load-path) "jsshell.el")

                        "jsshell.el")))
    (let ((bundle-el  (or bundle-el
                          (concat (file-name-directory jsshell-el) "jsshell-bundle.el")))
          (jsshell-js (or jsshell-js
                          (and jsshell-js-tmpf
                               (file-readable-p jsshell-js-tmpf)
                               jsshell-js-tmpf)
                          jsshell-location-of-jsshell-js ;; orig dev wkstation
                          (concat (file-name-directory jsshell-el) "jsshell.js")))
          jssrc)
      (with-temp-file bundle-el
        (insert (concat
                 ";;; "
                 (file-name-nondirectory bundle-el)
                 " -- JSShell generated bundle\n"))
        (insert (concat ";;\n;; generated " (current-time-string) "\n;;\n\n"))
        (insert-file-contents jsshell-el)
        (goto-char (point-max))
        (insert "\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
        (insert ";; this is the embedded Javascript code for the JS REPL\n\n")
        (setq jssrc (jsshell--minimized-js-contents jsshell-js))
        (goto-char (point-max))
        (insert (concat "(setq jsshell-js-src "
                        (pp-to-string jssrc)
                        ")\n"
                        "\n(provide '"
                        (file-name-sans-extension (file-name-nondirectory bundle-el))
                        ")\n"
                        "\n;;; "
                        (file-name-nondirectory bundle-el)
                        " ends\n"))))))

The helper fn to minimize the contents just collapses whitespace and eliminates consecutive newlines, that sort of thing.

The resulting .el file can then reference the variable, jsshell-js-src, which contains the minimized Javascript source. It looks like this:

enter image description here

I'm posting this here because I think the approach would probably be useful in other modules as well - anything that needs to bundle a separate data file.

like image 195
Cheeso Avatar answered Nov 16 '22 03:11

Cheeso