Skip to content

Commit

Permalink
Add 'concurrency' option. Improve CLI log. (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomontalbano committed Nov 14, 2020
1 parent 2a97810 commit 56d3e37
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 31 deletions.
14 changes: 12 additions & 2 deletions packages/cli/src/commands/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ComponentsCommand extends Command {
flags: {
page,
output,
concurrency,
outputter = [],
transformer = [],
},
Expand All @@ -29,14 +30,17 @@ class ComponentsCommand extends Command {

figmaExport.components({
fileId,
concurrency,
token: process.env.FIGMA_TOKEN || '',
onlyFromPages: page,
transformers: requirePackages<FigmaExport.StringTransformer>(transformer),
outputters: requirePackages<FigmaExport.ComponentOutputter>(outputter, { output }),
log: (message: string) => { spinner.text = message; },
}).finally(() => {
spinner.stop();
}).then(() => {
spinner.succeed('done');
}).catch((error: Error) => {
spinner.fail();

// eslint-disable-next-line no-console
console.error(error);
});
Expand All @@ -58,6 +62,12 @@ ComponentsCommand.flags = {
char: 'p',
description: 'Figma page names (defaults to \'all pages\')',
}),
concurrency: commandFlags.integer({
char: 'c',
description: 'Concurrency when fetching',
default: 30,
multiple: false,
}),
output: commandFlags.string({
char: 'o',
description: 'Output directory',
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/src/commands/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ class StylesCommand extends Command {
token: process.env.FIGMA_TOKEN || '',
outputters: requirePackages<FigmaExport.StyleOutputter>(outputter, { output }),
log: (message: string) => { spinner.text = message; },
}).finally(() => {
spinner.stop();
}).then(() => {
spinner.succeed('done');
}).catch((error: Error) => {
spinner.fail();

// eslint-disable-next-line no-console
console.log(error);
});
Expand Down
13 changes: 9 additions & 4 deletions packages/cli/src/commands/use-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ class UseConfigCommand extends Command {
fileId: '',
...options,
log: (message: string) => { spinner.text = message; },
}).then((any) => {
spinner.succeed().start();
return any;
});

const commandPromises: (() => Promise<any>)[] = commands.map((command: FigmaExportCommand) => {
const [commandName, options] = command;

spinner.start();

switch (commandName) {
case 'components':
return () => runExport(figmaExport.components, options);
Expand All @@ -49,11 +50,15 @@ class UseConfigCommand extends Command {
}
});

spinner.start();

commandPromises.reduce((actualPromise, nextPromise) => {
return actualPromise.then(nextPromise);
}, Promise.resolve()).finally(() => {
spinner.stop();
}, Promise.resolve()).then(() => {
spinner.succeed('done');
}).catch((error: Error) => {
spinner.fail();

// eslint-disable-next-line no-console
console.error(error);
});
Expand Down
43 changes: 29 additions & 14 deletions packages/core/src/lib/export-components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ describe('export-component', () => {
clientFileImages = sinon.stub().returns(Promise.resolve({
data: {
images: {
A1: 'https://example.com/A1.svg',
B2: 'https://example.com/B2.svg',
'10:8': 'https://example.com/10:8.svg',
'8:1': 'https://example.com/8:1.svg',
'9:1': 'https://example.com/9:1.svg',
},
},
}));
Expand All @@ -51,9 +52,14 @@ describe('export-component', () => {
};

nockScope = nock('https://example.com', { reqheaders: { 'Content-Type': 'images/svg+xml' } })
.get('/A1.svg')
.get('/10:8.svg')
.delay(1)
.reply(200, figmaDocument.svg.content)
.get('/B2.svg')
.get('/8:1.svg')
.delay(3)
.reply(200, figmaDocument.svg.content)
.get('/9:1.svg')
.delay(2)
.reply(200, figmaDocument.svg.content);

sinon.stub(FigmaExport, 'getClient').returns(client);
Expand Down Expand Up @@ -83,13 +89,17 @@ describe('export-component', () => {
});
expect(clientFile).to.have.been.calledOnceWithExactly('fileABCD');

expect(logger).to.have.been.calledTwice;
expect(logger.firstCall).to.have.been.calledWith('fetching document');
expect(logger.secondCall).to.have.been.calledWith('fetching components');
expect(logger).to.have.been.callCount(5);
expect(logger.getCall(0)).to.have.been.calledWith('fetching document');
expect(logger.getCall(1)).to.have.been.calledWith('preparing components');
expect(logger.getCall(2)).to.have.been.calledWith('fetching components 1/3');
expect(logger.getCall(3)).to.have.been.calledWith('fetching components 2/3');
expect(logger.getCall(4)).to.have.been.calledWith('fetching components 3/3');

expect(transformer).to.have.been.calledTwice;
expect(transformer).to.have.been.calledThrice;
expect(transformer.firstCall).to.have.been.calledWith(figmaDocument.svg.content);
expect(transformer.secondCall).to.have.been.calledWith(figmaDocument.svg.content);
expect(transformer.thirdCall).to.have.been.calledWith(figmaDocument.svg.content);

expect(outputter).to.have.been.calledOnceWithExactly(pagesWithSvg);
});
Expand All @@ -101,9 +111,12 @@ describe('export-component', () => {
});

/* eslint-disable no-console */
expect(console.log).to.have.been.calledTwice;
expect((console.log as sinon.SinonSpy<unknown[], unknown>).firstCall).to.have.been.calledWith('fetching document');
expect((console.log as sinon.SinonSpy<unknown[], unknown>).secondCall).to.have.been.calledWith('fetching components');
expect(console.log).to.have.been.callCount(5);
expect((console.log as sinon.SinonSpy<unknown[], unknown>).getCall(0)).to.have.been.calledWith('fetching document');
expect((console.log as sinon.SinonSpy<unknown[], unknown>).getCall(1)).to.have.been.calledWith('preparing components');
expect((console.log as sinon.SinonSpy<unknown[], unknown>).getCall(2)).to.have.been.calledWith('fetching components 1/3');
expect((console.log as sinon.SinonSpy<unknown[], unknown>).getCall(3)).to.have.been.calledWith('fetching components 2/3');
expect((console.log as sinon.SinonSpy<unknown[], unknown>).getCall(4)).to.have.been.calledWith('fetching components 3/3');
});

it('should throw an error when fetching file fails', async () => {
Expand Down Expand Up @@ -140,14 +153,16 @@ describe('export-component', () => {
it('should throw an error when fetching svg fails', async () => {
nock.cleanAll();
nock('https://example.com', { reqheaders: { 'Content-Type': 'images/svg+xml' } })
.get('/A1.svg')
.get('/10:8.svg')
.replyWithError({ code: 'ECONNRESET', message: 'some error' })
.get('/8:1.svg')
.replyWithError({ code: 'ECONNRESET', message: 'some error' })
.get('/B2.svg')
.get('/9:1.svg')
.replyWithError({ code: 'ECONNRESET', message: 'some error' });

await expect(exportComponents({
fileId: 'fileABCD',
token: 'token1234',
})).to.be.rejectedWith(Error, 'while fetching svg "https://example.com/A1.svg": some error');
})).to.be.rejectedWith(Error, 'while fetching svg "https://example.com/10:8.svg": some error');
});
});
11 changes: 9 additions & 2 deletions packages/core/src/lib/export-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const components = async ({
onlyFromPages = [],
transformers = [],
outputters = [],
concurrency = 30,
log = (msg): void => {
// eslint-disable-next-line no-console
console.log(msg);
Expand All @@ -28,8 +29,14 @@ export const components = async ({

const pages = getPages((document), { only: onlyFromPages });

log('fetching components');
const pagesWithSvg = await enrichPagesWithSvg(client, fileId, pages, transformers);
log('preparing components');
const pagesWithSvg = await enrichPagesWithSvg(client, fileId, pages, {
transformers,
concurrency,
onFetchCompleted: ({ index, total }) => {
log(`fetching components ${index}/${total}`);
},
});

await Promise.all(outputters.map((outputter) => outputter(pagesWithSvg)));

Expand Down
31 changes: 24 additions & 7 deletions packages/core/src/lib/figma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ const filterPagesByName = (pages: readonly Figma.Canvas[], pageNames: string | s
return pages.filter((page) => only.length === 0 || only.includes(page.name));
};

type FigmaExportPagesOptions = {
type GetPagesOptions = {
only?: string | string[];
}

const getPages = (document: Figma.Document, options: FigmaExportPagesOptions = {}): FigmaExport.PageNode[] => {
const getPages = (document: Figma.Document, options: GetPagesOptions = {}): FigmaExport.PageNode[] => {
const pages = filterPagesByName(document.children as Figma.Canvas[], options.only);

return pages.map((page) => ({
Expand Down Expand Up @@ -84,17 +84,34 @@ type FigmaExportFileSvg = {
[key: string]: string;
}

type FileSvgOptions = {
transformers?: FigmaExport.StringTransformer[]
concurrency?: number
onFetchCompleted?: (data: { index: number, total: number }) => void
}

const fileSvgs = async (
client: Figma.ClientInterface,
fileId: string,
ids: string[],
svgTransformers: FigmaExport.StringTransformer[] = [],
{
concurrency = 30,
transformers = [],
// eslint-disable-next-line @typescript-eslint/no-empty-function
onFetchCompleted = () => {},
}: FileSvgOptions = {},
): Promise<FigmaExportFileSvg> => {
const images = await fileImages(client, fileId, ids);
const limit = pLimit(50);
const limit = pLimit(concurrency);
let index = 0;
const svgPromises = Object.entries(images).map(async ([id, url]) => {
const svg = await limit(() => fetchAsSvgXml(url));
const svgTransformed = await promiseSequentially(svgTransformers, svg);
const svgTransformed = await promiseSequentially(transformers, svg);

onFetchCompleted({
index: index += 1,
total: ids.length,
});

return [id, svgTransformed];
});
Expand All @@ -108,15 +125,15 @@ const enrichPagesWithSvg = async (
client: Figma.ClientInterface,
fileId: string,
pages: FigmaExport.PageNode[],
svgTransformers: FigmaExport.StringTransformer[],
svgOptions?: FileSvgOptions,
): Promise<FigmaExport.PageNode[]> => {
const componentIds = getIdsFromPages(pages);

if (componentIds.length === 0) {
throw new Error('No components found');
}

const svgs = await fileSvgs(client, fileId, componentIds, svgTransformers);
const svgs = await fileSvgs(client, fileId, componentIds, svgOptions);

return pages.map((page) => ({
...page,
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type ComponentsCommandOptions = {
onlyFromPages?: string[];
transformers?: StringTransformer[];
outputters?: ComponentOutputter[];
concurrency?: number;
}

export type StylesCommandOptions = {
Expand Down

0 comments on commit 56d3e37

Please sign in to comment.