UNPKG

@google/clasp

Version:

Develop Apps Script Projects locally

124 lines (123 loc) 6.99 kB
// 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 defines the 'create-script' (alias 'create') command for the clasp // CLI. import path from 'node:path'; import { Command } from 'commander'; import inflection from 'inflection'; import { intl } from '../intl.js'; import { withSpinner } from './utils.js'; // https://developers.google.com/drive/api/v3/mime-types const DRIVE_FILE_MIMETYPES = { docs: 'application/vnd.google-apps.document', forms: 'application/vnd.google-apps.form', sheets: 'application/vnd.google-apps.spreadsheet', slides: 'application/vnd.google-apps.presentation', }; export const command = new Command('create-script') .alias('create') .description('Create a script') .option('--type <type>', 'Creates a new Apps Script project attached to a new Document, Spreadsheet, Presentation, Form, or as a standalone script, web app, or API.', 'standalone') .option('--title <title>', 'The project title.') .option('--parentId <id>', 'A project parent Id.') .option('--rootDir <rootDir>', 'Local root directory in which clasp will store your project files.') .action(async function () { var _a; const options = this.optsWithGlobals(); const clasp = options.clasp; if (clasp.project.exists()) { const msg = intl.formatMessage({ id: "kk5+4G", defaultMessage: [{ type: 0, value: "Project file already exists." }] }); this.error(msg); } // Create defaults. const parentId = options.parentId; const name = options.title ? options.title : getDefaultProjectName(process.cwd()); const type = options.type ? options.type.toLowerCase() : 'standalone'; const rootDir = (_a = options.rootDir) !== null && _a !== void 0 ? _a : '.'; clasp.withContentDir(rootDir); let scriptId; let createdParentId; // Handle container-bound script creation (e.g., for Sheets, Docs, Forms, Slides). if (type && type !== 'standalone') { const mimeType = DRIVE_FILE_MIMETYPES[type]; // Look up MIME type for the specified container type. if (!mimeType) { // If the type is invalid or not supported for container-bound scripts. const msg = intl.formatMessage({ id: "d2MBtN", defaultMessage: [{ type: 0, value: "Invalid container file type" }] }); this.error(msg); } const spinnerMsg = intl.formatMessage({ id: "TMfpGK", defaultMessage: [{ type: 0, value: "Creating script..." }] }); // This call creates both the Google Drive file (e.g., a new Spreadsheet) // and the Apps Script project bound to it. const result = await withSpinner(spinnerMsg, async () => await clasp.project.createWithContainer(name, mimeType)); scriptId = result.scriptId; createdParentId = result.parentId; if (!options.json) { const parentUrl = `https://drive.google.com/open?id=${createdParentId}`; // URL to the container file. const scriptUrl = `https://script.google.com/d/${scriptId}/edit`; // URL to the new Apps Script project. const successMessage = intl.formatMessage({ id: "yf9wXJ", defaultMessage: [{ type: 0, value: "Created new document: " }, { type: 1, value: "parentUrl" }, { type: 1, value: "br" }, { type: 0, value: "Created new script: " }, { type: 1, value: "scriptUrl" }] }, { parentUrl, scriptUrl, br: '\n', }); console.log(successMessage); } } else { // Handle standalone script creation. const spinnerMsg = intl.formatMessage({ id: "TMfpGK", defaultMessage: [{ type: 0, value: "Creating script..." }] }); // This call creates a standalone Apps Script project. // If `parentId` is provided, it attempts to create it within that Drive folder. scriptId = await withSpinner(spinnerMsg, async () => await clasp.project.createScript(name, parentId)); if (!options.json) { const parentUrl = `https://drive.google.com/open?id=${parentId}`; // URL to parent folder if specified. const scriptUrl = `https://script.google.com/d/${scriptId}/edit`; // URL to the new Apps Script project. const successMessage = intl.formatMessage({ id: "0a429S", defaultMessage: [{ type: 0, value: "Created new script: " }, { type: 1, value: "scriptUrl" }, { type: 5, value: "parentId", options: { undefined: { value: [] }, other: { value: [{ type: 1, value: "br" }, { type: 0, value: "Bound to document: " }, { type: 1, value: "parentUrl" }] } } }] }, { parentId, parentUrl, scriptUrl, br: '\n', }); console.log(successMessage); } } const spinnerMsg = intl.formatMessage({ id: "UTMHnH", defaultMessage: [{ type: 0, value: "Cloning script..." }] }); // After creating the script (either standalone or container-bound), // pull its initial files (e.g., Code.gs, appsscript.json) to the local directory. const files = await withSpinner(spinnerMsg, async () => { const files = await clasp.files.pull(); // Update the local .clasp.json with the new scriptId and other settings. clasp.project.updateSettings(); return files; }); if (options.json) { console.log(JSON.stringify({ scriptId, parentId: createdParentId, files: files.map(f => f.localPath) }, null, 2)); return; } // Log the paths of the pulled files. files.forEach(f => console.log(`└─ ${f.localPath}`)); const successMessage = intl.formatMessage({ id: "XABSyD", defaultMessage: [{ type: 0, value: "Cloned " }, { type: 6, value: "count", options: { "=0": { value: [{ type: 0, value: "no files." }] }, one: { value: [{ type: 0, value: "one file." }] }, other: { value: [{ type: 7 }, { type: 0, value: " files" }] } }, offset: 0, pluralType: "cardinal" }, { type: 0, value: "." }] }, { count: files.length, }); console.log(successMessage); }); /** * Generates a default project name based on the current directory's basename. * It humanizes the directory name (e.g., 'my-project-folder' becomes 'My project folder'). * @param {string} dir - The directory path from which to derive the project name. * @returns {string} The humanized default project name. */ export function getDefaultProjectName(dir) { const dirName = path.basename(dir); return inflection.humanize(dirName); }