1

I have a JavaScript file that's a generated parser (let's call it MyParser), which I am using in an add-on for Google Forms.

It needs to be used in the client side's Sidebar.html where I'm including it with HtmlService.createHtmlOutputFromFile('MyParser.js').getContent();, which means it must be an .html file as far as I understand. Then it must be used on the server side where I have it in a file MyParser.js.gs.

With my current solution, it's duplicated in my file structure:

Code.gs
MyParser.js.gs
MyParser.js.html
Sidebar.html

Is there a way I can make this work without having two files? Edit: As I understand it, libraries are only for the server side.

If not, any hints to making the updating of the two files more robust (currently it's manual copy/paste)?

Edit: According to the best practices, one must wrap the JavaScript inside a <script> tag inside the .html file:

Notice that the included files contain <style> and <script> tags because they're HTML snippets and not pure .css or .js files.

So it seems it's not easy to have just one file.

Fuhrmanator
  • 11,459
  • 6
  • 62
  • 111
  • I use browserify.org plugin to run the same javascript code on the client and server-side. I would suggest trying to use Browserify and post any issues with code samples. – Steven Friedman Jan 04 '20 at 19:00
  • 1
    Switch to local development with [@google/clasp](https://GitHub.com/Google/clasp) and you can create a workflow where you auto-generate the HTML version from the JS version using normal dev tools like webpack or browserify or roll-up or grunt or .... – tehhowch Jan 04 '20 at 20:03
  • @StevenFriedman it doesn't sound like you've used it in Google Apps Script (with CAJA - it strips a lot of stuff out for security and compatibility). Before anyone explores that rabbit hole, can you verify you've used it in GAS? – Fuhrmanator Jan 04 '20 at 20:12
  • I haven’t used it in GAS, I was just providing a possible approach that might be helpful. – Steven Friedman Jan 04 '20 at 22:46
  • @tehhowch Thanks! My life has changed. You make your comment an answer and I'll accept. By the way, once an add-on is published, to update it, is it as easy as `clasp deploy n ...` (`n` is version)? – Fuhrmanator Jan 05 '20 at 05:47
  • Feel free to self answer and provide an actual example rather than handwaving – tehhowch Jan 05 '20 at 06:44

2 Answers2

1

As far as I could tell, there's no way to reuse a single file and respect Google's best practices.

My solution, following @tehhowch's advice, was with @google/clasp and doing local development.

To build the parser (in another GitHub project), I use npm-run-script. So, I just appended a && bash makeHTML.sh to the build script.

Inside makeHTML.sh I wrapped the built MyParser.js file in a <script> tag with:

#!/usr/bin/env bash
{ echo "<script>"; cat MyParser.js; echo "</script>"; } > MyParser.js.html

Since I'm using bash it's not a true node.js solution (won't run unless there's bash installed). If someone knows of a better way to pull off the wrapping that's 100% node and doesn't require installing a whole bunch of other modules, feel free to edit the answer.

Fuhrmanator
  • 11,459
  • 6
  • 62
  • 111
1

As your add-on has just few files you might use ScriptApp.getResource(filename).getDataAsString() to get the content of a .gs file and add it to the sidebar HttpOutput object enclosed in <script> tags.

Code.gs

function showSidebar(){
  const htmlOutput = HtmlService.createHtmlOutput('<h1>My Sidebar content</h1>');
  .append(`<script>${ScriptApp.getResource('JavaScript').getDataAsString()}</script>`);
  SpreadsheetApp.showSidebar(htmlOutput,'My Sidebar');
}

JavaScript.gs
To keep things simple, this file should contain only reusable (server-side + cliente-side) JavaScript

function myFunction(){
  console.log('My function ran successfully');
}

The above over simplistic code example might be applied to a bit more complex projects and will keep you away from having to use CLASP.

Contrary as happens with .gs and .html files, so far I was unable to make ScriptApp.getResource(filename).getDataAsString() to work to pull reusable JavaScript from .gs files hosted on a Google Apps Script library. Anyway, it's still possible to stay away of CLASP, one option might be to use Advanced Service Google Apps Script API, another might be to host the shared JavaScript and imported from Google Drive or from another host by using Url Fetch service.

Related

Rubén
  • 34,714
  • 9
  • 70
  • 166
  • Actually, I'm happy now that I use clasp (makes it easier to maintain the code and make it open). But it's good to have a no-clasp solution. – Fuhrmanator Sep 26 '22 at 15:21