Skip to main content

bootstrap (Local Development Environment Setup)

npx gassma bootstrap is a command that sets up a local GAS development environment (clasp + esbuild + TypeScript + GASsma library) for a new project in one shot.

$ npx gassma bootstrap my-app   # create my-app/ and set up inside it
$ npx gassma bootstrap # ask for a directory first (default: gassma-project)
$ npx gassma bootstrap . # set up in the current directory

Since it runs via npx, no prior installation is required. When executed, it walks you through interactive questions and completes everything from creating the target directory and the Apps Script project to build configuration, schema file generation, and dependency installation.

note

The target directory is created by the command itself, so you do not need to prepare one in advance. Passing . sets up in the existing current directory. Existing files are skipped or merged instead of overwritten (see "Directory Handling" and "Idempotency" below).

Prerequisites

clasp must be installed and you must be logged in.

$ npm install -g @google/clasp
$ clasp login

Also, enable the Apps Script API on the Apps Script API settings page (clasp needs it to create projects).

If clasp is not found, the command shows the following message and exits safely (no files are modified).

clasp is required but was not found in your PATH.
Install it with: npm install -g @google/clasp
Then log in with: clasp login

Interactive Flow

You will be asked the following questions in order (the prompts are the actual wording).

1. Project directory?

The directory to set up the project in. Defaults to gassma-project; entering . sets up in the current directory. A directory that does not exist is created, and if it exists and is not empty, a confirmation is asked before continuing (see "Directory Handling" below).

note

If you pass a directory as a command argument (npx gassma bootstrap my-app or npx gassma bootstrap .), this question is skipped.

2. Project title?

The title of the Apps Script project to create. Defaults to the target directory name (the name of the directory given as the argument or in question 1).

3. Create a new spreadsheet as well?

With Yes (the default), a new spreadsheet is created together with a container-bound script attached to it (clasp create-script --type sheets). With No, a standalone script is created (--type standalone).

After this, clasp create-script runs and generates .clasp.json (with rootDir set to ./dist). Then the GASsma library dependency and other settings are automatically applied to dist/appsscript.json (see "What Gets Generated" below).

note

If .clasp.json already exists, this question and clasp create-script are skipped (Found an existing .clasp.json. Skipping clasp create-script.).

4. Function exposure style?

Choose how functions are exposed to GAS. Sample code for each style is shown before the choices.

export (recommended) — Uses @gassma/gas-esbuild-plugin; exported functions become GAS global functions as-is.

export const main = () => console.log("Hello GAS!");

global (esbuild-gas-plugin style) — Uses esbuild-gas-plugin; functions are exposed by assigning to the global object.

const main = () => console.log("Hello GAS!");

interface Global {
main: typeof main;
}

declare const global: Global;

global.main = main;

The plugin in the generated esbuild.mjs and the devDependencies in package.json are switched according to the selected style.

5. Linter and formatter setup?

Choose the linter and formatter setup.

ChoiceDescription
oxlint + oxfmtrecommended (the default)
eslint + prettierESLint (typescript-eslint) + Prettier
noneNo linter or formatter

With --yes, the default oxlint + oxfmt is selected. The devDependencies in package.json, the lint / lint:fix / format / format:check scripts, and the generated config files change according to the choice (see "Linter and Formatter Choice" below).

6. Generate a sample src/index.ts?

With Yes (the default), sample code for the selected style is generated as src/index.ts.

7. Install dependencies now?

With Yes (the default), dependencies are installed with the detected package manager (e.g. Install dependencies now? (npm install)). This question is not asked when --skip-install is specified.

What Gets Generated

FileDescription
.clasp.jsonGenerated by clasp create-script (rootDir: ./dist)
dist/appsscript.jsonGASsma library dependency, timeZone, exceptionLogging: STACKDRIVER, and runtimeVersion: V8 are set automatically
package.jsonbuild / push / open / deploy scripts and dependencies (plus lint / format scripts depending on question 5)
esbuild.mjsBuild configuration for the selected style
tsconfig.jsonTypeScript configuration for GAS (@types/google-apps-script)
.gitignore.clasp.json / .clasprc.json / .env / node_modules/ / dist/* (except dist/appsscript.json)
.oxlintrc.jsonoxlint configuration (only if you chose oxlint + oxfmt in question 5)
eslint.config.mjs / .prettierrcESLint / Prettier configuration (only if you chose eslint + prettier in question 5)
src/index.tsSample code (only if you answered Yes to question 6)
AGENTS.mdProject guidance for coding agents (commands, development flow, constraints, and a pointer to the GASsma reference)
gassma/schema.prisma / gassma.config.tsEquivalent to gassma init (schema with a sample User model and the config file)

dist/appsscript.json

The timeZone is detected automatically from your environment (falls back to America/New_York if detection fails). The GASsma library is added as the following entry.

{
"userSymbol": "Gassma",
"libraryId": "1ZVuWMUYs4hVKDCcP3nVw74AY48VqLm50wRceKIQLFKL0wf4Hyou-FIBH",
"version": "<latest version resolved at run time>",
"developmentMode": false
}

If the existing manifest already has exceptionLogging or runtimeVersion set, those values are preserved.

package.json Scripts

ScriptDescription
buildnode esbuild.mjs (bundles src/index.ts into dist/index.js)
pushclasp push
openclasp open-script
deploynpm run build && npm run push

If you chose oxlint + oxfmt or eslint + prettier in question 5, lint / lint:fix / format / format:check are added on top of these (see the next section for their contents).

Note that in a newly generated package.json, devDependencies are written out sorted alphabetically (formatter checks expect them to be sorted).

Linter and Formatter Choice

The devDependencies, npm scripts, and config files that get added depend on your answer to question 5.

oxlint@^1.76.0 and oxfmt@^0.61.0 are added to devDependencies.

ScriptDescription
lintoxlint
lint:fixoxlint --fix
formatoxfmt
format:checkoxfmt --check

.oxlintrc.json is generated.

{
"plugins": ["typescript"],
"categories": { "correctness": "error" },
"ignorePatterns": ["dist/**", "src/generated/**"]
}

eslint + prettier

eslint@^10.8.0 / eslint-config-prettier@^10.1.8 / prettier@^3.9.6 / typescript-eslint@^8.65.0 are added to devDependencies.

ScriptDescription
linteslint .
lint:fixeslint . --fix
formatprettier --write .
format:checkprettier --check .

eslint.config.mjs and .prettierrc are generated.

import { defineConfig, globalIgnores } from "eslint/config";
import prettier from "eslint-config-prettier/flat";
import tseslint from "typescript-eslint";

export default defineConfig([
globalIgnores(["dist/**", "src/generated/**"]),
{
files: ["**/*.ts"],
extends: [tseslint.configs.recommended, prettier],
},
]);
{
"semi": true,
"singleQuote": false,
"trailingComma": "all"
}

none

No devDependencies, scripts, or config files are added.

note

The above is all bootstrap sets up. The following are not included (add them yourself if you need them).

  • pre-commit hooks (husky / lint-staged and the like)
  • oxlint type-aware linting (oxlint-tsgolint)
  • an oxfmt config file (it is used with its defaults)

About .gitignore

.clasp.json and .clasprc.json are gitignored following the official clasp CI guide (they contain credentials and the script ID). When sharing the project with your team, restore them from your team's secret store. The same guidance is shown when setup completes.

Note: .clasp.json is gitignored. Restore it from your team's secret store when sharing this project.

Arguments and Options

ArgumentDescription
[directory]Directory to set up the project in (. for the current directory). Omitting it prompts for one
OptionDescription
--yesAnswer all prompts with their default values (non-interactive mode). Without an argument, ./gassma-project is created and set up, and the non-empty directory confirmation is answered with continue
--skip-installSkip dependency installation
--dry-runShow planned actions without writing files, creating directories, or running commands (directory creation is also shown as a plan entry like create directory my-app)
note

In a non-interactive terminal (such as CI), --yes is required. Without it, the command exits with An interactive terminal is required. Run with --yes for non-interactive mode.

Behavior Details

Directory Handling

  • If the specified directory does not exist, it is created and the project is set up inside it.
  • If it exists and is not empty, Directory "my-app" is not empty. Continue? (default No) is asked. Choosing No exits safely with Bootstrap cancelled. without changing anything. With --yes, the command continues automatically.
  • Because of this, an interrupted setup can be resumed simply by running the same command again and answering Yes to the confirmation (already generated files are skipped/merged as described in "Idempotency" below).
  • If a non-directory file with the same name already exists, the command exits with the error "my-app" already exists and is not a directory.

Idempotency

The command is designed to be safe to re-run.

  • If .clasp.json exists, clasp create-script is skipped.
  • esbuild.mjs / tsconfig.json / src/index.ts / gassma/schema.prisma / AGENTS.md are skipped if they already exist.
  • The linter / formatter config files (.oxlintrc.json / eslint.config.mjs / .prettierrc, generated only for the choice made in question 5) are likewise skipped if they already exist.
  • If package.json already exists, the bootstrap settings are merged into it (existing values win).
  • If .gitignore already exists, only the missing entries are appended.
  • If dist/appsscript.json already has the GASsma library entry, it is not added twice.

GASsma Library Version Resolution

The latest GASsma library version written to dist/appsscript.json is resolved automatically at run time.

  1. Fetch the latest library version number via clasp list-versions
  2. If that fails, fetch it from GASsma's package.json on GitHub

If both fail (e.g. offline), adding the library entry is skipped and manual instructions for adding it in the Apps Script editor (including the script ID) are shown. Re-running gassma bootstrap while online adds the entry automatically.

Package Manager Auto-Detection

npm / pnpm / yarn / bun are detected automatically (falling back to npm), and the install command and the completion guidance reflect the detected package manager.

Next Steps After Setup

When setup completes, the following steps are shown.

  1. Edit gassma/schema.prisma to define your models
  2. Run npx gassma generate to generate the typed client
  3. Run npm run deploy to build and push to Apps Script
  4. Run npm run open to open the project in the Apps Script editor

For how to write schemas and the details of gassma generate, see Local Development with Prisma Schema.