@google/clasp
Version:
Develop Apps Script Projects locally
56 lines (55 loc) • 3.08 kB
JavaScript
import { Command } from 'commander';
import fs from 'fs/promises';
import inquirer from 'inquirer';
import { intl } from '../intl.js';
import { isInteractive, withSpinner } from './utils.js';
export const command = new Command('pull')
.description('Fetch a remote project')
.option('--versionNumber <version>', 'The version number of the project to retrieve.')
.option('-d, --deleteUnusedFiles ', 'Delete local files that are not in the remote project. Use with caution.')
.option('-f, --force', 'Forcibly delete local files that are not in the remote project without prompting.')
.action(async function (options) {
const clasp = this.opts().clasp;
const versionNumber = options.versionNumber;
const forceDelete = options.force;
let spinnerMsg = intl.formatMessage({ id: "dh7Bw6", defaultMessage: [{ type: 0, value: "Checking local files..." }] });
const localFiles = await clasp.files.collectLocalFiles();
spinnerMsg = intl.formatMessage({ id: "jilcJH", defaultMessage: [{ type: 0, value: "Pulling files..." }] });
const files = await withSpinner(spinnerMsg, async () => {
return await clasp.files.pull(versionNumber);
});
if (options.deleteUnusedFiles) {
const filesToDelete = localFiles.filter(f => !files.find(p => p.localPath === f.localPath));
await deleteLocalFiles(filesToDelete, forceDelete);
}
files.forEach(f => console.log(`└─ ${f.localPath}`));
const successMessage = intl.formatMessage({ id: "4mRAfN", defaultMessage: [{ type: 0, value: "Pulled " }, { 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);
});
async function deleteLocalFiles(filesToDelete, forceDelete = false) {
if (!filesToDelete || filesToDelete.length === 0) {
return;
}
const skipConfirmation = forceDelete;
if (!isInteractive() && !forceDelete) {
const msg = intl.formatMessage({ id: "zLuvSg", defaultMessage: [{ type: 0, value: "You are not in an interactive terminal and --force not used. Skipping file deletion." }] });
console.warn(msg);
return;
}
for (const file of filesToDelete) {
if (!skipConfirmation) {
const confirm = await inquirer.prompt({
type: 'confirm',
name: 'deleteFile',
message: intl.formatMessage({ id: "lVx/lI", defaultMessage: [{ type: 0, value: "Delete " }, { type: 1, value: "file" }, { type: 0, value: "?" }] }, { file: file.localPath }),
});
if (!confirm.deleteFile) {
continue;
}
}
await fs.unlink(file.localPath);
console.log(intl.formatMessage({ id: "Nx315v", defaultMessage: [{ type: 0, value: "Deleted " }, { type: 1, value: "file" }] }, { file: file.localPath }));
}
}