mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-01-22 14:49:27 -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 './object.util';
|
||||||
export * from './parse-arr.util';
|
export * from './parse-arr.util';
|
||||||
export * from './parse.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);
|
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
|
* Converts a string to camelCase from kebab-case and snake_case
|
||||||
*/
|
*/
|
|
@ -1,8 +1,5 @@
|
||||||
import {
|
import { isObject, toCamelCase } from './object.util';
|
||||||
isObject,
|
import { isSingleObjectWithProperty } from './parse-properties.util';
|
||||||
isSingleObjectWithProperty,
|
|
||||||
toCamelCase,
|
|
||||||
} from './object.util';
|
|
||||||
|
|
||||||
interface Name {
|
interface Name {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -143,13 +140,18 @@ export function parseObjArr(obj: unknown): unknown {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an array is a nested array of objects
|
* Checks if an array is a nested array of objects
|
||||||
|
*
|
||||||
|
* It also checks and filter empty nested arrays
|
||||||
*/
|
*/
|
||||||
function isNestedArray(arr: unknown[]): boolean {
|
function isNestedArray(arr: unknown[]): boolean {
|
||||||
if (
|
if (Array.isArray(arr) && arr.every((a) => Array.isArray(a))) {
|
||||||
Array.isArray(arr) &&
|
// Filter empty nested arrays
|
||||||
arr.every((a) => Array.isArray(a) && a.every((b) => isObject(b)))
|
const filtered = arr.filter((a) => (a as unknown[]).length > 0);
|
||||||
) {
|
|
||||||
return true;
|
// Check if every nested array is an array of objects
|
||||||
|
return filtered.every(
|
||||||
|
(a) => Array.isArray(a) && a.every((b) => isObject(b))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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 {
|
import { ParsedFile } from '../models/parsed-file.model';
|
||||||
isObject,
|
import { isObject, toCamelCase } from './object.util';
|
||||||
isSingleObjectWithProperty,
|
|
||||||
toCamelCase,
|
|
||||||
} from './object.util';
|
|
||||||
import { flattenNestedArrays, parseObjArr } from './parse-arr.util';
|
import { flattenNestedArrays, parseObjArr } from './parse-arr.util';
|
||||||
|
import {
|
||||||
|
isSingleObjectWithProperties,
|
||||||
|
isSingleObjectWithProperty,
|
||||||
|
parseSingleProperties,
|
||||||
|
} from './parse-properties.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively cleans an object from unnecesary properties
|
* Recursively cleans an object from unnecesary properties
|
||||||
|
@ -42,6 +44,8 @@ export function cleanObject(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively checks for "arr" properties and parses them
|
* 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 {
|
export function parseObject(obj: unknown): unknown {
|
||||||
// If it's an array, parse each element
|
// If it's an array, parse each element
|
||||||
|
@ -58,6 +62,16 @@ export function parseObject(obj: unknown): unknown {
|
||||||
return parseObject(parsed);
|
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 it's an object, parse each property
|
||||||
if (isObject(obj)) {
|
if (isObject(obj)) {
|
||||||
return Object.keys(obj as Record<string, unknown>).reduce(
|
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
|
* Parse a file object into a more typescript friendly object
|
||||||
*/
|
*/
|
||||||
export function parseFile(file: unknown): unknown {
|
export function parseFile(file: unknown): ParsedFile {
|
||||||
return parseObject(cleanObject(file));
|
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 { ɵloadPlugin } from './lib/load-plugin';
|
||||||
import { setFileState, setPageState, setSelection } from './lib/api';
|
import { setFileState, setPageState, setSelection } from './lib/api';
|
||||||
import { parseFile } from './lib/utils';
|
|
||||||
|
|
||||||
repairIntrinsics({
|
repairIntrinsics({
|
||||||
evalTaming: 'unsafeEval',
|
evalTaming: 'unsafeEval',
|
||||||
|
@ -28,8 +27,6 @@ export function initialize(api: any) {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
api.addListener('plugin-file', 'file', (file: any) => {
|
api.addListener('plugin-file', 'file', (file: any) => {
|
||||||
// console.log('File Changed (parsed):', parseFile(file));
|
|
||||||
|
|
||||||
console.log('File Changed:', file);
|
console.log('File Changed:', file);
|
||||||
|
|
||||||
setFileState(file);
|
setFileState(file);
|
||||||
|
|
5
nx.json
5
nx.json
|
@ -16,6 +16,11 @@
|
||||||
"cache": true,
|
"cache": true,
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"inputs": ["default", "^default"]
|
"inputs": ["default", "^default"]
|
||||||
|
},
|
||||||
|
"@nx/js:tsc": {
|
||||||
|
"cache": true,
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"inputs": ["default", "^default"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
"skipDefaultLibCheck": true,
|
"skipDefaultLibCheck": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"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-runtime": ["libs/plugins-runtime/src/index.ts"],
|
||||||
"plugins-styles/*": ["libs/plugins-styles/src/*"]
|
"plugins-styles/*": ["libs/plugins-styles/src/*"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue