@tokens-studio/sdk
Version:
The official SDK for Tokens Studio
158 lines • 5.86 kB
JavaScript
/* eslint-disable no-console */
import path from 'node:path';
import fs from 'node:fs/promises';
import { tasks, prompt } from '@tokens-studio/cli-kit';
import { convertTokenData } from 'style-dictionary/utils';
import { error, info, success } from './messages.js';
import chalk from 'chalk';
import { createClient } from './client.js';
import { configFileName } from './utils.js';
import { getSets } from './queries.js';
async function fetchSets(client, config) {
let sets = [];
await tasks({
start: 'Fetching token data',
end: 'Done!',
}, [
{
pending: 'tokens',
start: 'Getting tokensets',
end: 'Fetched tokensets',
while: async () => {
const fetchPage = async (page) => {
return await getSets(client, {
org: config.org,
project: config.project,
branch: config.branch,
page,
});
};
const addSets = (added) => {
sets = [...sets, ...added];
};
const firstBatch = await fetchPage(1);
addSets(firstBatch.data);
if (firstBatch.totalPages > 1) {
const subsequentBatches = await Promise.all(Array(firstBatch.totalPages - 1)
.fill(null)
.map((_, i) => fetchPage(i + 2)));
const setData = subsequentBatches.flatMap((batch) => batch.data);
addSets(setData);
}
},
onError(e) {
error(e);
throw e;
},
},
]);
return sets;
}
async function writeSetsToFs(sets, args, config) {
let yesToAll = !!process.env.CI; // in CI we always want to override
const writeSetToFs = async (set) => {
const filePath = `${path.resolve(
// assume the "output" in config is configured relative to the config path
args['--config'] ?? '', config.output, set.name.replace(/\//g, path.sep))}.json`;
const dirPath = path.dirname(filePath);
try {
await fs.lstat(dirPath);
}
catch (_err) {
await fs.mkdir(dirPath, { recursive: true });
}
let fileExists = false;
try {
const stats = await fs.lstat(filePath);
if (stats)
fileExists = true;
}
catch (_e) {
//
}
let shouldWrite = true;
if (fileExists && !yesToAll) {
const { overwriteFile } = await prompt([
{
name: 'overwriteFile',
type: 'select',
label: '',
message: `File ${chalk.cyanBright(filePath)} already exists, do you want to overwrite it?`,
choices: [
{
value: 'all',
label: 'Yes to all',
},
{
value: 'yes',
label: 'Yes',
},
{
value: 'no',
label: 'No',
},
],
initial: 'all',
},
]);
if (overwriteFile === 'all') {
yesToAll = true;
}
if (overwriteFile === 'no' || overwriteFile === 'yes') {
shouldWrite = false;
}
}
if (shouldWrite) {
await fs.writeFile(filePath, JSON.stringify(set.raw, null, '\t'));
}
};
// This could be done in parallel but due to the synchronous nature of prompts
// this is a rather big pain in the butt to parallelize because you have to set up a queuing/interrupt system..
// so not really worth it for the code complexity
// If needed, we can break out of the for loop if the user hits "Yes to all"
// and parallelize the remaining sets if performance is an issue.
let setsDoneIndex = 0;
for (const set of sets) {
await writeSetToFs(set);
setsDoneIndex++;
if (yesToAll) {
// we can now parallelize the remaining sets
break;
}
}
if (setsDoneIndex !== sets.length && yesToAll) {
await Promise.all(sets.slice(setsDoneIndex).map(writeSetToFs));
// we still have some remaining sets but we can parallize them
}
}
export async function pull(args) {
const client = createClient(args);
const resolvedConfigPath = path.resolve(args['--config'] ?? '', configFileName);
let raw;
try {
raw = await fs.readFile(resolvedConfigPath, 'utf-8');
}
catch (e) {
error(`Could not find config file at: ${chalk.cyan(resolvedConfigPath)}`);
throw e;
}
const config = JSON.parse(raw);
console.log('\r');
const sets = await fetchSets(client, config);
if (sets.length === 0) {
error(`No sets found for project with id: ${config.project}.`);
return;
}
console.log('\r');
const tokensCount = sets.reduce((acc, curr) => acc +
convertTokenData(curr.raw, { output: 'array', usesDtcg: true }).length, 0);
success(`Found ${sets.length} sets with ${tokensCount} tokens in total.`);
sets.forEach((set) => {
info(`${set.name}.json`, '', 1);
});
await writeSetsToFs(sets, args, config);
console.log(`\nSuccessfully pulled tokens into ${chalk.cyan(path.resolve(
// assume the "output" in config is configured relative to the config path
args['--config'] ?? '', config.output))}`);
}
//# sourceMappingURL=pull.js.map