@google/clasp
Version:
Develop Apps Script Projects locally
160 lines (159 loc) • 7.82 kB
JavaScript
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file sets up the main CLI program for clasp. It initializes commander,
// defines global options, registers all command modules, and handles
// versioning and error handling.
import { Command, Option } from 'commander';
import { PROJECT_NAME } from '../constants.js';
import { command as cloneCommand } from './clone-script.js';
import { command as createDeploymentCommand } from './create-deployment.js';
import { command as createCommand } from './create-script.js';
import { command as createVersionCommand } from './create-version.js';
import { command as deleteDeploymentCOmand } from './delete-deployment.js';
import { command as deleteCommand } from './delete-script.js';
import { command as disableApiCommand } from './disable-api.js';
import { command as enableApiCommand } from './enable-api.js';
import { command as listApisCommand } from './list-apis.js';
import { command as listDeploymentsCommand } from './list-deployments.js';
import { command as listCommand } from './list-scripts.js';
import { command as listVersionsCommand } from './list-versions.js';
import { command as loginCommand } from './login.js';
import { command as logoutCommand } from './logout.js';
import { command as openApisConsoleCommand } from './open-apis.js';
import { command as openContainerCommand } from './open-container.js';
import { command as openAuthCommand } from './open-credentials.js';
import { command as openLogsCommand } from './open-logs.js';
import { command as openScriptCommand } from './open-script.js';
import { command as openWebappCommand } from './open-webapp.js';
import { command as pullCommand } from './pull.js';
import { command as pushCommand } from './push.js';
import { command as runCommand } from './run-function.js';
import { command as setupLogsCommand } from './setup-logs.js';
import { command as authStatusCommand } from './show-authorized-user.js';
import { command as filesStatusCommand } from './show-file-status.js';
import { command as mcpCommand } from './start-mcp.js';
import { command as tailLogsCommand } from './tail-logs.js';
import { command as updateDeploymentCommand } from './update-deployment.js';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import { readPackageUpSync } from 'read-package-up';
import { initAuth } from '../auth/auth.js';
import { initClaspInstance } from '../core/clasp.js';
import { intl } from '../intl.js';
/**
* Retrieves the version of the clasp CLI from its package.json.
* @returns {string} The version string, or 'unknown' if it cannot be determined.
*/
export function getVersion() {
const __dirname = dirname(fileURLToPath(import.meta.url));
const manifest = readPackageUpSync({ cwd: __dirname });
const version = manifest ? manifest.packageJson.version : 'unknown';
return version;
}
/**
* Creates and configures the main Commander program for the clasp CLI.
* This includes setting the version, defining global options, registering all commands,
* and setting up pre-action hooks for auth and Clasp instance initialization.
* @param { (err: CommanderError) => void } [exitOveride] - Optional function to override
* the default exit behavior of Commander, primarily for testing.
* @returns {Command} The configured Commander program instance.
*/
export function makeProgram(exitOveride) {
const version = getVersion();
const program = new Command();
program.exitOverride(exitOveride);
program.storeOptionsAsProperties(false);
/**
* Displays clasp version
*/
program.version(version, '-v, --version', 'output the current version');
program.name(PROJECT_NAME).usage('<command> [options]').description(`${PROJECT_NAME} - The Apps Script CLI`);
// This hook runs before any command's action handler.
// It's used to initialize authentication and the main Clasp instance,
// making them available to all command actions.
program.hook('preAction', async (_, cmd) => {
// `optsWithGlobals()` retrieves all options, including global ones like --auth, --project, etc.
const opts = cmd.optsWithGlobals();
// Initialize authentication based on global options.
// This will load existing credentials or prepare for a new auth flow if needed.
const auth = await initAuth({
authFilePath: opts.auth, // Path to .clasprc.json
userKey: opts.user, // User key for multi-user support
useApplicationDefaultCredentials: opts.adc, // Flag for using ADC
});
// Initialize the main Clasp instance with the (potentially) authenticated client
// and paths to project config and ignore files.
const clasp = await initClaspInstance({
credentials: auth.credentials, // Pass the OAuth2 client (if authenticated)
configFile: opts.project, // Path to .clasp.json
ignoreFile: opts.ignore, // Path to .claspignore
});
// Make the initialized `clasp` and `auth` objects available to the
// actual command's action function via its options.
cmd.setOptionValue('clasp', clasp);
cmd.setOptionValue('authInfo', auth);
});
/**
* Path to an auth file, or to a folder with a '.clasprc.json' file.
*/
program.addOption(new Option('-A, --auth <file>', "path to an auth file or a folder with a '.clasprc.json' file.").env('clasp_config_auth'));
program.option('-u,--user <name>', 'Store named credentials. If unspecified, the "default" user is used.', 'default');
program.option('--adc', 'Use the application default credentials from the environemnt.');
program.option('--json', 'Show output in JSON format');
program.addOption(new Option('-I, --ignore <file>', "path to an ignore file or a folder with a '.claspignore' file.").env('clasp_config_ignore'));
program.addOption(new Option('-P, --project <file>', "path to a project file or to a folder with a '.clasp.json' file.").env('clasp_config_project'));
const commandsToAdd = [
loginCommand,
logoutCommand,
openAuthCommand,
cloneCommand,
createCommand,
pushCommand,
pullCommand,
createDeploymentCommand,
deleteCommand,
deleteDeploymentCOmand,
listDeploymentsCommand,
updateDeploymentCommand,
disableApiCommand,
enableApiCommand,
listApisCommand,
openApisConsoleCommand,
authStatusCommand,
filesStatusCommand,
openLogsCommand,
setupLogsCommand,
tailLogsCommand,
openScriptCommand,
openContainerCommand,
openWebappCommand,
runCommand,
listCommand,
createVersionCommand,
listVersionsCommand,
mcpCommand,
];
for (const cmd of commandsToAdd) {
program.addCommand(cmd);
cmd.copyInheritedSettings(program);
}
program.on('command:*', async function (op) {
const msg = intl.formatMessage({ id: "UlHLBZ", defaultMessage: [{ type: 0, value: "Unknown command \"clasp " }, { type: 1, value: "command" }, { type: 0, value: "\"" }] }, {
command: op[0],
});
this.error(msg);
});
program.error;
return program;
}