zapier-platform-cli
Version:
The CLI for managing integrations in Zapier Developer Platform.
201 lines (168 loc) • 5.38 kB
JavaScript
'use strict';
/**
* can call this multiple times safely
*/
const strLiteral = (input) =>
!(input.startsWith("'") || input.startsWith('"')) ? `"${input}"` : input;
const interpLiteral = (input) => '`' + input + '`';
const block = (...statements) => statements.join('\n');
const RESPONSE_VAR = 'response';
// extra logic to handle inline comments
const obj = (...properties) =>
`
{${properties
// if we don't join this with new lines, then prettier will minimize its height where possible
// i was doing some trickery with ending comments with : to group things, but that wasn't working well
// could probably do it if we took the block holistically and spaced things after the fact, but that's overkill for now.
.map((p) => (p.startsWith('/') ? `\n\n${p}\n` : p + ','))
.join('')}}
`.trim();
// TypeScript version of obj that supports satisfies clause
const objTS = (type, ...properties) =>
`
{${properties
.map((p) => (p.startsWith('/') ? `\n\n${p}\n` : p + ','))
.join('')}}
`.trim() + ` satisfies ${type}`;
const exportStatement = (obj, language) =>
language === 'typescript'
? `export default ${obj}`
: `module.exports = ${obj}`;
/**
* @param {string} key could be a variable name or string value
* @param {string | undefined} value can either be a variable or actual string. or could be missing, in which case the input is treated as a variable
* @param {string | undefined} satisfiesType if provided, the property will be typed with the given type
*/
const objProperty = (key, value, satisfiesType) => {
if (value === undefined) {
return `${key}`;
}
// wrap key in quotes here in case the key isn't a valid property. prettier will remove if needed
return `'${key}': ${value}${satisfiesType ? ` satisfies ${satisfiesType}` : ''}`;
};
const variableAssignmentDeclaration = (varName, value, exportConst = false) =>
`${exportConst ? 'export const' : 'const'} ${varName} = ${value}`;
const fatArrowReturnFunctionDeclaration = (varname, args, statement) =>
`const ${varname} = (${args.join(', ')}) => ${statement}`;
const functionDeclaration = (
varName,
{ args = [], isAsync = false } = {},
...statements
) =>
`
const ${varName} = ${isAsync ? 'async ' : ''}(${args.join(', ')}) => {
${block(...statements)}
}
`.trim();
/**
* takes a bunch of object properties that get folded into z.request
*/
const zRequest = (url, ...requestOptions) => `
z.request(${obj(objProperty('url', url), ...requestOptions)})
`;
// trim here is important because ASI adds a semi after a lonely return
const returnStatement = (statement) => `return ${statement.trim()}`;
const awaitStatement = (statement) => `await ${statement.trim()}`;
const arr = (...elements) => `[${elements.join(', ')}]`;
const zResponseErr = (message, type = strLiteral('AuthenticationError')) =>
`
throw new z.errors.Error(
// This message is surfaced to the user
${message},
${type},
${RESPONSE_VAR}.status
)
`.trim();
const ifStatement = (condition, ...results) => `
if (${condition}) {
${block(...results)}
}
`;
class CommentFormatter {
constructor(comment) {
this.words = comment.split(' ');
this.currentLine = this.generateNewLine();
this.lines = [];
this.lineLimit = 80;
this.splitComment();
}
generateNewLine() {
return {
words: [],
sumWordsLength: 0,
};
}
get currentLineLength() {
return this.currentLine.sumWordsLength + this.currentLine.words.length;
}
resetLine() {
this.lines.push(this.currentLine);
this.currentLine = this.generateNewLine();
}
addWordToLine(word) {
this.currentLine.words.push(word);
this.currentLine.sumWordsLength += word.length;
}
splitComment() {
this.words.forEach((word) => {
if (word.includes('\n')) {
const [endOfCurrent, firstOfNext] = word.split('\n');
this.addWordToLine(endOfCurrent);
this.resetLine();
this.addWordToLine(firstOfNext);
return;
}
// extra 1 because that's the space this word will "cost"
if (this.currentLineLength + word.length + 1 > this.lineLimit) {
// too long, start a new line
this.resetLine();
}
this.addWordToLine(word);
});
// add the final (incomplete) line to the result
this.resetLine();
}
toString() {
return this.lines.map((line) => `// ${line.words.join(' ')}`).join('\n');
}
}
const comment = (text, leadingNewlines = 0) =>
`${'\n'.repeat(leadingNewlines)}${new CommentFormatter(text)}`;
const assignmentStatement = (variable, result) =>
`
${variable} = ${result};
`.trim();
// files look more natural when each block is spaced a bit.
const file = (...statements) =>
`
'use strict'
${statements.join('\n\n')}
`.trim();
const fileTS = (typesToImport = [], ...statements) =>
`
import type { ${typesToImport.join(', ')} } from 'zapier-platform-core';
${statements.join('\n\n')}
`.trim();
module.exports = {
arr,
assignmentStatement,
awaitStatement,
block,
comment,
exportStatement,
fatArrowReturnFunctionDeclaration,
file,
fileTS,
functionDeclaration,
ifStatement,
interpLiteral,
obj,
objProperty,
objTS,
RESPONSE_VAR,
returnStatement,
strLiteral,
variableAssignmentDeclaration,
zRequest,
zResponseErr,
};