@greensight/gts 1.0.0-alpha.4 → 1.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @greensight/gts
1
+ # Greensight Token System
2
2
 
3
3
  Generate design tokens from Figma.
4
4
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/classes/Config/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,qBAAa,MAAM;IACf,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAmB;WAE5C,MAAM;IAQN,IAAI,IAAI,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;CAUvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/classes/Config/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,qBAAa,MAAM;IACf,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAmB;WAE5C,MAAM;IAQN,IAAI,IAAI,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;CAYvD"}
package/index.cjs ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";var U=Object.defineProperty;var z=(o,e,r)=>e in o?U(o,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):o[e]=r;var b=(o,e,r)=>z(o,typeof e!="symbol"?e+"":e,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const H=require("path"),K=require("ts-import"),k=require("node:fs"),p=require("node:fs/promises"),T=require("node:path"),u=class u{static resolveReadPath(e){if(!e||!e.trim())throw new Error("File path must be a non-empty string");return T.resolve(u.baseDir,e)}static resolveWritePath(e,r){const t=T.resolve(u.baseDir,r??"");return{targetDir:t,targetPath:T.resolve(t,e)}}static handleReadError(e,r){throw e.code==="ENOENT"?new Error(`File not found: ${r}`):new Error(`Failed to read file "${r}": ${e.message??String(e)}`)}static async read(e,r="utf8"){const t=u.resolveReadPath(e);try{return await p.readFile(t,{encoding:r})}catch(s){u.handleReadError(s,t)}}static async readBuffer(e){const r=u.resolveReadPath(e);try{return await p.readFile(r)}catch(t){u.handleReadError(t,r)}}static async readJson(e){const r=u.resolveReadPath(e);try{const t=await p.readFile(r,{encoding:"utf8"});try{return JSON.parse(t)}catch(s){throw new Error(`Failed to parse JSON from "${r}": ${s.message}`)}}catch(t){u.handleReadError(t,r)}}static async write(e,r="",t={}){const{directory:s,overwrite:n=!0}=t,{targetDir:a,targetPath:l}=u.resolveWritePath(e,s);if(!n&&k.existsSync(l))throw new Error(`File ${l} already exists`);return await p.mkdir(a,{recursive:!0}),await p.writeFile(l,r,{encoding:"utf8"}),l}static async writeWithExtension(e,r,t="",s){const n=r.startsWith(".")?r:`.${r}`,a=`${e}${n}`;return u.write(a,t,s)}static exists(e){const r=u.resolveReadPath(e);return k.existsSync(r)}static async delete(e,r){const{targetPath:t}=u.resolveWritePath(e,r);k.existsSync(t)&&await p.rm(t,{force:!0})}};b(u,"baseDir",process.cwd());let h=u;const v=class v{static async create(){if(h.exists(v.configFileName))throw new Error("The file already exists");await h.write(v.configFileName,"",{overwrite:!1})}async load(){try{const e=await K.tsImport.compile(`${H.resolve(process.cwd(),v.configFileName)}`);if(!e)throw new Error;return e.default}catch(e){console.error("Cannot find module gts.config.ts",e)}}};b(v,"configFileName","gts.config.ts");let x=v;const Q=o=>{const e=new URLSearchParams;return Object.keys(o).forEach(r=>{Array.isArray(o[r])?o[r].forEach(t=>e.append(`${r}[]`,t)):e.append(r,o[r])}),e},Z=(o,e=50)=>{const r=[];for(let t=0;t<o.length;t+=e)r.push(o.slice(t,t+e));return r};class R{constructor(e,r){b(this,"figmaToken");b(this,"fileId");b(this,"onTimeMeasureHandler");this.figmaToken=e,this.fileId=r}setOnTimeMeasureHandler(e){this.onTimeMeasureHandler=e}static async returnJSON(e){const r=await e.json();if(!e.ok){let t="Request failed";throw new Error(t)}return r}async performControlledRequest(e,{params:r={},timeout:t=3e4,abortController:s=new AbortController}={}){var g;const n=Object.entries(r).reduce((c,[y,w])=>typeof w<"u"?{...c,[y]:w}:c,{}),a=`https://api.figma.com/v1${e}${n&&Object.keys(n).length?`?${Q(n)}`:""}`;console.log("endpoinWithParams=",a);const l=setTimeout(()=>s.abort(),t),i={"Content-Type":"application/json",...this.figmaToken&&{"X-Figma-Token":this.figmaToken}},d={method:"GET",headers:i,signal:s.signal},m=performance.now(),f=await fetch(`${a}`,d);clearTimeout(l);const $=performance.now()-m;return(g=this.onTimeMeasureHandler)==null||g.call(this,a,i,$),f}async request(e,r){var s;const t=await this.performControlledRequest(e,{...r});return(s=t.headers.get("content-type"))!=null&&s.includes("application/json")?R.returnJSON(t):t}async getComponents(){return this.request(`/files/${this.fileId}/components`)}async getStyles(){return this.request(`/files/${this.fileId}/styles`)}async getNodes(e){const r=Z(e).map(n=>this.request(`/files/${this.fileId}/nodes`,{params:{ids:n.join(",")}})),t=await Promise.all(r);return{...t[0],nodes:t.reduce((n,a)=>({...n,...a.nodes}),{})}}}const _=async()=>{const e=await new x().load();if(!e)throw new Error("Заполнить ошибку через нейронку");const{figmaToken:r,fileId:t,modules:s}=e,n=new R(r,t);await Promise.all(s.map(a=>a.executor({figmaApiClient:n})))},ee=async()=>{await x.create(),console.log("\x1B[32m%s\x1B[0m","✔️ Configuration file created gts.config.ts")},re=({r:o,g:e,b:r})=>{const t=s=>`0${s.toString(16)}`.slice(-2);return`#${t(o)}${t(e)}${t(r)}`},N=({opacity:o,r:e,g:r,b:t})=>{const s=[e,r,t].map(n=>Math.round(n*255));return o<1?`rgba(${s[0]}, ${s[1]}, ${s[2]}, ${o})`:re({r:s[0],g:s[1],b:s[2]})},V=(o,e=0)=>{const r=Math.atan2(o[1].y-o[0].y,o[1].x-o[0].x);return Math.round(r*180/Math.PI)+e},C=o=>o.reduce((e,r)=>{const t=Number((r.position*100).toFixed(1));return[...e,`${N({opacity:r.color.a,r:r.color.r,g:r.color.g,b:r.color.b})}${t>0&&t<100?` ${t}%`:""}`]},[]).join(", "),W=o=>{const e=o[0].x,r=o[0].y,t=(e*100).toFixed(2),s=(r*100).toFixed(2);return{centerX:t,centerY:s}},oe=o=>{const{gradientHandlePositions:e,gradientStops:r}=o,t=V(e,90),s=C(r);return`linear-gradient(${t}deg, ${s})`},te=o=>{const e=o[0].x,r=o[0].y,t=o[1].x,s=o[1].y,n=o[2].x,a=o[2].y,l=(Math.sqrt((n-e)**2+(a-r)**2)*100).toFixed(2),i=(Math.sqrt((t-e)**2+(s-r)**2)*100).toFixed(2);return{radiusX:l,radiusY:i}},se=o=>{const{gradientHandlePositions:e,gradientStops:r}=o,{centerX:t,centerY:s}=W(e),{radiusX:n,radiusY:a}=te(e),l=C(r);return`radial-gradient(${n}% ${a}% at ${t}% ${s}%, ${l})`},ne=o=>{const{gradientHandlePositions:e,gradientStops:r}=o,t=V(e,30),{centerX:s,centerY:n}=W(e),a=C(r);return`conic-gradient(from ${t}deg at ${s}% ${n}%, ${a})`},ae=o=>{const e=o.type;return e==="SOLID"?N({opacity:o.color.a,r:o.color.r,g:o.color.g,b:o.color.b}):e==="GRADIENT_LINEAR"?oe(o):e==="GRADIENT_RADIAL"?se(o):e==="GRADIENT_ANGULAR"?ne(o):""},J=(o,e)=>{if(!e.length)return"";const r=e.map(t=>` ${t}`).join(`
2
+ `);return`${o} {
3
+ ${r}
4
+ }`},ce=o=>`.${o.replace(/\s+/g,"-").toLowerCase()}`,le=o=>o.replaceAll(/ /g,"").split("/").at(-1),ie=o=>`--${o}`,de=o=>o.reduce((e,r)=>{const t=ie(r.name);return typeof r.value=="object"?Object.entries(r.value).forEach(([s,n])=>{e[s]||(e[s]=[]),e[s].push(`${t}: ${n};`)}):e.root.push(`${t}: ${r.value};`),e},{root:[]}),ue=o=>{const e=J(":root",o.root),r=Object.entries(o).reduce((t,[s,n])=>{if(s==="root"||!n.length)return t;const a=J(ce(s),n);return a&&t.push(a),t},[]).join(`
5
+
6
+ `);return[e,r].filter(Boolean).join(`
7
+
8
+ `)},ge=o=>{const e=o.reduce((r,t)=>({...r,[t.name]:t.value}),{});return JSON.stringify(e)},fe=async(o,e,r,t,s,n)=>{await Promise.all([h.delete(s,r),h.delete(n,t)]);const a=h.write(s,o,{directory:r}),l=h.write(n,e,{directory:t});await Promise.all([a,l])},B=async({colorTokens:o,jsonDir:e,stylesDir:r,jsonFileName:t,cssFileName:s})=>{const n=de(o),a=ue(n),l=ge(o);await fe(l,a,e,r,t,s)},he=({input:o,output:{jsonDir:e,stylesDir:r,jsonFileName:t="colors.json",cssFileName:s="colors.css"}})=>({name:"styles/colors",executor:async({figmaApiClient:n})=>{try{console.log("[styles/colors] Fetching styles from Figma...");const l=(await n.getStyles()).meta.styles,i=(o==null?void 0:o.variablePaths)||[],d=l.filter(c=>c.style_type==="FILL");if(console.log(`[styles/colors] Found ${d.length} color styles`),d.length===0){console.warn("[styles/colors] No color styles found in Figma file");return}console.log(`[styles/colors] Fetching ${d.length} color nodes from Figma...`);const m=await n.getNodes(d.map(c=>c.node_id)),f=Object.entries(m.nodes);let $=[];if(i.length>1){console.log(`[styles/colors] Reading ${i.length} variable files...`);try{$=await Promise.all(i.map(async c=>{try{return await h.readJson(c)}catch(y){throw console.error(`[styles/colors] Failed to read variable file: ${c}`,y),y}}))}catch(c){throw console.error("[styles/colors] Error reading variable files:",c),new Error(`Failed to read variable files: ${c.message}`)}}console.log(`[styles/colors] Processing ${f.length} color nodes...`);const g=f.reduce((c,[,y])=>{var w,j,O;try{const{document:E}=y,P=le(E.name);if(E.type!=="RECTANGLE")return c;const F=(w=E.fills)==null?void 0:w[0];if(!F)return c;if(F.type==="SOLID"&&$.length>1){const M=(O=(j=F.boundVariables)==null?void 0:j.color)==null?void 0:O.id;if(M){const A=$.reduce((q,G)=>{var L;const D=(L=Object.entries(G).find(([,S])=>S.$extensions["com.figma.variableId"]===M))==null?void 0:L[1];if(D){const{components:S,alpha:X}=D.$value,Y=N({opacity:X,r:S[0],g:S[1],b:S[2]});return{...q,[G.$extensions["com.figma.modeName"]]:Y}}return q},{});if(Object.keys(A).length>1)return[...c,{name:P,value:A}]}}const I=ae(F);return I?[...c,{name:P,value:I}]:c}catch(E){return console.warn("[styles/colors] Error processing color node:",E),c}},[]);if(console.log(`[styles/colors] Generated ${g.length} color tokens`),g.length===0){console.warn("[styles/colors] No color tokens generated. Check your Figma styles configuration.");return}console.log(`[styles/colors] Writing files to ${e} and ${r}...`),await B({colorTokens:g,jsonDir:e,stylesDir:r,jsonFileName:t,cssFileName:s}),console.log("[styles/colors] ✅ Successfully generated color files")}catch(a){const l=a instanceof Error?a.message:String(a);throw console.error("[styles/colors] ❌ Failed to generate colors from styles:",l),a instanceof Error&&a.stack&&console.error("[styles/colors] Stack trace:",a.stack),a}}}),me=({input:{variablePaths:o},output:{jsonDir:e,stylesDir:r,jsonFileName:t="colors.json",cssFileName:s="colors.css"}})=>({name:"variables/colors",executor:async()=>{try{if(!o.length)throw new Error("At least one variable file path is required");console.log(`[variables/colors] Reading ${o.length} variable files...`);const n=await Promise.all(o.map(async i=>{try{return console.log(`[variables/colors] Reading file: ${i}`),await h.readJson(i)}catch(d){throw console.error(`[variables/colors] Failed to read variable file: ${i}`,d),new Error(`Failed to read variable file "${i}": ${d.message}`)}}));console.log(`[variables/colors] Processing ${n.length} variable files...`);const a=new Map;n.forEach((i,d)=>{try{if(!i.$extensions||!i.$extensions["com.figma.modeName"]){console.warn(`[variables/colors] File ${o[d]} is missing modeName in extensions`);return}const m=i.$extensions["com.figma.modeName"];Object.entries(i).forEach(([f,$])=>{if(f!=="$extensions")try{const g=$;if(!g||g.$type!=="color")return;if(!g.$value||!g.$value.components){console.warn(`[variables/colors] Variable "${f}" in mode "${m}" has invalid structure`);return}const{components:c,alpha:y}=g.$value,w=N({opacity:y??1,r:c[0],g:c[1],b:c[2]});a.has(f)||a.set(f,{}),a.get(f)[m]=w}catch(g){console.warn(`[variables/colors] Error processing variable "${f}" in mode "${m}":`,g)}})}catch(m){console.error(`[variables/colors] Error processing file ${o[d]}:`,m)}});const l=Array.from(a.entries()).map(([i,d])=>({name:i,value:Object.keys(d).length>1?d:Object.values(d)[0]}));if(console.log(`[variables/colors] Generated ${l.length} color tokens`),l.length===0){console.warn("[variables/colors] No color tokens generated. Check your variable files structure.");return}console.log(`[variables/colors] Writing files to ${e} and ${r}...`),await B({colorTokens:l,jsonDir:e,stylesDir:r,jsonFileName:t,cssFileName:s}),console.log("[variables/colors] ✅ Successfully generated color files")}catch(n){const a=n instanceof Error?n.message:String(n);throw console.error("[variables/colors] ❌ Failed to generate colors from variables:",a),n instanceof Error&&n.stack&&console.error("[variables/colors] Stack trace:",n.stack),n}}});exports.colorsFromStyles=he;exports.colorsFromVariables=me;exports.generate=_;exports.init=ee;
package/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { generate } from './commands/generate';
2
2
  export { init } from './commands/init';
3
+ export type { IGtsConfig } from './classes/Config';
4
+ export * from './modules/colors';
3
5
  //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,cAAc,kBAAkB,CAAC"}
package/index.mjs ADDED
@@ -0,0 +1,457 @@
1
+ var U = Object.defineProperty;
2
+ var z = (o, e, r) => e in o ? U(o, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : o[e] = r;
3
+ var p = (o, e, r) => z(o, typeof e != "symbol" ? e + "" : e, r);
4
+ import H from "path";
5
+ import { tsImport as K } from "ts-import";
6
+ import { existsSync as N } from "node:fs";
7
+ import { readFile as k, mkdir as Q, writeFile as Z, rm as _ } from "node:fs/promises";
8
+ import { resolve as R } from "node:path";
9
+ const d = class d {
10
+ static resolveReadPath(e) {
11
+ if (!e || !e.trim())
12
+ throw new Error("File path must be a non-empty string");
13
+ return R(d.baseDir, e);
14
+ }
15
+ static resolveWritePath(e, r) {
16
+ const t = R(d.baseDir, r ?? "");
17
+ return {
18
+ targetDir: t,
19
+ targetPath: R(t, e)
20
+ };
21
+ }
22
+ static handleReadError(e, r) {
23
+ throw e.code === "ENOENT" ? new Error(`File not found: ${r}`) : new Error(
24
+ `Failed to read file "${r}": ${e.message ?? String(e)}`
25
+ );
26
+ }
27
+ static async read(e, r = "utf8") {
28
+ const t = d.resolveReadPath(e);
29
+ try {
30
+ return await k(t, { encoding: r });
31
+ } catch (s) {
32
+ d.handleReadError(s, t);
33
+ }
34
+ }
35
+ static async readBuffer(e) {
36
+ const r = d.resolveReadPath(e);
37
+ try {
38
+ return await k(r);
39
+ } catch (t) {
40
+ d.handleReadError(t, r);
41
+ }
42
+ }
43
+ static async readJson(e) {
44
+ const r = d.resolveReadPath(e);
45
+ try {
46
+ const t = await k(r, { encoding: "utf8" });
47
+ try {
48
+ return JSON.parse(t);
49
+ } catch (s) {
50
+ throw new Error(`Failed to parse JSON from "${r}": ${s.message}`);
51
+ }
52
+ } catch (t) {
53
+ d.handleReadError(t, r);
54
+ }
55
+ }
56
+ static async write(e, r = "", t = {}) {
57
+ const { directory: s, overwrite: n = !0 } = t, { targetDir: a, targetPath: l } = d.resolveWritePath(e, s);
58
+ if (!n && N(l))
59
+ throw new Error(`File ${l} already exists`);
60
+ return await Q(a, { recursive: !0 }), await Z(l, r, { encoding: "utf8" }), l;
61
+ }
62
+ static async writeWithExtension(e, r, t = "", s) {
63
+ const n = r.startsWith(".") ? r : `.${r}`, a = `${e}${n}`;
64
+ return d.write(a, t, s);
65
+ }
66
+ static exists(e) {
67
+ const r = d.resolveReadPath(e);
68
+ return N(r);
69
+ }
70
+ static async delete(e, r) {
71
+ const { targetPath: t } = d.resolveWritePath(e, r);
72
+ N(t) && await _(t, { force: !0 });
73
+ }
74
+ };
75
+ p(d, "baseDir", process.cwd());
76
+ let m = d;
77
+ const b = class b {
78
+ static async create() {
79
+ if (m.exists(b.configFileName))
80
+ throw new Error("The file already exists");
81
+ await m.write(b.configFileName, "", { overwrite: !1 });
82
+ }
83
+ async load() {
84
+ try {
85
+ const e = await K.compile(
86
+ `${H.resolve(process.cwd(), b.configFileName)}`
87
+ );
88
+ if (!e) throw new Error();
89
+ return e.default;
90
+ } catch (e) {
91
+ console.error("Cannot find module gts.config.ts", e);
92
+ }
93
+ }
94
+ };
95
+ p(b, "configFileName", "gts.config.ts");
96
+ let F = b;
97
+ const ee = (o) => {
98
+ const e = new URLSearchParams();
99
+ return Object.keys(o).forEach((r) => {
100
+ Array.isArray(o[r]) ? o[r].forEach((t) => e.append(`${r}[]`, t)) : e.append(r, o[r]);
101
+ }), e;
102
+ }, re = (o, e = 50) => {
103
+ const r = [];
104
+ for (let t = 0; t < o.length; t += e)
105
+ r.push(o.slice(t, t + e));
106
+ return r;
107
+ };
108
+ class T {
109
+ constructor(e, r) {
110
+ p(this, "figmaToken");
111
+ p(this, "fileId");
112
+ p(this, "onTimeMeasureHandler");
113
+ this.figmaToken = e, this.fileId = r;
114
+ }
115
+ setOnTimeMeasureHandler(e) {
116
+ this.onTimeMeasureHandler = e;
117
+ }
118
+ static async returnJSON(e) {
119
+ const r = await e.json();
120
+ if (!e.ok) {
121
+ let t = "Request failed";
122
+ throw new Error(t);
123
+ }
124
+ return r;
125
+ }
126
+ async performControlledRequest(e, { params: r = {}, timeout: t = 3e4, abortController: s = new AbortController() } = {}) {
127
+ var u;
128
+ const n = Object.entries(r).reduce((c, [y, w]) => typeof w < "u" ? { ...c, [y]: w } : c, {}), a = `https://api.figma.com/v1${e}${n && Object.keys(n).length ? `?${ee(n)}` : ""}`;
129
+ console.log("endpoinWithParams=", a);
130
+ const l = setTimeout(() => s.abort(), t), i = {
131
+ "Content-Type": "application/json",
132
+ ...this.figmaToken && { "X-Figma-Token": this.figmaToken }
133
+ }, g = {
134
+ method: "GET",
135
+ headers: i,
136
+ signal: s.signal
137
+ }, h = performance.now(), f = await fetch(`${a}`, g);
138
+ clearTimeout(l);
139
+ const $ = performance.now() - h;
140
+ return (u = this.onTimeMeasureHandler) == null || u.call(this, a, i, $), f;
141
+ }
142
+ async request(e, r) {
143
+ var s;
144
+ const t = await this.performControlledRequest(e, {
145
+ ...r
146
+ });
147
+ return (s = t.headers.get("content-type")) != null && s.includes("application/json") ? T.returnJSON(t) : t;
148
+ }
149
+ async getComponents() {
150
+ return this.request(`/files/${this.fileId}/components`);
151
+ }
152
+ async getStyles() {
153
+ return this.request(`/files/${this.fileId}/styles`);
154
+ }
155
+ async getNodes(e) {
156
+ const r = re(e).map(
157
+ (n) => this.request(`/files/${this.fileId}/nodes`, { params: { ids: n.join(",") } })
158
+ ), t = await Promise.all(r);
159
+ return {
160
+ ...t[0],
161
+ nodes: t.reduce((n, a) => ({ ...n, ...a.nodes }), {})
162
+ };
163
+ }
164
+ }
165
+ const ve = async () => {
166
+ const e = await new F().load();
167
+ if (!e)
168
+ throw new Error("Заполнить ошибку через нейронку");
169
+ const { figmaToken: r, fileId: t, modules: s } = e, n = new T(r, t);
170
+ await Promise.all(
171
+ // [
172
+ // colorsFromStyles({
173
+ // input: { variablePaths: ['./dark.tokens.json', './light.tokens.json'] },
174
+ // output: { jsonDir: './fromStyles', stylesDir: './fromStyles' },
175
+ // }),
176
+ // colorsFromVariables({
177
+ // input: { variablePaths: ['./dark.tokens.json', './light.tokens.json'] },
178
+ // output: { jsonDir: './fromVariables', stylesDir: './fromVariables' },
179
+ // }),
180
+ // ]
181
+ s.map((a) => a.executor({ figmaApiClient: n }))
182
+ );
183
+ }, Ee = async () => {
184
+ await F.create(), console.log("\x1B[32m%s\x1B[0m", "✔️ Configuration file created gts.config.ts");
185
+ }, oe = ({ r: o, g: e, b: r }) => {
186
+ const t = (s) => `0${s.toString(16)}`.slice(-2);
187
+ return `#${t(o)}${t(e)}${t(r)}`;
188
+ }, S = ({ opacity: o, r: e, g: r, b: t }) => {
189
+ const s = [e, r, t].map((n) => Math.round(n * 255));
190
+ return o < 1 ? `rgba(${s[0]}, ${s[1]}, ${s[2]}, ${o})` : oe({ r: s[0], g: s[1], b: s[2] });
191
+ }, W = (o, e = 0) => {
192
+ const r = Math.atan2(
193
+ o[1].y - o[0].y,
194
+ o[1].x - o[0].x
195
+ );
196
+ return Math.round(r * 180 / Math.PI) + e;
197
+ }, C = (o) => o.reduce((e, r) => {
198
+ const t = Number((r.position * 100).toFixed(1));
199
+ return [
200
+ ...e,
201
+ `${S({
202
+ opacity: r.color.a,
203
+ r: r.color.r,
204
+ g: r.color.g,
205
+ b: r.color.b
206
+ })}${t > 0 && t < 100 ? ` ${t}%` : ""}`
207
+ ];
208
+ }, []).join(", "), V = (o) => {
209
+ const e = o[0].x, r = o[0].y, t = (e * 100).toFixed(2), s = (r * 100).toFixed(2);
210
+ return { centerX: t, centerY: s };
211
+ }, te = (o) => {
212
+ const { gradientHandlePositions: e, gradientStops: r } = o, t = W(e, 90), s = C(r);
213
+ return `linear-gradient(${t}deg, ${s})`;
214
+ }, se = (o) => {
215
+ const e = o[0].x, r = o[0].y, t = o[1].x, s = o[1].y, n = o[2].x, a = o[2].y, l = (Math.sqrt((n - e) ** 2 + (a - r) ** 2) * 100).toFixed(2), i = (Math.sqrt((t - e) ** 2 + (s - r) ** 2) * 100).toFixed(2);
216
+ return { radiusX: l, radiusY: i };
217
+ }, ne = (o) => {
218
+ const { gradientHandlePositions: e, gradientStops: r } = o, { centerX: t, centerY: s } = V(e), { radiusX: n, radiusY: a } = se(e), l = C(r);
219
+ return `radial-gradient(${n}% ${a}% at ${t}% ${s}%, ${l})`;
220
+ }, ae = (o) => {
221
+ const { gradientHandlePositions: e, gradientStops: r } = o, t = W(e, 30), { centerX: s, centerY: n } = V(e), a = C(r);
222
+ return `conic-gradient(from ${t}deg at ${s}% ${n}%, ${a})`;
223
+ }, ce = (o) => {
224
+ const e = o.type;
225
+ return e === "SOLID" ? S({
226
+ opacity: o.color.a,
227
+ r: o.color.r,
228
+ g: o.color.g,
229
+ b: o.color.b
230
+ }) : e === "GRADIENT_LINEAR" ? te(o) : e === "GRADIENT_RADIAL" ? ne(o) : e === "GRADIENT_ANGULAR" ? ae(o) : "";
231
+ }, J = (o, e) => {
232
+ if (!e.length) return "";
233
+ const r = e.map((t) => ` ${t}`).join(`
234
+ `);
235
+ return `${o} {
236
+ ${r}
237
+ }`;
238
+ }, le = (o) => `.${o.replace(/\s+/g, "-").toLowerCase()}`, ie = (o) => o.replaceAll(/ /g, "").split("/").at(-1), ge = (o) => `--${o}`, de = (o) => o.reduce(
239
+ (e, r) => {
240
+ const t = ge(r.name);
241
+ return typeof r.value == "object" ? Object.entries(r.value).forEach(([s, n]) => {
242
+ e[s] || (e[s] = []), e[s].push(`${t}: ${n};`);
243
+ }) : e.root.push(`${t}: ${r.value};`), e;
244
+ },
245
+ { root: [] }
246
+ ), ue = (o) => {
247
+ const e = J(":root", o.root), r = Object.entries(o).reduce((t, [s, n]) => {
248
+ if (s === "root" || !n.length) return t;
249
+ const a = J(le(s), n);
250
+ return a && t.push(a), t;
251
+ }, []).join(`
252
+
253
+ `);
254
+ return [e, r].filter(Boolean).join(`
255
+
256
+ `);
257
+ }, fe = (o) => {
258
+ const e = o.reduce((r, t) => ({ ...r, [t.name]: t.value }), {});
259
+ return JSON.stringify(e);
260
+ }, me = async (o, e, r, t, s, n) => {
261
+ await Promise.all([m.delete(s, r), m.delete(n, t)]);
262
+ const a = m.write(s, o, { directory: r }), l = m.write(n, e, { directory: t });
263
+ await Promise.all([a, l]);
264
+ }, B = async ({
265
+ colorTokens: o,
266
+ jsonDir: e,
267
+ stylesDir: r,
268
+ jsonFileName: t,
269
+ cssFileName: s
270
+ }) => {
271
+ const n = de(o), a = ue(n), l = fe(o);
272
+ await me(l, a, e, r, t, s);
273
+ }, xe = ({
274
+ input: o,
275
+ output: { jsonDir: e, stylesDir: r, jsonFileName: t = "colors.json", cssFileName: s = "colors.css" }
276
+ }) => ({
277
+ name: "styles/colors",
278
+ executor: async ({ figmaApiClient: n }) => {
279
+ try {
280
+ console.log("[styles/colors] Fetching styles from Figma...");
281
+ const l = (await n.getStyles()).meta.styles, i = (o == null ? void 0 : o.variablePaths) || [], g = l.filter((c) => c.style_type === "FILL");
282
+ if (console.log(`[styles/colors] Found ${g.length} color styles`), g.length === 0) {
283
+ console.warn("[styles/colors] No color styles found in Figma file");
284
+ return;
285
+ }
286
+ console.log(`[styles/colors] Fetching ${g.length} color nodes from Figma...`);
287
+ const h = await n.getNodes(g.map((c) => c.node_id)), f = Object.entries(h.nodes);
288
+ let $ = [];
289
+ if (i.length > 1) {
290
+ console.log(`[styles/colors] Reading ${i.length} variable files...`);
291
+ try {
292
+ $ = await Promise.all(
293
+ i.map(async (c) => {
294
+ try {
295
+ return await m.readJson(c);
296
+ } catch (y) {
297
+ throw console.error(`[styles/colors] Failed to read variable file: ${c}`, y), y;
298
+ }
299
+ })
300
+ );
301
+ } catch (c) {
302
+ throw console.error("[styles/colors] Error reading variable files:", c), new Error(`Failed to read variable files: ${c.message}`);
303
+ }
304
+ }
305
+ console.log(`[styles/colors] Processing ${f.length} color nodes...`);
306
+ const u = f.reduce((c, [, y]) => {
307
+ var w, j, O;
308
+ try {
309
+ const { document: v } = y, P = ie(v.name);
310
+ if (v.type !== "RECTANGLE") return c;
311
+ const x = (w = v.fills) == null ? void 0 : w[0];
312
+ if (!x) return c;
313
+ if (x.type === "SOLID" && $.length > 1) {
314
+ const M = (O = (j = x.boundVariables) == null ? void 0 : j.color) == null ? void 0 : O.id;
315
+ if (M) {
316
+ const A = $.reduce((G, D) => {
317
+ var q;
318
+ const L = (q = Object.entries(D).find(
319
+ ([, E]) => E.$extensions["com.figma.variableId"] === M
320
+ )) == null ? void 0 : q[1];
321
+ if (L) {
322
+ const { components: E, alpha: X } = L.$value, Y = S({
323
+ opacity: X,
324
+ r: E[0],
325
+ g: E[1],
326
+ b: E[2]
327
+ });
328
+ return { ...G, [D.$extensions["com.figma.modeName"]]: Y };
329
+ }
330
+ return G;
331
+ }, {});
332
+ if (Object.keys(A).length > 1)
333
+ return [
334
+ ...c,
335
+ {
336
+ name: P,
337
+ value: A
338
+ }
339
+ ];
340
+ }
341
+ }
342
+ const I = ce(x);
343
+ return I ? [
344
+ ...c,
345
+ {
346
+ name: P,
347
+ value: I
348
+ }
349
+ ] : c;
350
+ } catch (v) {
351
+ return console.warn("[styles/colors] Error processing color node:", v), c;
352
+ }
353
+ }, []);
354
+ if (console.log(`[styles/colors] Generated ${u.length} color tokens`), u.length === 0) {
355
+ console.warn("[styles/colors] No color tokens generated. Check your Figma styles configuration.");
356
+ return;
357
+ }
358
+ console.log(`[styles/colors] Writing files to ${e} and ${r}...`), await B({
359
+ colorTokens: u,
360
+ jsonDir: e,
361
+ stylesDir: r,
362
+ jsonFileName: t,
363
+ cssFileName: s
364
+ }), console.log("[styles/colors] ✅ Successfully generated color files");
365
+ } catch (a) {
366
+ const l = a instanceof Error ? a.message : String(a);
367
+ throw console.error("[styles/colors] ❌ Failed to generate colors from styles:", l), a instanceof Error && a.stack && console.error("[styles/colors] Stack trace:", a.stack), a;
368
+ }
369
+ }
370
+ }), Fe = ({
371
+ input: { variablePaths: o },
372
+ output: { jsonDir: e, stylesDir: r, jsonFileName: t = "colors.json", cssFileName: s = "colors.css" }
373
+ }) => ({
374
+ name: "variables/colors",
375
+ executor: async () => {
376
+ try {
377
+ if (!o.length)
378
+ throw new Error("At least one variable file path is required");
379
+ console.log(`[variables/colors] Reading ${o.length} variable files...`);
380
+ const n = await Promise.all(
381
+ o.map(async (i) => {
382
+ try {
383
+ return console.log(`[variables/colors] Reading file: ${i}`), await m.readJson(i);
384
+ } catch (g) {
385
+ throw console.error(`[variables/colors] Failed to read variable file: ${i}`, g), new Error(`Failed to read variable file "${i}": ${g.message}`);
386
+ }
387
+ })
388
+ );
389
+ console.log(`[variables/colors] Processing ${n.length} variable files...`);
390
+ const a = /* @__PURE__ */ new Map();
391
+ n.forEach((i, g) => {
392
+ try {
393
+ if (!i.$extensions || !i.$extensions["com.figma.modeName"]) {
394
+ console.warn(
395
+ `[variables/colors] File ${o[g]} is missing modeName in extensions`
396
+ );
397
+ return;
398
+ }
399
+ const h = i.$extensions["com.figma.modeName"];
400
+ Object.entries(i).forEach(([f, $]) => {
401
+ if (f !== "$extensions")
402
+ try {
403
+ const u = $;
404
+ if (!u || u.$type !== "color") return;
405
+ if (!u.$value || !u.$value.components) {
406
+ console.warn(
407
+ `[variables/colors] Variable "${f}" in mode "${h}" has invalid structure`
408
+ );
409
+ return;
410
+ }
411
+ const { components: c, alpha: y } = u.$value, w = S({
412
+ opacity: y ?? 1,
413
+ r: c[0],
414
+ g: c[1],
415
+ b: c[2]
416
+ });
417
+ a.has(f) || a.set(f, {}), a.get(f)[h] = w;
418
+ } catch (u) {
419
+ console.warn(
420
+ `[variables/colors] Error processing variable "${f}" in mode "${h}":`,
421
+ u
422
+ );
423
+ }
424
+ });
425
+ } catch (h) {
426
+ console.error(`[variables/colors] Error processing file ${o[g]}:`, h);
427
+ }
428
+ });
429
+ const l = Array.from(a.entries()).map(([i, g]) => ({
430
+ name: i,
431
+ value: Object.keys(g).length > 1 ? g : Object.values(g)[0]
432
+ }));
433
+ if (console.log(`[variables/colors] Generated ${l.length} color tokens`), l.length === 0) {
434
+ console.warn(
435
+ "[variables/colors] No color tokens generated. Check your variable files structure."
436
+ );
437
+ return;
438
+ }
439
+ console.log(`[variables/colors] Writing files to ${e} and ${r}...`), await B({
440
+ colorTokens: l,
441
+ jsonDir: e,
442
+ stylesDir: r,
443
+ jsonFileName: t,
444
+ cssFileName: s
445
+ }), console.log("[variables/colors] ✅ Successfully generated color files");
446
+ } catch (n) {
447
+ const a = n instanceof Error ? n.message : String(n);
448
+ throw console.error("[variables/colors] ❌ Failed to generate colors from variables:", a), n instanceof Error && n.stack && console.error("[variables/colors] Stack trace:", n.stack), n;
449
+ }
450
+ }
451
+ });
452
+ export {
453
+ xe as colorsFromStyles,
454
+ Fe as colorsFromVariables,
455
+ ve as generate,
456
+ Ee as init
457
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greensight/gts",
3
- "version": "1.0.0-alpha.4",
3
+ "version": "1.0.0-alpha.6",
4
4
  "description": "Generate design tokens from Figma",
5
5
  "keywords": [
6
6
  "figma",
@@ -12,7 +12,6 @@
12
12
  "typescript"
13
13
  ],
14
14
  "private": false,
15
- "type": "module",
16
15
  "engines": {
17
16
  "node": ">=16.0.0"
18
17
  },
@@ -28,8 +27,15 @@
28
27
  "ts-import": "2.0.40"
29
28
  },
30
29
  "types": "./index.d.ts",
31
- "module": "./index.js",
32
- "main": "./index.js",
30
+ "main": "./index.cjs",
31
+ "module": "./index.mjs",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./index.d.ts",
35
+ "import": "./index.mjs",
36
+ "require": "./index.cjs"
37
+ }
38
+ },
33
39
  "bin": {
34
40
  "gts-init": "./bin/init.js",
35
41
  "gts-generate": "./bin/generate.js"
package/index.js DELETED
@@ -1,186 +0,0 @@
1
- var k = Object.defineProperty;
2
- var x = (n, e, t) => e in n ? k(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
3
- var l = (n, e, t) => x(n, typeof e != "symbol" ? e + "" : e, t);
4
- import { tsImport as F } from "ts-import";
5
- import { existsSync as h } from "node:fs";
6
- import { readFile as u, mkdir as O, writeFile as b, rm as j } from "node:fs/promises";
7
- import { resolve as w } from "node:path";
8
- const a = class a {
9
- static resolveReadPath(e) {
10
- if (!e || !e.trim())
11
- throw new Error("File path must be a non-empty string");
12
- return w(a.baseDir, e);
13
- }
14
- static resolveWritePath(e, t) {
15
- const r = w(a.baseDir, t ?? "");
16
- return {
17
- targetDir: r,
18
- targetPath: w(r, e)
19
- };
20
- }
21
- static handleReadError(e, t) {
22
- throw e.code === "ENOENT" ? new Error(`File not found: ${t}`) : new Error(
23
- `Failed to read file "${t}": ${e.message ?? String(e)}`
24
- );
25
- }
26
- static async read(e, t = "utf8") {
27
- const r = a.resolveReadPath(e);
28
- try {
29
- return await u(r, { encoding: t });
30
- } catch (s) {
31
- a.handleReadError(s, r);
32
- }
33
- }
34
- static async readBuffer(e) {
35
- const t = a.resolveReadPath(e);
36
- try {
37
- return await u(t);
38
- } catch (r) {
39
- a.handleReadError(r, t);
40
- }
41
- }
42
- static async readJson(e) {
43
- const t = a.resolveReadPath(e);
44
- try {
45
- const r = await u(t, { encoding: "utf8" });
46
- try {
47
- return JSON.parse(r);
48
- } catch (s) {
49
- throw new Error(`Failed to parse JSON from "${t}": ${s.message}`);
50
- }
51
- } catch (r) {
52
- a.handleReadError(r, t);
53
- }
54
- }
55
- static async write(e, t = "", r = {}) {
56
- const { directory: s, overwrite: o = !0 } = r, { targetDir: i, targetPath: c } = a.resolveWritePath(e, s);
57
- if (!o && h(c))
58
- throw new Error(`File ${c} already exists`);
59
- return await O(i, { recursive: !0 }), await b(c, t, { encoding: "utf8" }), c;
60
- }
61
- static async writeWithExtension(e, t, r = "", s) {
62
- const o = t.startsWith(".") ? t : `.${t}`, i = `${e}${o}`;
63
- return a.write(i, r, s);
64
- }
65
- static exists(e) {
66
- const t = a.resolveReadPath(e);
67
- return h(t);
68
- }
69
- static async delete(e, t) {
70
- const { targetPath: r } = a.resolveWritePath(e, t);
71
- h(r) && await j(r, { force: !0 });
72
- }
73
- };
74
- l(a, "baseDir", process.cwd());
75
- let f = a;
76
- const d = class d {
77
- static async create() {
78
- if (f.exists(d.configFileName))
79
- throw new Error("The file already exists");
80
- await f.write(d.configFileName, "", { overwrite: !1 });
81
- }
82
- async load() {
83
- try {
84
- const e = await F.compile(d.configFileName);
85
- if (!e) throw new Error();
86
- return e.default;
87
- } catch (e) {
88
- console.error("Cannot find module gts.config.ts", e);
89
- }
90
- }
91
- };
92
- l(d, "configFileName", "gts.config.ts");
93
- let m = d;
94
- const I = (n) => {
95
- const e = new URLSearchParams();
96
- return Object.keys(n).forEach((t) => {
97
- Array.isArray(n[t]) ? n[t].forEach((r) => e.append(`${t}[]`, r)) : e.append(t, n[t]);
98
- }), e;
99
- }, q = (n, e = 50) => {
100
- const t = [];
101
- for (let r = 0; r < n.length; r += e)
102
- t.push(n.slice(r, r + e));
103
- return t;
104
- };
105
- class g {
106
- constructor(e, t) {
107
- l(this, "figmaToken");
108
- l(this, "fileId");
109
- l(this, "onTimeMeasureHandler");
110
- this.figmaToken = e, this.fileId = t;
111
- }
112
- setOnTimeMeasureHandler(e) {
113
- this.onTimeMeasureHandler = e;
114
- }
115
- static async returnJSON(e) {
116
- const t = await e.json();
117
- if (!e.ok) {
118
- let r = "Request failed";
119
- throw new Error(r);
120
- }
121
- return t;
122
- }
123
- async performControlledRequest(e, { params: t = {}, timeout: r = 3e4, abortController: s = new AbortController() } = {}) {
124
- var y;
125
- const o = Object.entries(t).reduce((E, [N, P]) => typeof P < "u" ? { ...E, [N]: P } : E, {}), i = `https://api.figma.com/v1${e}${o && Object.keys(o).length ? `?${I(o)}` : ""}`;
126
- console.log("endpoinWithParams=", i);
127
- const c = setTimeout(() => s.abort(), r), p = {
128
- "Content-Type": "application/json",
129
- ...this.figmaToken && { "X-Figma-Token": this.figmaToken }
130
- }, $ = {
131
- method: "GET",
132
- headers: p,
133
- signal: s.signal
134
- }, T = performance.now(), R = await fetch(`${i}`, $);
135
- clearTimeout(c);
136
- const v = performance.now() - T;
137
- return (y = this.onTimeMeasureHandler) == null || y.call(this, i, p, v), R;
138
- }
139
- async request(e, t) {
140
- var s;
141
- const r = await this.performControlledRequest(e, {
142
- ...t
143
- });
144
- return (s = r.headers.get("content-type")) != null && s.includes("application/json") ? g.returnJSON(r) : r;
145
- }
146
- async getComponents() {
147
- return this.request(`/files/${this.fileId}/components`);
148
- }
149
- async getStyles() {
150
- return this.request(`/files/${this.fileId}/styles`);
151
- }
152
- async getNodes(e) {
153
- const t = q(e).map(
154
- (o) => this.request(`/files/${this.fileId}/nodes`, { params: { ids: o.join(",") } })
155
- ), r = await Promise.all(t);
156
- return {
157
- ...r[0],
158
- nodes: r.reduce((o, i) => ({ ...o, ...i.nodes }), {})
159
- };
160
- }
161
- }
162
- const J = async () => {
163
- const e = await new m().load();
164
- if (!e)
165
- throw new Error("Заполнить ошибку через нейронку");
166
- const { figmaToken: t, fileId: r, modules: s } = e, o = new g(t, r);
167
- await Promise.all(
168
- // [
169
- // colorsFromStyles({
170
- // input: { variablePaths: ['./dark.tokens.json', './light.tokens.json'] },
171
- // output: { jsonDir: './fromStyles', stylesDir: './fromStyles' },
172
- // }),
173
- // colorsFromVariables({
174
- // input: { variablePaths: ['./dark.tokens.json', './light.tokens.json'] },
175
- // output: { jsonDir: './fromVariables', stylesDir: './fromVariables' },
176
- // }),
177
- // ]
178
- s.map((i) => i.executor({ figmaApiClient: o }))
179
- );
180
- }, A = async () => {
181
- await m.create(), console.log("\x1B[32m%s\x1B[0m", "✔️ Configuration file created gts.config.ts");
182
- };
183
- export {
184
- J as generate,
185
- A as init
186
- };