Apps Script Addon. Part 1. Cooking Svelte+Material+Clasp

Max Makhrov
15 min readMar 20, 2023

--

The first result will look like that:

The first dummy sidebar will look like this ↑

The current article shows how to install ingredients for a sidebar in Apps Script, with the front made of Svelte and Google Material.

🧙🏼‍♂️The story begins

This is my story because I want to create a beautiful add-on for Google Sheets. I decided to use Svelte, Material, and Clasp to achieve my goal.

Svelte

Svelte is a framework, simpler than popular React and Vue. Svelte is supposed to make my Apps Script work as it creates vanilla JS in the end.

Svelte is so tempting! (image from https://svelte.dev)

Google Material Design

I like Material 2 reasons for:

  • 1) it will help me to approve the addon as Google requires a similar design,
  • 2) it has lots of ready-to-use components

Clasp

Clasp is the only tool I know that is capable of connecting local development, and Apps Script.

VS Code

As I’m rooky in Web development, this will be my first editor, as it seems to be the most popular.

=>

That is my plan. There are lots of different ways to go, and I’d felt lost in the beginning. Next, I watched videos, read articles, and made my choice.

Questions

  1. Why not Vue or React? Well, I’m ready to suffer and install Svelte, knowing it is not so popular. As a benefit, I’ll be able to use Svelte, as I like the philosophy of Svelte: use less code to do the same.
  2. Will it be easy? No way! So why not just create an add-on in the Apps Script environment? Reason #1: I will make it look modern. Reason #2: I want to make the add-on live long, and it should be easy to maintain and modify it.
The modern Web-Development is the infinite path to Heaven…

Links First

Let the list of all links go next, as I hate to jump everywhere while learning one thing. The rest of the article will hold code and comments, and no links.

Some links will be by the names of the authors as this will let me easily reference them later.

About the author

Max Makhrov, writes about Google Sheets and Scripts:

  1. Twitter — custom tricks, 500+posts
  2. Site — presentation of me and my main works, open source projects.

What I want to achieve

  1. Custom Sidebars \Docs\Google—imagine a Google Sheets document open — a custom sidebar would appear on the right-hand side of the screen. Sidebars are HTML files styled with CSS and JavaScript is used to interact with the server-side code => usual Apps Script.
  2. Navigation Bar\Docs\Material — this sample will give you an idea of design I wish to have. Google Material Design team created really beautiful guidelines, showing the best practice of UI.

Major resources

  1. Visual Studio Code — or VS Code. The text editor on steroids.
  2. Node.js — the thing that installs everything
  3. Svelte — front-end framework, and a “radical” new approach to building user interfaces :)
  4. Clasp — to develop and manage Apps Script projects from your terminal rather than the Apps Script editor
  5. Material V3 — a new beautiful design system by Google, not available for the WEB yet (2023/03), but will come soon.
  6. Material V2 — an old beautiful design system by Google. We’ll use it for now

Svelte + Clasp installation

  1. 🦸🏼Adam Morris\Svelte+Rollup+Clasp\Repo— my first stop. Lots of code and brilliant ideas will be a copy-paste from this repo in the future.
  2. 🦸🏼Mike Nikles\Svelte+Rollup+Clasp\Tutorial — this is a step-by-step installation guide. It was extremely helpful for me, and I began to understand more in the process.

Svelte + Material

  1. 🦸🏼Sam Cook\Svelte+Rollup+Material\Video— this video guide has also the repo link below the video. It made my idea “alive”.

Clasp + Apps Script

  1. 🦸🏼Dmitry Kostyuk\Clasp\Article— the explanation on how to use and install Clasp = is very well written! Here’s the other good article by Dmitry — Guide to NPM Modules in Google Apps Script
  2. clasp: ignore\Clasp\Docs — ignoring files. When the file is imported to your Apps Script Project, you want to skip some files. This is important as you do not want to import everything to your Apps Script.

Web Development

