mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-01-06 14:50:21 -05:00
feat: move parser to plugins-data-parser library
This commit is contained in:
parent
afe00b1977
commit
6616fc45c8
18 changed files with 317 additions and 44 deletions
25
libs/plugins-data-parser/.eslintrc.json
Normal file
25
libs/plugins-data-parser/.eslintrc.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.json"],
|
||||
"parser": "jsonc-eslint-parser",
|
||||
"rules": {
|
||||
"@nx/dependency-checks": "error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
7
libs/plugins-data-parser/README.md
Normal file
7
libs/plugins-data-parser/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# plugins-data-parser
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Building
|
||||
|
||||
Run `nx build plugins-data-parser` to build the library.
|
10
libs/plugins-data-parser/package.json
Normal file
10
libs/plugins-data-parser/package.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "plugins-data-parser",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"main": "./src/index.js",
|
||||
"typings": "./src/index.d.ts"
|
||||
}
|
19
libs/plugins-data-parser/project.json
Normal file
19
libs/plugins-data-parser/project.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "plugins-data-parser",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/plugins-data-parser/src",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/plugins-data-parser",
|
||||
"main": "libs/plugins-data-parser/src/index.ts",
|
||||
"tsConfig": "libs/plugins-data-parser/tsconfig.lib.json",
|
||||
"assets": ["libs/plugins-data-parser/*.md"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
1
libs/plugins-data-parser/src/index.ts
Normal file
1
libs/plugins-data-parser/src/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './lib/plugins-data-parser';
|
1
libs/plugins-data-parser/src/lib/models/index.ts
Normal file
1
libs/plugins-data-parser/src/lib/models/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './parsed-file.model';
|
127
libs/plugins-data-parser/src/lib/models/parsed-file.model.ts
Normal file
127
libs/plugins-data-parser/src/lib/models/parsed-file.model.ts
Normal file
|
@ -0,0 +1,127 @@
|
|||
export interface ParsedFile {
|
||||
id: string;
|
||||
name: string;
|
||||
data: FileData;
|
||||
}
|
||||
|
||||
export interface FileData {
|
||||
id: string;
|
||||
version: number;
|
||||
colors: IdData<Color>[];
|
||||
typographies: IdData<Typhography>[];
|
||||
pages: RootTail<unknown, string[]>; // Tail is an array of uuid (string)
|
||||
pagesIndex?: IdData<PageIndex>[];
|
||||
components: IdData<Components>[];
|
||||
media?: IdData<Media>[];
|
||||
}
|
||||
|
||||
export interface Color {
|
||||
color: string;
|
||||
opacity: number;
|
||||
id: string;
|
||||
name: string;
|
||||
fileId: string;
|
||||
path: string | null;
|
||||
}
|
||||
|
||||
export interface Typhography {
|
||||
lineHeight: string;
|
||||
path: string | null;
|
||||
fontStyle: string;
|
||||
textTransform: string;
|
||||
fontId: string;
|
||||
fontSize: string;
|
||||
fontWeight: string;
|
||||
name: string;
|
||||
fontVariantId: string;
|
||||
id: string;
|
||||
letterSpacing: string;
|
||||
fontFamily: string;
|
||||
modifiedAt?: Date;
|
||||
}
|
||||
|
||||
export interface PageIndex {
|
||||
options: IdData<Option>[];
|
||||
name: string;
|
||||
objects: IdData<ObjectI>[];
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
position: number;
|
||||
frameId: string;
|
||||
id: string;
|
||||
axis: null | unknown;
|
||||
x: null | unknown;
|
||||
}
|
||||
|
||||
export interface Components {
|
||||
id: string;
|
||||
name: string;
|
||||
path: string;
|
||||
objects: IdData<ObjectI>[];
|
||||
}
|
||||
|
||||
export interface ObjectI {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
rotation: 0;
|
||||
selrect: Selrect;
|
||||
points: RootTail<unknown, Point[]>;
|
||||
transform: Transform;
|
||||
transformInverse: Transform;
|
||||
parentId: null | string;
|
||||
frameId: null | string;
|
||||
}
|
||||
|
||||
export interface Selrect {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
x1: number;
|
||||
y1: number;
|
||||
x2: number;
|
||||
y2: number;
|
||||
}
|
||||
|
||||
export interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface Transform {
|
||||
a: number;
|
||||
b: number;
|
||||
c: number;
|
||||
d: number;
|
||||
e: number;
|
||||
f: number;
|
||||
}
|
||||
|
||||
export interface Media {
|
||||
id: string;
|
||||
name: string;
|
||||
width: number;
|
||||
height: number;
|
||||
mtype: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
/*****************
|
||||
* Generic types *
|
||||
*****************/
|
||||
|
||||
export interface RootTail<R, T> {
|
||||
root: R;
|
||||
tail: T;
|
||||
}
|
||||
|
||||
export interface IdData<T> {
|
||||
id: string;
|
||||
data: T;
|
||||
}
|
2
libs/plugins-data-parser/src/lib/plugins-data-parser.ts
Normal file
2
libs/plugins-data-parser/src/lib/plugins-data-parser.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { parseFile } from './utils';
|
||||
export * from './models';
|
|
@ -1,3 +1,4 @@
|
|||
export * from './object.util';
|
||||
export * from './parse-arr.util';
|
||||
export * from './parse.util';
|
||||
export * from './parse-properties.util';
|
|
@ -5,30 +5,6 @@ export function isObject(obj: unknown): boolean {
|
|||
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an object have only one property, and if that
|
||||
* property is the one passed as argument.
|
||||
*
|
||||
* examples checking property 'hello':
|
||||
*
|
||||
* { hello: 'world' } => true,
|
||||
*
|
||||
* { hello: 'world', foo: 'bar' } => false
|
||||
*/
|
||||
export function isSingleObjectWithProperty(
|
||||
object: unknown,
|
||||
property: string
|
||||
): boolean {
|
||||
if (isObject(object)) {
|
||||
return (
|
||||
Object.keys(object as Record<string, unknown>).length === 1 &&
|
||||
!!(object as Record<string, unknown>)[property]
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to camelCase from kebab-case and snake_case
|
||||
*/
|
|
@ -1,8 +1,5 @@
|
|||
import {
|
||||
isObject,
|
||||
isSingleObjectWithProperty,
|
||||
toCamelCase,
|
||||
} from './object.util';
|
||||
import { isObject, toCamelCase } from './object.util';
|
||||
import { isSingleObjectWithProperty } from './parse-properties.util';
|
||||
|
||||
interface Name {
|
||||
name: string;
|
||||
|
@ -143,13 +140,18 @@ export function parseObjArr(obj: unknown): unknown {
|
|||
|
||||
/**
|
||||
* Checks if an array is a nested array of objects
|
||||
*
|
||||
* It also checks and filter empty nested arrays
|
||||
*/
|
||||
function isNestedArray(arr: unknown[]): boolean {
|
||||
if (
|
||||
Array.isArray(arr) &&
|
||||
arr.every((a) => Array.isArray(a) && a.every((b) => isObject(b)))
|
||||
) {
|
||||
return true;
|
||||
if (Array.isArray(arr) && arr.every((a) => Array.isArray(a))) {
|
||||
// Filter empty nested arrays
|
||||
const filtered = arr.filter((a) => (a as unknown[]).length > 0);
|
||||
|
||||
// Check if every nested array is an array of objects
|
||||
return filtered.every(
|
||||
(a) => Array.isArray(a) && a.every((b) => isObject(b))
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
|
@ -0,0 +1,55 @@
|
|||
import { isObject } from '.';
|
||||
|
||||
/**
|
||||
* Checks if an object have only one property, and if that
|
||||
* property is the one passed as argument.
|
||||
*
|
||||
* examples checking property 'hello':
|
||||
*
|
||||
* { hello: 'world' } => true,
|
||||
*
|
||||
* { hello: 'world', foo: 'bar' } => false
|
||||
*/
|
||||
export function isSingleObjectWithProperty(
|
||||
object: unknown,
|
||||
property: string
|
||||
): boolean {
|
||||
if (isObject(object)) {
|
||||
return (
|
||||
Object.keys(object as Record<string, unknown>).length === 1 &&
|
||||
!!(object as Record<string, unknown>)[property]
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isSingleObjectWithProperties(
|
||||
object: unknown,
|
||||
properties: string[]
|
||||
): boolean {
|
||||
if (isObject(object)) {
|
||||
const keys = Object.keys(object as Record<string, unknown>);
|
||||
|
||||
if (keys.length === 1) {
|
||||
return properties.includes(keys[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function parseSingleProperties(
|
||||
obj: unknown,
|
||||
properties: string[]
|
||||
): unknown {
|
||||
let result = obj;
|
||||
|
||||
properties.forEach((property) => {
|
||||
if (isSingleObjectWithProperty(obj, property)) {
|
||||
result = (obj as Record<string, unknown>)[property];
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import {
|
||||
isObject,
|
||||
isSingleObjectWithProperty,
|
||||
toCamelCase,
|
||||
} from './object.util';
|
||||
import { ParsedFile } from '../models/parsed-file.model';
|
||||
import { isObject, toCamelCase } from './object.util';
|
||||
import { flattenNestedArrays, parseObjArr } from './parse-arr.util';
|
||||
import {
|
||||
isSingleObjectWithProperties,
|
||||
isSingleObjectWithProperty,
|
||||
parseSingleProperties,
|
||||
} from './parse-properties.util';
|
||||
|
||||
/**
|
||||
* Recursively cleans an object from unnecesary properties
|
||||
|
@ -42,6 +44,8 @@ export function cleanObject(
|
|||
|
||||
/**
|
||||
* Recursively checks for "arr" properties and parses them
|
||||
*
|
||||
* It also checks for useless one-property objects like uuid or root
|
||||
*/
|
||||
export function parseObject(obj: unknown): unknown {
|
||||
// If it's an array, parse each element
|
||||
|
@ -58,6 +62,16 @@ export function parseObject(obj: unknown): unknown {
|
|||
return parseObject(parsed);
|
||||
}
|
||||
|
||||
// If it's an object with only properties singleProperties, parse them
|
||||
const singleProperties = ['root', 'uuid', 'name', 'guides'];
|
||||
if (isSingleObjectWithProperties(obj, singleProperties)) {
|
||||
const parsed = parseSingleProperties(
|
||||
obj as Record<string, unknown>,
|
||||
singleProperties
|
||||
);
|
||||
return parseObject(parsed);
|
||||
}
|
||||
|
||||
// If it's an object, parse each property
|
||||
if (isObject(obj)) {
|
||||
return Object.keys(obj as Record<string, unknown>).reduce(
|
||||
|
@ -75,6 +89,6 @@ export function parseObject(obj: unknown): unknown {
|
|||
/**
|
||||
* Parse a file object into a more typescript friendly object
|
||||
*/
|
||||
export function parseFile(file: unknown): unknown {
|
||||
return parseObject(cleanObject(file));
|
||||
export function parseFile(file: unknown): ParsedFile {
|
||||
return parseObject(cleanObject(file)) as ParsedFile;
|
||||
}
|
19
libs/plugins-data-parser/tsconfig.json
Normal file
19
libs/plugins-data-parser/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
10
libs/plugins-data-parser/tsconfig.lib.json
Normal file
10
libs/plugins-data-parser/tsconfig.lib.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
|
||||
}
|
|
@ -3,7 +3,6 @@ import './lib/plugin-modal';
|
|||
|
||||
import { ɵloadPlugin } from './lib/load-plugin';
|
||||
import { setFileState, setPageState, setSelection } from './lib/api';
|
||||
import { parseFile } from './lib/utils';
|
||||
|
||||
repairIntrinsics({
|
||||
evalTaming: 'unsafeEval',
|
||||
|
@ -28,8 +27,6 @@ export function initialize(api: any) {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api.addListener('plugin-file', 'file', (file: any) => {
|
||||
// console.log('File Changed (parsed):', parseFile(file));
|
||||
|
||||
console.log('File Changed:', file);
|
||||
|
||||
setFileState(file);
|
||||
|
|
5
nx.json
5
nx.json
|
@ -16,6 +16,11 @@
|
|||
"cache": true,
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["default", "^default"]
|
||||
},
|
||||
"@nx/js:tsc": {
|
||||
"cache": true,
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["default", "^default"]
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
"skipDefaultLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"plugins-data-parser": ["libs/plugins-data-parser/src/index.ts"],
|
||||
"plugins-parser": ["libs/plugins-parser/src/index.ts"],
|
||||
"plugins-runtime": ["libs/plugins-runtime/src/index.ts"],
|
||||
"plugins-styles/*": ["libs/plugins-styles/src/*"]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue