mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2025-01-03 05:10:13 -05:00
Attempt to use Queues
This commit is contained in:
parent
c692b8834d
commit
c49fa97dbf
8 changed files with 70 additions and 66 deletions
3
package-lock.json
generated
3
package-lock.json
generated
|
@ -12,6 +12,7 @@
|
||||||
"@create-figma-plugin/ui": "^3.2",
|
"@create-figma-plugin/ui": "^3.2",
|
||||||
"base64-js": "^1.5",
|
"base64-js": "^1.5",
|
||||||
"classnames": "^2.5",
|
"classnames": "^2.5",
|
||||||
|
"fastq": "^1.17.1",
|
||||||
"lru-cache": "^10.2",
|
"lru-cache": "^10.2",
|
||||||
"preact": "^10.22",
|
"preact": "^10.22",
|
||||||
"react-hook-form": "^7.51",
|
"react-hook-form": "^7.51",
|
||||||
|
@ -4821,7 +4822,6 @@
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||||
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
|
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
}
|
}
|
||||||
|
@ -7129,7 +7129,6 @@
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"iojs": ">=1.0.0",
|
"iojs": ">=1.0.0",
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"@create-figma-plugin/ui": "^3.2",
|
"@create-figma-plugin/ui": "^3.2",
|
||||||
"base64-js": "^1.5",
|
"base64-js": "^1.5",
|
||||||
"classnames": "^2.5",
|
"classnames": "^2.5",
|
||||||
|
"fastq": "^1.17.1",
|
||||||
"lru-cache": "^10.2",
|
"lru-cache": "^10.2",
|
||||||
"preact": "^10.22",
|
"preact": "^10.22",
|
||||||
"react-hook-form": "^7.51",
|
"react-hook-form": "^7.51",
|
||||||
|
|
30
plugin-src/Queue.ts
Normal file
30
plugin-src/Queue.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import * as fastq from 'fastq';
|
||||||
|
import type { queueAsPromised } from 'fastq';
|
||||||
|
|
||||||
|
import { transformSceneNode } from '@plugin/transformers';
|
||||||
|
|
||||||
|
import { PenpotNode } from '@ui/types';
|
||||||
|
|
||||||
|
class Queue<T, R> {
|
||||||
|
private queue: queueAsPromised<T, R>;
|
||||||
|
|
||||||
|
constructor(worker: fastq.asyncWorker<unknown, T, R>, concurrency: number) {
|
||||||
|
this.queue = fastq.promise(worker, concurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enqueue(task: T): Promise<R> {
|
||||||
|
return this.queue.push(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async waitIdle() {
|
||||||
|
await this.queue.drain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const nodeQueue = new Queue(
|
||||||
|
async ([sceneNode, position]: [SceneNode, number]): Promise<[PenpotNode | undefined, number]> => [
|
||||||
|
await transformSceneNode(sceneNode),
|
||||||
|
position
|
||||||
|
],
|
||||||
|
1
|
||||||
|
);
|
|
@ -1,33 +1,27 @@
|
||||||
|
import { PenpotNode } from '@ui/types';
|
||||||
|
|
||||||
class RemoteComponentsLibrary {
|
class RemoteComponentsLibrary {
|
||||||
private components: Record<string, ComponentNode | ComponentSetNode> = {};
|
private components: PenpotNode[] = [];
|
||||||
private queue: string[] = [];
|
private registry: Record<string, null> = {};
|
||||||
|
|
||||||
public register(id: string, component: ComponentNode | ComponentSetNode) {
|
public add(component: PenpotNode) {
|
||||||
if (!Object.prototype.hasOwnProperty.call(this.components, id)) {
|
this.components.push(component);
|
||||||
this.queue.push(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.components[id] = component;
|
public register(id: string) {
|
||||||
|
this.registry[id] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(id: string): ComponentNode | ComponentSetNode | undefined {
|
public has(id: string): boolean {
|
||||||
return this.components[id];
|
return this.registry[id] === null;
|
||||||
}
|
|
||||||
|
|
||||||
public next(): ComponentNode | ComponentSetNode {
|
|
||||||
const lastKey = this.queue.pop();
|
|
||||||
|
|
||||||
if (!lastKey) throw new Error('No components to pop');
|
|
||||||
|
|
||||||
return this.components[lastKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
public remaining(): number {
|
|
||||||
return this.queue.length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public total(): number {
|
public total(): number {
|
||||||
return Object.keys(this.components).length;
|
return this.components.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public all(): PenpotNode[] {
|
||||||
|
return this.components;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { componentsLibrary } from '@plugin/ComponentLibrary';
|
import { componentsLibrary } from '@plugin/ComponentLibrary';
|
||||||
import { imagesLibrary } from '@plugin/ImageLibrary';
|
import { imagesLibrary } from '@plugin/ImageLibrary';
|
||||||
|
import { nodeQueue } from '@plugin/Queue';
|
||||||
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
||||||
import { translateRemoteChildren } from '@plugin/translators';
|
import { translateRemoteChildren } from '@plugin/translators';
|
||||||
import { sleep } from '@plugin/utils';
|
import { sleep } from '@plugin/utils';
|
||||||
|
@ -75,10 +76,12 @@ const processPages = async (node: DocumentNode): Promise<PenpotPage[]> => {
|
||||||
export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotDocument> => {
|
export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotDocument> => {
|
||||||
const children = await processPages(node);
|
const children = await processPages(node);
|
||||||
|
|
||||||
if (remoteComponentLibrary.remaining() > 0) {
|
await nodeQueue.waitIdle();
|
||||||
|
|
||||||
|
if (remoteComponentLibrary.total() > 0) {
|
||||||
children.push({
|
children.push({
|
||||||
name: 'External Components',
|
name: 'External Components',
|
||||||
children: translateRemoteChildren()
|
children: remoteComponentLibrary.all()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { overridesLibrary } from '@plugin/OverridesLibrary';
|
import { overridesLibrary } from '@plugin/OverridesLibrary';
|
||||||
|
import { nodeQueue } from '@plugin/Queue';
|
||||||
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
||||||
import {
|
import {
|
||||||
transformAutoLayout,
|
transformAutoLayout,
|
||||||
|
@ -78,11 +79,15 @@ const getPrimaryComponent = (mainComponent: ComponentNode): ComponentNode | Comp
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerExternalComponents = (primaryComponent: ComponentNode | ComponentSetNode): void => {
|
const registerExternalComponents = (primaryComponent: ComponentNode | ComponentSetNode): void => {
|
||||||
if (remoteComponentLibrary.get(primaryComponent.id) !== undefined) {
|
if (remoteComponentLibrary.has(primaryComponent.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteComponentLibrary.register(primaryComponent.id, primaryComponent);
|
remoteComponentLibrary.register(primaryComponent.id);
|
||||||
|
|
||||||
|
nodeQueue.enqueue([primaryComponent, 0]).then(([penpotNode, _]) => {
|
||||||
|
if (penpotNode) remoteComponentLibrary.add(penpotNode);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getComponentTextPropertyOverrides = (
|
const getComponentTextPropertyOverrides = (
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
import { nodeQueue } from '@plugin/Queue';
|
||||||
import { transformGroupNodeLike, transformSceneNode } from '@plugin/transformers';
|
import { transformGroupNodeLike } from '@plugin/transformers';
|
||||||
import { transformMaskFigmaIds } from '@plugin/transformers/partials';
|
import { transformMaskFigmaIds } from '@plugin/transformers/partials';
|
||||||
|
|
||||||
import { GroupShape } from '@ui/lib/types/shapes/groupShape';
|
|
||||||
import { PenpotNode } from '@ui/types';
|
import { PenpotNode } from '@ui/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,7 +16,7 @@ import { PenpotNode } from '@ui/types';
|
||||||
export const translateMaskChildren = (
|
export const translateMaskChildren = (
|
||||||
children: readonly SceneNode[],
|
children: readonly SceneNode[],
|
||||||
maskIndex: number
|
maskIndex: number
|
||||||
): Promise<PenpotNode | undefined>[] => {
|
): PenpotNode[] => {
|
||||||
const maskChild = children[maskIndex];
|
const maskChild = children[maskIndex];
|
||||||
|
|
||||||
const unmaskedChildren = translateChildren(children.slice(0, maskIndex));
|
const unmaskedChildren = translateChildren(children.slice(0, maskIndex));
|
||||||
|
@ -38,50 +37,23 @@ export const translateMaskChildren = (
|
||||||
return [...unmaskedChildren, ...maskedChildren];
|
return [...unmaskedChildren, ...maskedChildren];
|
||||||
}
|
}
|
||||||
|
|
||||||
const maskGroup = Promise.resolve<GroupShape>({
|
const maskGroup = {
|
||||||
...transformMaskFigmaIds(maskChild),
|
...transformMaskFigmaIds(maskChild),
|
||||||
...transformGroupNodeLike(maskChild),
|
...transformGroupNodeLike(maskChild),
|
||||||
children: maskedChildren,
|
children: maskedChildren,
|
||||||
maskedGroup: true
|
maskedGroup: true
|
||||||
});
|
};
|
||||||
|
|
||||||
return [...unmaskedChildren, maskGroup];
|
return [...unmaskedChildren, maskGroup];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const translateChildren = (
|
export const translateChildren = (children: readonly SceneNode[]): PenpotNode[] => {
|
||||||
children: readonly SceneNode[]
|
const transformedChildren: PenpotNode[] = [];
|
||||||
): Promise<PenpotNode | undefined>[] => {
|
let count = 0;
|
||||||
const transformedChildren: Promise<PenpotNode | undefined>[] = [];
|
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
transformedChildren.push(transformSceneNode(child));
|
nodeQueue.enqueue([child, count++]).then(([penpotNode, position]) => {
|
||||||
}
|
if (penpotNode) transformedChildren[position] = penpotNode;
|
||||||
|
|
||||||
return transformedChildren;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const translateRemoteChildren = (): Promise<PenpotNode | undefined>[] => {
|
|
||||||
const transformedChildren: Promise<PenpotNode | undefined>[] = [];
|
|
||||||
let currentRemote = 1;
|
|
||||||
|
|
||||||
figma.ui.postMessage({
|
|
||||||
type: 'PROGRESS_STEP',
|
|
||||||
data: 'remote'
|
|
||||||
});
|
|
||||||
|
|
||||||
while (remoteComponentLibrary.remaining() > 0) {
|
|
||||||
figma.ui.postMessage({
|
|
||||||
type: 'PROGRESS_TOTAL_ITEMS',
|
|
||||||
data: remoteComponentLibrary.total()
|
|
||||||
});
|
|
||||||
|
|
||||||
const child = remoteComponentLibrary.next();
|
|
||||||
|
|
||||||
transformedChildren.push(transformSceneNode(child));
|
|
||||||
|
|
||||||
figma.ui.postMessage({
|
|
||||||
type: 'PROGRESS_PROCESSED_ITEMS',
|
|
||||||
data: currentRemote++
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { PenpotNode } from '@ui/types';
|
import { PenpotNode } from '@ui/types';
|
||||||
|
|
||||||
export type Children = { children: Promise<PenpotNode | undefined>[] | (PenpotNode | undefined)[] };
|
export type Children = { children?: PenpotNode[] };
|
||||||
|
|
Loading…
Reference in a new issue