A list worth watching if you are new to that, like me:

  1. Package.json\Article — package.json file will be with you all the time during the installation, importing, and testing of the project. This is the one thing, each front-end developer knows about.
  2. 🦸🏼Lihautan\Bundlers\Video —if you develop locally, there are 2 types of files => (1) files you use to code, and (2) files that are sent to the Browser, and shown to users. Bundlers create files (2) from files (1) automatically. Now as I understood, “Vite” is faster. Svelte used “Rollup” earlier, but now it is on Vite. OK, we’ll see what will work better later.
This is my Tweet :)

More Web-Development

The following links are optional:

  1. SASS in 100 sec.\Video —SASS brings variables to CSS, we should also take a look at SCSS.

Let the Carnage Begin!

Before you start, you need to install VS Code and Node.js.

Create a folder on your computer, open VS Code, and use the File menu to open that folder. VS Code hint:

  • use [Ctrl]+[Shift]+[`] to open a new Terminal window.

Please note that everything changes faster than I write these words. This means any of the suggested Terminal commands below may appear outdated now. Please google them before use.

throwing tools and computers to the night sky, DALL·E

1️⃣Svelte

When you try new stuff, it is easy to install lots of things you do not need and break the project. Let’s do it carefully now.

🟦Terminal:

npm init vite MaxVite

This line says to install Svelte with Vite bundler. Change “MaxVite” to your name if you want, we will refer to it later.

It may ask you: “Ok to proceed? (y)”. Type “y” and enter.

In the next prompt select Svelte.

In the next prompt I selected JS, please select what you prefer.

🟦Terminal:

cd MaxVite  

This command will open the subfolder “MaxVite” inside your main folder.

🟦Terminal:

npm install

Guess, need this command once. This will install npm for the selected folder. Why this one is not installed by default? Who knows…

Also, open the “MaxVite” folder in Explorer, and click the package.json file. You’ll find script commands there:

📄package.json:


"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},

This is what we need. You run them by prepending “npm run ” before their names:

🟦Terminal:

npm run dev

This command will launch your installed App and show the URL for the local site, you can now edit your app and see changes in Browser!

Be careful as this command takes your terminal. I guess it does so because it runs constantly, and if you close the terminal window, the process will stop, and your browser will show an error. The good news, you can open more terminal windows.

(New) 🟦Terminal:

npm run build

Before you run it, please note the folders you have in the project. My folders are now:

📂Project Explorer:

.vscode
node_modules
public
src

Your working code is in “src”. But appears, this code cannot be used by Browser. This means you need to use a bundler, in our case “Vite” to build it for you.

After you run “npm run build” you’ll have a new folder, called “dist”. This folder should be the “source” for your browser.

Now run npm run build and explore the folder “dist”. It should have index.html and a few other files.

Let's make an experiment. Go to the “src” folder in Explorer and:

  1. Delete everything from App.svelte. You will have your own code there.
  2. Save “App.svelte”, [Ctrl]+[S]
  3. Delete the file app.css or any .css. You do not need this one
  4. Delete ./assets and ./lib subfolders
  5. Go to Main.js and delete import './app.css'
  6. ⚠️Important! Change the target in “Main.js” to target: document.body. By this, you’ll get rid of one annoying error.
  7. Save “Main.js”.

Go to the root folder to the file “index.html” and:

  1. Delete <link rel=”icon” type=”image/svg+xml” href=”/vite.svg” />
  2. Change <title> from “Vite + Svelte” to “Thanks Max”, optionally :)
  3. Save the “index.html” file.

Now, look at your Browser, to see a clean app, without no clutter.

Run again:

🟦Terminal:

npm run build

Visit the “dist” folder again. You’ll see that the “assets” subfolder now does not have a “.css” file. I think we’ll have one later. For now, your project seems clean. And this means you have options to move it in any direction!

2️⃣Svelte + Material

I’ve found the video by Sam Cook. It works fine, but it works with Rollup. It appears the Rollup setup is outdated, and I wish to stay with Vite.

Sam Cook suggests installing a few things:

  1. Autoprefixer — for CSS compatibility across different web browsers.
  2. PostSCC — same bla-bla-bla transforming CSS.
  3. SASS — Syntactically Awesome Style Sheets. Like new CSS.
  4. node-sass — some bla-bla-bla for SASS
  5. svelte-preprocess — to make Svelte work with SCSS

After fighting with this, googling, and asking ChatGPT a lot, I’ve stopped on this code:

🟦Terminal:

npm install -D sass svelte-preprocess

As you see, we need only “sass” & “svelte-preprocess” to install with Vite. This sounds great to me as I wish to install the minimum.

Sass is a must, as it adds the possibility to write code on the modern “CSS” dialect, we cannot skip it. Is “svelte-preprocess” required also? I think, the answer is yes, but not sure. ChatGPT also says: yes. So I ran the code above.

The next steps are the modified version of the code by Sam Cook.

I’ve created a new file:

src/📄global.scss:

<style global lang="scss">
@use "@material/button/styles";
</style>

Edited main.js:

src/📄main.js:


import App from './App.svelte'
import './global.scss';

const app = new App({
target: document.body,
})

export default app

I’ve changed the target target: document.body as I do not have an element with the ID = “app” anymore. And imported ./global.scss file.

Next, I created a new file in “src”. I will later move it to the special folder called “Components”, as I plan to have lots of Material components, like butons.

src/📄MaterialButton.svelte:

<script>
import { onMount } from "svelte";
import { MDCRipple } from "@material/ripple";
onMount(() => {
const buttonRipple = new MDCRipple(document.getElementById(id));
});
export let id;
export let text;
export let onClickFunction;
</script>

<button class="mdc-button" {id} on:click={onClickFunction}>
<span class="mdc-button__ripple" />
<span class="mdc-button__label">{text}</span>
</button>

↑ This one is for my future buttons :)

and finally, I changed the main file:

src/📄App.svelte:

<script>
import MaterialButton from "./MaterialButton.svelte";
</script>

<MaterialButton
id="one"
text="first button"
onClickFunction={() => {
console.log("you just clicked the first button");
}}
/>
<MaterialButton
id="two"
text="second button"
onClickFunction={() => {
console.log("you just clicked the second button");
}}
/>

↑ This adds 2 buttons to my app. Buttons showed up, but the console showed me a few errors. I’ve closed the terminal window, running my “dev” code, and ran it again:

🟦Terminal:

npm run dev

This magically fixed my errors, and “console.log” became clean again.

Wow! I’ve got my buttons now. No errors. Clicking buttons also works fine.

Let’s move on to the next stage!

3️⃣Svelte + Material + Clasp

Now, in the third part only, I wish to add Clasp. There are 2 reasons for that.

First, not everyone needs an Apps Script project. If you just want Svelte on Vite+ Material, then your stop is here, all is set up to be happy for now.

The second reason to move Clasp installation to the end is the system of folders we create. I want to have only one Package.json in my system. Hope, it will be possible to achieve. Now, Mike Nikles and his great article helped me a lot with the process. Mike did it in a way, that the project had 2 Package.jsonfiles and I want to fix that.

The first command, suggested by Mike, sounds logical to me and I’ll run it from my root folder ↓

🟦Terminal:

npm i -D @google/clasp @types/google-apps-script

This installs Clasp itself, and google-apps-script dialect.

@google/clasp— Clasp lets us develop and manage Apps Script projects from the terminal rather than the Apps Script editor. We have no other choice here.

@types/google-apps-script— the Apps Script types assist with autocomplete in editors and Mike highly recommends us install them as it makes navigating the API much simpler. Sounds right!

We want to create a new folder for all the files that will go to the Apps Script.

🟦Terminal:

md gas

↑ I call it “gas” as I like the idea of Dmitry Kostyik, as GAS stands for “Google Apps Script”. Now we have folders:

  1. 📂src—this folder holds the code you edit for the sidebar front-end part.
  2. 📂dist—the content of this folder is a bounded version, ready to be shown by any modern Browser. This looks almost the same as we want for Apps Script, but not quite. We’ll fix it later.
  3. 📂gas—we just created a folder that will fly to our Google Apps Script, in the cloud… It is now empty, but we’ll add some magic to fill it with files to import.

We should add a claspignore file, to instruct Clasp to ignore everything except the “gas” folder.

Create a new root 📄.claspignore:

**/**
!appsscript.json
!gas/**/**

It instructs Clasp to ignore everything, except the file appsscript.json , and files inside the gas folder.

appsscript.json —file related to Apps Script with your project settings, it is not present yet but will appear soon.

Create some Apps Script inside your gas folder:

New gas/📄code.js:

function onOpen(e) {
SpreadsheetApp.getUi().createAddonMenu()
.addItem("Show sidebar", "showSidebar")
.addToUi();
}

function onInstall(e) {
onOpen(e);
}

function showSidebar() {
var ui = HtmlService
.createTemplateFromFile('gas/sidebar')
.evaluate()
.setTitle("My Svelte+Material Sidebar");
SpreadsheetApp.getUi().showSidebar(ui);
}

var include = function (filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent(); };

var ho = function() {
// this is for the furure
return 'Hello from Apps Script!'
}

↑ This code should be familiar to you if you coded in Apps Script before. It refers to Apps Script objects. The code creates a sidebar add-on menu and adds a few helper functions. Please note that the code extension is .js, Clasp will convert it to .gs during the import.

If we want Apps Script Autocomplete to work, we must add a file to import this syntax. This file must have .js extension, and it may live in the root folder. Let’s create a new file:

📄appscripthelp.js:

import "google-apps-script"

↑ Create and save this file. Next go to your gas/📄code.jsand try using SpreadsheetApp. to see if autocomplete works.

Autocomplete worked fine after I added the “appscripthelp.js" file to root!

You may notice that the appscripthelp.js will be ignored by Clasp as we did not mention it in our .claspignore exceptions. Thanks to Dmitry Kostyuk again for his tip with import "google-apps-script".

So far we have Clasp, have Apps Script Autocomplete, and have a file with a few initial Apps Script Code locally. The next thing is linking the local project with an Apps Script.

🟦Terminal:

clasp login

↑ this must be run once if you are new to Clasp. It will login you using Google Account.

🟦Terminal:

clasp create

↑ this will create a new Apps Script project linked to your local dev project. Select “sheets” project, as we’re preparing a sidebar for Google Spreadsheets:

The next useful command will open the linked Apps Script Project in Browser:

🟦Terminal:

clasp open

If you do it now, you’ll see blank project. Please note that “clasp create” command also created root-based “appscript.json” file. Take a look at it, you can now see it locally, and in your new project, too. It was created and cloned for you.

Now run

🟦Terminal:

clasp push

↑ this command will put all your local stored files and save them inside your Apps Script project. Reload the page with code in your Browser. You must be able to see your gas/code.gs file now. It is also present in your local folder, but has a ‘js’ extension.

Now, the most challenging part for me. We must add the code to bundle apps script project for Apps Script usage.

Denger zone goes next, be careful!

It’s time to recall our package.json:

📄package.json(fragment):

  "scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},

↑ this is the current state, but we need to think of the “build” command, as it must do much more for us:

  1. vite build — it must build the project as usual, to make Vite do it’s job and convert /scr files into /dist files. This is done!
  2. We should add our logic to bundle apps script files from what we’ve got in /distand put those files into /gas folder
  3. We should run the clasp push command in order to move all useful code to the web.

We need to write some of out own code here. Here’s a sample script for bundling .css. Breaking down the command:

  • echo '<style>' > ./gas/stylesheet.html writes the string <style> to the file ./gas/stylesheet.html, overwriting any existing content in that file.
  • cat ./dist/assets/index*.css >> ./gas/stylesheet.html concatenates the contents of all CSS files in the ./dist/assets/ directory that start with index and end with .css, and appends the resulting CSS to the ./gas/stylesheet.html file.
  • echo '</style>' >> ./gas/stylesheet.html writes the string </style> to the end of the ./gas/stylesheet.html file, closing the <style> tag.

In summary, this script command generates a new CSS stylesheet by creating a new stylesheet.html file in the ./gas/ directory, concatenating the contents of all CSS files in the ./dist/assets/ that start with index and end with .css, and wrapping the resulting CSS in <style> tags. The resulting stylesheet can be used to style an HTML file. Just what we want for the legal Apps Script Code, as Apps Script projects only support HTML files.

Danger! The language used by the package.json depends on the platform and environment in which they are executed.

On Unix-like systems, such as Linux or macOS, the default shell for executing command-line scripts is usually Bash. Therefore, scripts in package.json files are often written in Bash syntax and executed using the Bash shell.

On Windows systems, the default shell is usually PowerShell.

In this case, I decided to use Bash as it is more common in the Web now, and I ran this command to install Bash as a default language for package.json scripts:

🟦Terminal:

npm config set script-shell bash

↑ You may need this command only if you are on Windows as I am.

Now we know enough to get things done, thanks to the article by Mike Nikles, and to Chat-GPT. Mike splits logic into 3 actions, and I like that, will do the same. Here’s my new scripts:

📄package.json(fragment):

"scripts": {
"dev": "vite",
"build": "run-s build:ui build:ui:generate && clasp push",
"build:ui": "vite build",
"build:ui:generate": "run-p build:ui:generate:*",
"build:ui:generate:css": "echo '<style>' > ./gas/stylesheet.html && cat ./dist/assets/index*.css >> ./gas/stylesheet.html && echo '</style>' >> ./gas/stylesheet.html",
"build:ui:generate:js": "echo '<script type=\"module\">' > ./gas/javascript.html && cat ./dist/assets/index*.js >> ./gas/javascript.html && echo '</script>' >> ./gas/javascript.html",
"preview": "vite preview"
},

↑ so you need to go to the scripts section in your package.jsonand replace the text there with this new code.

Note this type=\”module\”>, this additional code will save you from unexpected errors after building a project. This is related to Vite.

Now we can use “npm run build”. It will build a project with the help of Vite, and then it will convert Vite-generated CSS and JS files into 2 HTML files. This is not the end as it runs clasp push command after that so that all files go to the Web.

Before you can run the “build” command, you need to install this:

🟦Terminal:

npm install npm-run-all --save-dev

↑ this will install npm-run-all package and make the commands valid:

  • run-scommand, used earlier, runs multiple npm scripts sequentially,
  • run-pcommand runs multiple npm scripts in parallel.

Now, you can run the command:

🟦Terminal:

npm run build

↑ please be patient, it takes some time to execute. After it ends, refresh your Apps Script project in Browser again. Surprize is waiting for you!

Say hello to gas/stylesheet.html & /gas/javascript.html

We still need to make this work in a sidebar. Create a central HTML file in “gas” folder, called “sidebar.html”:

gas/📄sidebar.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte+Material</title>
<?!= include("gas/stylesheet"); ?>
</head>

<body>
<?!= include("gas/javascript"); ?>
</body>
</html>

↑ this is just a sample HTML document, but it uses “include” methods. They work due to the include function inside code.js file, which is converted to code.gs, and this is our native Google Apps Script!

Now run npm run build again. And after that, please find a Spreadsheet, linked to your Apps Script Code, and open it. Go to the “Extensions” and find your addon menu, and run “MaxVite (or your name)” > “Show sidebar” menu. Authorize, and click your material buttons from the Spreadsheet’s sidebar:

The result is not astonishing right now, but this is just the beginning.

Final Words

This was a long path, but this is only the beginning. We’ll continue in the next article.

See you!

--

--