@charcoal-ui/icons-cli 1.0.0-alpha.1

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/dist/index.cjs ADDED
@@ -0,0 +1,899 @@
1
+ var fsExtra = require('fs-extra');
2
+ var yargs = require('yargs');
3
+ var path = require('path');
4
+ var camelCase = require('camelcase');
5
+ var Figma = require('figma-js');
6
+ var got = require('got');
7
+ var pathToRegexp = require('path-to-regexp');
8
+ var PQueue = require('p-queue');
9
+ var rest = require('@octokit/rest');
10
+ var fs = require('fs');
11
+ var child_process = require('child_process');
12
+ var node = require('@gitbeaker/node');
13
+ var jsdom = require('jsdom');
14
+ var polished = require('polished');
15
+ var Svgo = require('svgo');
16
+
17
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
18
+
19
+ var yargs__default = /*#__PURE__*/_interopDefaultLegacy(yargs);
20
+ var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
21
+ var camelCase__default = /*#__PURE__*/_interopDefaultLegacy(camelCase);
22
+ var Figma__default = /*#__PURE__*/_interopDefaultLegacy(Figma);
23
+ var got__default = /*#__PURE__*/_interopDefaultLegacy(got);
24
+ var PQueue__default = /*#__PURE__*/_interopDefaultLegacy(PQueue);
25
+ var Svgo__default = /*#__PURE__*/_interopDefaultLegacy(Svgo);
26
+
27
+ function concurrently(tasks) {
28
+ const queue = new PQueue__default["default"]({
29
+ concurrency: 3
30
+ });
31
+
32
+ for (const task of tasks) {
33
+ void queue.add(task);
34
+ }
35
+
36
+ queue.start();
37
+ return queue.onIdle();
38
+ }
39
+
40
+ const extracter = pathToRegexp.match('/file/:file/:name');
41
+
42
+ function extractFileId(url) {
43
+ const result = extracter(new URL(url).pathname);
44
+
45
+ if (result === false) {
46
+ throw new Error('no :file in url');
47
+ }
48
+
49
+ return result.params.file;
50
+ }
51
+
52
+ function extractNodeId(url) {
53
+ if (!url.includes('?')) {
54
+ return undefined;
55
+ }
56
+
57
+ const [, query] = url.split('?');
58
+ const params = new URLSearchParams(query);
59
+ const nodeId = params.get('node-id');
60
+
61
+ if (nodeId === null) {
62
+ return undefined;
63
+ }
64
+
65
+ return decodeURIComponent(nodeId);
66
+ }
67
+
68
+ function filenamify(name) {
69
+ return camelCase__default["default"](name, {
70
+ pascalCase: true
71
+ }).replace(' ', '');
72
+ } // eslint-disable-next-line prefer-named-capture-group
73
+
74
+
75
+ const iconName = /^(\d|Inline)+\s*\/\s*/u;
76
+
77
+ function isIconNode(node) {
78
+ return iconName.test(node.name);
79
+ }
80
+
81
+ function getContentType(exportFormat) {
82
+ switch (exportFormat) {
83
+ case 'svg':
84
+ return 'image/svg+xml';
85
+
86
+ case 'pdf':
87
+ return 'application/pdf';
88
+ }
89
+ }
90
+
91
+ class FigmaFileClient {
92
+ static async runFromCli(url, token, outputRootDir, exportFormat) {
93
+ const client = new this(url, token, exportFormat);
94
+ const root = process.cwd();
95
+ const outputDir = path__default["default"].join(root, outputRootDir, exportFormat);
96
+ console.log(`Exporting ${url} components`);
97
+ await client.loadSvg(outputDir);
98
+ console.log(`Exporting components type`);
99
+ const typedefDir = path__default["default"].join(root, outputRootDir, 'src');
100
+ await client.writeTypeDef(typedefDir);
101
+ console.log('success!');
102
+ }
103
+
104
+ constructor(url, personalAccessToken, exportFormat) {
105
+ this.fileId = void 0;
106
+ this.nodeId = void 0;
107
+ this.exportFormat = void 0;
108
+ this.client = void 0;
109
+ this.components = {};
110
+ this.targets = [];
111
+ this.client = Figma__default["default"].Client({
112
+ personalAccessToken
113
+ });
114
+ this.fileId = extractFileId(url);
115
+ this.nodeId = extractNodeId(url);
116
+ this.exportFormat = exportFormat;
117
+ }
118
+
119
+ async loadSvg(outputDir) {
120
+ await fsExtra.remove(outputDir);
121
+ await fsExtra.ensureDir(outputDir);
122
+ await this.loadComponents();
123
+ await this.loadImageUrls();
124
+ await this.downloadImages(outputDir);
125
+ }
126
+ /**
127
+ * Generate union of file names for typing
128
+ */
129
+
130
+
131
+ async writeTypeDef(outputDir) {
132
+ const fullname = path__default["default"].resolve(outputDir, 'filenames.ts');
133
+ const KNOWN_ICON_FILES = Object.values(this.components).map(({
134
+ name
135
+ }) => filenamify(name));
136
+ console.log(`writing to ${outputDir}`);
137
+ await fsExtra.ensureFile(fullname);
138
+ await fsExtra.writeFile(fullname, `/** This file is auto generated. DO NOT EDIT BY HAND. */
139
+
140
+ /* eslint-disable */
141
+
142
+ // prettier-ignore
143
+ export const KNOWN_ICON_FILES = ${JSON.stringify(KNOWN_ICON_FILES)} as const;
144
+
145
+ export type KnownIconFile = typeof KNOWN_ICON_FILES[number];
146
+
147
+ /* eslint-enable */
148
+ `, {
149
+ encoding: 'utf8'
150
+ });
151
+ }
152
+
153
+ async loadComponents() {
154
+ const {
155
+ document
156
+ } = await this.getFile();
157
+ this.targets = document.children.filter(node => {
158
+ if (this.nodeId !== undefined) {
159
+ return node.id === this.nodeId;
160
+ }
161
+
162
+ return false;
163
+ });
164
+ this.targets.forEach(child => this.findComponentsRecursively(child));
165
+
166
+ if (Object.values(this.targets).length === 0) {
167
+ throw new Error('No components found!');
168
+ }
169
+ }
170
+
171
+ async loadImageUrls() {
172
+ console.log('Getting export urls');
173
+ const {
174
+ data
175
+ } = await this.client.fileImages(this.fileId, {
176
+ format: this.exportFormat,
177
+ ids: Object.keys(this.components),
178
+ scale: 1
179
+ });
180
+
181
+ for (const [id, image] of Object.entries(data.images)) {
182
+ this.components[id].image = image;
183
+ }
184
+ }
185
+
186
+ async downloadImages(outputDir) {
187
+ return concurrently(Object.values(this.components).map(component => async () => {
188
+ if (component.image === undefined) {
189
+ return;
190
+ }
191
+
192
+ const response = await got__default["default"].get(component.image, {
193
+ headers: {
194
+ 'Content-Type': getContentType(this.exportFormat)
195
+ },
196
+ encoding: 'utf8'
197
+ });
198
+ const filename = `${filenamify(component.name)}.${this.exportFormat}`;
199
+ const fullname = path__default["default"].join(outputDir, filename);
200
+ const dirname = path__default["default"].dirname(fullname);
201
+ await fsExtra.ensureDir(dirname);
202
+ console.log(`found: ${filename} => ✅ writing...`);
203
+ await fsExtra.writeFile(fullname, response.body, 'utf8');
204
+ }));
205
+ }
206
+
207
+ async getFile() {
208
+ console.log('Processing response');
209
+ const {
210
+ data
211
+ } = await this.client.file(this.fileId.toString());
212
+ return data;
213
+ }
214
+
215
+ findComponentsRecursively(child) {
216
+ if (child.type === 'COMPONENT') {
217
+ const {
218
+ name,
219
+ id
220
+ } = child;
221
+
222
+ if (isIconNode(child)) {
223
+ this.components[id] = {
224
+ name,
225
+ id
226
+ };
227
+ }
228
+ } else if ('children' in child) {
229
+ child.children.forEach(grandChild => this.findComponentsRecursively(grandChild));
230
+ }
231
+ }
232
+
233
+ }
234
+
235
+ function _asyncIterator(iterable) {
236
+ var method,
237
+ async,
238
+ sync,
239
+ retry = 2;
240
+
241
+ for ("undefined" != typeof Symbol && (async = Symbol.asyncIterator, sync = Symbol.iterator); retry--;) {
242
+ if (async && null != (method = iterable[async])) return method.call(iterable);
243
+ if (sync && null != (method = iterable[sync])) return new AsyncFromSyncIterator(method.call(iterable));
244
+ async = "@@asyncIterator", sync = "@@iterator";
245
+ }
246
+
247
+ throw new TypeError("Object is not async iterable");
248
+ }
249
+
250
+ function AsyncFromSyncIterator(s) {
251
+ function AsyncFromSyncIteratorContinuation(r) {
252
+ if (Object(r) !== r) return Promise.reject(new TypeError(r + " is not an object."));
253
+ var done = r.done;
254
+ return Promise.resolve(r.value).then(function (value) {
255
+ return {
256
+ value: value,
257
+ done: done
258
+ };
259
+ });
260
+ }
261
+
262
+ return AsyncFromSyncIterator = function (s) {
263
+ this.s = s, this.n = s.next;
264
+ }, AsyncFromSyncIterator.prototype = {
265
+ s: null,
266
+ n: null,
267
+ next: function () {
268
+ return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments));
269
+ },
270
+ return: function (value) {
271
+ var ret = this.s.return;
272
+ return void 0 === ret ? Promise.resolve({
273
+ value: value,
274
+ done: !0
275
+ }) : AsyncFromSyncIteratorContinuation(ret.apply(this.s, arguments));
276
+ },
277
+ throw: function (value) {
278
+ var thr = this.s.return;
279
+ return void 0 === thr ? Promise.reject(value) : AsyncFromSyncIteratorContinuation(thr.apply(this.s, arguments));
280
+ }
281
+ }, new AsyncFromSyncIterator(s);
282
+ }
283
+
284
+ function _AwaitValue(value) {
285
+ this.wrapped = value;
286
+ }
287
+
288
+ function _AsyncGenerator(gen) {
289
+ var front, back;
290
+
291
+ function send(key, arg) {
292
+ return new Promise(function (resolve, reject) {
293
+ var request = {
294
+ key: key,
295
+ arg: arg,
296
+ resolve: resolve,
297
+ reject: reject,
298
+ next: null
299
+ };
300
+
301
+ if (back) {
302
+ back = back.next = request;
303
+ } else {
304
+ front = back = request;
305
+ resume(key, arg);
306
+ }
307
+ });
308
+ }
309
+
310
+ function resume(key, arg) {
311
+ try {
312
+ var result = gen[key](arg);
313
+ var value = result.value;
314
+ var wrappedAwait = value instanceof _AwaitValue;
315
+ Promise.resolve(wrappedAwait ? value.wrapped : value).then(function (arg) {
316
+ if (wrappedAwait) {
317
+ resume(key === "return" ? "return" : "next", arg);
318
+ return;
319
+ }
320
+
321
+ settle(result.done ? "return" : "normal", arg);
322
+ }, function (err) {
323
+ resume("throw", err);
324
+ });
325
+ } catch (err) {
326
+ settle("throw", err);
327
+ }
328
+ }
329
+
330
+ function settle(type, value) {
331
+ switch (type) {
332
+ case "return":
333
+ front.resolve({
334
+ value: value,
335
+ done: true
336
+ });
337
+ break;
338
+
339
+ case "throw":
340
+ front.reject(value);
341
+ break;
342
+
343
+ default:
344
+ front.resolve({
345
+ value: value,
346
+ done: false
347
+ });
348
+ break;
349
+ }
350
+
351
+ front = front.next;
352
+
353
+ if (front) {
354
+ resume(front.key, front.arg);
355
+ } else {
356
+ back = null;
357
+ }
358
+ }
359
+
360
+ this._invoke = send;
361
+
362
+ if (typeof gen.return !== "function") {
363
+ this.return = undefined;
364
+ }
365
+ }
366
+
367
+ _AsyncGenerator.prototype[typeof Symbol === "function" && Symbol.asyncIterator || "@@asyncIterator"] = function () {
368
+ return this;
369
+ };
370
+
371
+ _AsyncGenerator.prototype.next = function (arg) {
372
+ return this._invoke("next", arg);
373
+ };
374
+
375
+ _AsyncGenerator.prototype.throw = function (arg) {
376
+ return this._invoke("throw", arg);
377
+ };
378
+
379
+ _AsyncGenerator.prototype.return = function (arg) {
380
+ return this._invoke("return", arg);
381
+ };
382
+
383
+ function _wrapAsyncGenerator(fn) {
384
+ return function () {
385
+ return new _AsyncGenerator(fn.apply(this, arguments));
386
+ };
387
+ }
388
+
389
+ function _awaitAsyncGenerator(value) {
390
+ return new _AwaitValue(value);
391
+ }
392
+
393
+ function _extends() {
394
+ _extends = Object.assign || function (target) {
395
+ for (var i = 1; i < arguments.length; i++) {
396
+ var source = arguments[i];
397
+
398
+ for (var key in source) {
399
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
400
+ target[key] = source[key];
401
+ }
402
+ }
403
+ }
404
+
405
+ return target;
406
+ };
407
+
408
+ return _extends.apply(this, arguments);
409
+ }
410
+
411
+ /**
412
+ * FIXME: util.promisify を使うと node-libs-browser に入っている方が使われてしまい、壊れる
413
+ */
414
+
415
+ const execp = command => new Promise((resolve, reject) => {
416
+ child_process.exec(command, (err, stdout) => {
417
+ if (err) {
418
+ return reject(err);
419
+ }
420
+
421
+ return resolve(stdout);
422
+ });
423
+ });
424
+ function mustBeDefined(value, name) {
425
+ if (typeof value === 'undefined') {
426
+ throw new TypeError(`${name} must be defined.`);
427
+ }
428
+ }
429
+
430
+ const targetDir = path__default["default"].resolve(process.cwd(), 'packages', 'icons', 'svg');
431
+ /**
432
+ * dir 内で変更があったファイル情報を for await で回せるようにするやつ
433
+ */
434
+
435
+ function getChangedFiles() {
436
+ return _getChangedFiles.apply(this, arguments);
437
+ }
438
+
439
+ function _getChangedFiles() {
440
+ _getChangedFiles = _wrapAsyncGenerator(function* (dir = targetDir) {
441
+ const directories = yield _awaitAsyncGenerator(fs.promises.readdir(dir));
442
+ const gitStatus = yield _awaitAsyncGenerator(collectGitStatus());
443
+
444
+ for (const dir of directories) {
445
+ const directory = path__default["default"].resolve(targetDir, dir); // eslint-disable-next-line no-await-in-loop
446
+
447
+ const stat = yield _awaitAsyncGenerator(fs.promises.stat(directory));
448
+
449
+ if (!stat.isDirectory()) {
450
+ continue;
451
+ } // eslint-disable-next-line no-await-in-loop
452
+
453
+
454
+ const files = yield _awaitAsyncGenerator(fs.promises.readdir(directory));
455
+
456
+ for (const file of files) {
457
+ const fullpath = path__default["default"].resolve(targetDir, dir, file);
458
+ const relativePath = path__default["default"].relative(process.cwd(), fullpath);
459
+ const status = gitStatus[relativePath];
460
+
461
+ if (status == null) {
462
+ // Already up-to-date
463
+ continue;
464
+ } // eslint-disable-next-line no-await-in-loop
465
+
466
+
467
+ const content = yield _awaitAsyncGenerator(fs.promises.readFile(fullpath, {
468
+ encoding: 'utf-8'
469
+ }));
470
+ yield {
471
+ relativePath,
472
+ content,
473
+ status
474
+ };
475
+ }
476
+ }
477
+ });
478
+ return _getChangedFiles.apply(this, arguments);
479
+ }
480
+
481
+ async function collectGitStatus() {
482
+ return Object.fromEntries(
483
+ /**
484
+ * @see https://git-scm.com/docs/git-status#_porcelain_format_version_1
485
+ */
486
+ (await execp(`git status --porcelain`)).split('\n').map(s => [s.slice(3), s.startsWith(' M') ? 'modified' : s.startsWith('??') ? 'untracked' : s.startsWith(' D') ? 'deleted' : null]));
487
+ }
488
+
489
+ class GithubClient {
490
+ static async runFromCli(repoOwner, repoName, token, defaultBranch) {
491
+ const client = new this(repoOwner, repoName, token, defaultBranch);
492
+ const diff = await client.createTreeFromDiff();
493
+ console.log(`${diff.length} files are changed`);
494
+
495
+ if (diff.length === 0) {
496
+ console.log('no changes. aborting');
497
+ return;
498
+ }
499
+
500
+ const newBranch = await client.createBranch();
501
+ await client.createCommit(diff, newBranch);
502
+ return client.createPullRequest(newBranch);
503
+ }
504
+
505
+ constructor(repoOwner, repoName, token, defaultBranch, now = new Date()) {
506
+ this.repoOwner = void 0;
507
+ this.repoName = void 0;
508
+ this.defaultBranch = void 0;
509
+ this.api = void 0;
510
+ this.now = void 0;
511
+ this.repoOwner = repoOwner;
512
+ this.repoName = repoName;
513
+ this.defaultBranch = defaultBranch;
514
+ this.api = new rest.Octokit({
515
+ auth: token
516
+ });
517
+ this.now = now;
518
+ }
519
+
520
+ get branch() {
521
+ return `icons/update/${this.now.getTime()}`;
522
+ }
523
+ /**
524
+ * both used for commit message or pull request title
525
+ */
526
+
527
+
528
+ get message() {
529
+ return `[icons-cli] Update icons ${this.now.toDateString()}`;
530
+ }
531
+
532
+ async createTreeFromDiff() {
533
+ const tree = [];
534
+ var _iteratorAbruptCompletion = false;
535
+ var _didIteratorError = false;
536
+
537
+ var _iteratorError;
538
+
539
+ try {
540
+ for (var _iterator = _asyncIterator(getChangedFiles()), _step; _iteratorAbruptCompletion = !(_step = await _iterator.next()).done; _iteratorAbruptCompletion = false) {
541
+ const file = _step.value;
542
+ const item = {
543
+ path: file.relativePath,
544
+ // 100 はファイル 644 は実行不可なファイルであるという意味
545
+ // @see https://octokit.github.io/rest.js/v18#git-create-tree
546
+ mode: '100644',
547
+ content: file.content
548
+ };
549
+
550
+ if (file.status === 'deleted') {
551
+ // https://stackoverflow.com/questions/23637961/how-do-i-mark-a-file-as-deleted-in-a-tree-using-the-github-api
552
+ tree.push(_extends({}, item, {
553
+ sha: null
554
+ }));
555
+ } else {
556
+ tree.push(item);
557
+ }
558
+ }
559
+ } catch (err) {
560
+ _didIteratorError = true;
561
+ _iteratorError = err;
562
+ } finally {
563
+ try {
564
+ if (_iteratorAbruptCompletion && _iterator.return != null) {
565
+ await _iterator.return();
566
+ }
567
+ } finally {
568
+ if (_didIteratorError) {
569
+ throw _iteratorError;
570
+ }
571
+ }
572
+ }
573
+
574
+ return tree;
575
+ }
576
+
577
+ async createCommit(tree, targetBranch) {
578
+ const parentCommit = await this.api.git.getCommit({
579
+ owner: this.repoOwner,
580
+ repo: this.repoName,
581
+ // eslint-disable-next-line @typescript-eslint/naming-convention
582
+ commit_sha: targetBranch.data.object.sha
583
+ });
584
+ const newTree = await this.api.git.createTree({
585
+ owner: this.repoOwner,
586
+ repo: this.repoName,
587
+ // eslint-disable-next-line @typescript-eslint/naming-convention
588
+ base_tree: parentCommit.data.tree.sha,
589
+ tree
590
+ }); // この時点ではどのブランチにも属さないコミットができる
591
+
592
+ const commit = await this.api.git.createCommit({
593
+ owner: this.repoOwner,
594
+ repo: this.repoName,
595
+ message: this.message,
596
+ tree: newTree.data.sha,
597
+ parents: [parentCommit.data.sha]
598
+ }); // ref を更新することで、commit が targetBranch に属するようになる
599
+
600
+ await this.api.git.updateRef({
601
+ owner: this.repoOwner,
602
+ repo: this.repoName,
603
+ ref: `heads/${this.branch}`,
604
+ sha: commit.data.sha
605
+ });
606
+ return commit;
607
+ }
608
+
609
+ async createPullRequest(targetBranch) {
610
+ const defaultBranch = await this.getDefaultBranchRef();
611
+ return this.api.pulls.create({
612
+ owner: this.repoOwner,
613
+ repo: this.repoName,
614
+ head: targetBranch.data.ref,
615
+ base: defaultBranch.data.ref,
616
+ title: this.message,
617
+ body: ''
618
+ });
619
+ }
620
+
621
+ getDefaultBranchRef() {
622
+ return this.api.git.getRef({
623
+ owner: this.repoOwner,
624
+ repo: this.repoName,
625
+ ref: `heads/${this.defaultBranch}`
626
+ });
627
+ }
628
+
629
+ async createBranch() {
630
+ const defaultBranch = await this.getDefaultBranchRef();
631
+ return this.api.git.createRef({
632
+ owner: this.repoOwner,
633
+ repo: this.repoName,
634
+ ref: `refs/heads/${this.branch}`,
635
+ sha: defaultBranch.data.object.sha
636
+ });
637
+ }
638
+
639
+ }
640
+
641
+ class GitlabClient {
642
+ static async runFromCli(host, projectId, privateToken, defaultBranch) {
643
+ const client = new this(host, projectId, privateToken, defaultBranch);
644
+ const diff = await client.createActionsFromDiff();
645
+ console.log(`${diff.length} files are changed`);
646
+
647
+ if (diff.length === 0) {
648
+ console.log('no changes. aborting');
649
+ return;
650
+ }
651
+
652
+ await client.createCommit(diff);
653
+ return client.createMergeRequest();
654
+ }
655
+
656
+ constructor(host, projectId, privateToken, defaultBranch, now = new Date()) {
657
+ this.host = void 0;
658
+ this.projectId = void 0;
659
+ this.defaultBranch = void 0;
660
+ this.api = void 0;
661
+ this.now = void 0;
662
+ this.host = host;
663
+ this.projectId = projectId;
664
+ this.defaultBranch = defaultBranch;
665
+ this.api = new node.Gitlab({
666
+ host: this.host,
667
+ token: privateToken
668
+ });
669
+ this.now = now;
670
+ }
671
+
672
+ get branch() {
673
+ return `icons/update/${this.now.getTime()}`;
674
+ }
675
+ /**
676
+ * both used for commit message or merge request title
677
+ */
678
+
679
+
680
+ get message() {
681
+ return `[icons-cli] Update icons ${this.now.toDateString()}`;
682
+ }
683
+
684
+ async createActionsFromDiff() {
685
+ const actions = [];
686
+ var _iteratorAbruptCompletion = false;
687
+ var _didIteratorError = false;
688
+
689
+ var _iteratorError;
690
+
691
+ try {
692
+ for (var _iterator = _asyncIterator(getChangedFiles()), _step; _iteratorAbruptCompletion = !(_step = await _iterator.next()).done; _iteratorAbruptCompletion = false) {
693
+ const file = _step.value;
694
+ actions.push({
695
+ action: file.status === 'untracked' ? 'create' : file.status === 'deleted' ? 'delete' : 'update',
696
+ filePath: file.relativePath,
697
+ content: file.content
698
+ });
699
+ }
700
+ } catch (err) {
701
+ _didIteratorError = true;
702
+ _iteratorError = err;
703
+ } finally {
704
+ try {
705
+ if (_iteratorAbruptCompletion && _iterator.return != null) {
706
+ await _iterator.return();
707
+ }
708
+ } finally {
709
+ if (_didIteratorError) {
710
+ throw _iteratorError;
711
+ }
712
+ }
713
+ }
714
+
715
+ return actions;
716
+ }
717
+
718
+ async createCommit(diff) {
719
+ return this.api.Commits.create(this.projectId, this.branch, this.message, diff, {
720
+ // eslint-disable-next-line @typescript-eslint/naming-convention
721
+ start_branch: this.defaultBranch
722
+ });
723
+ }
724
+
725
+ createMergeRequest() {
726
+ return this.api.MergeRequests.create(this.projectId, this.branch, this.defaultBranch, this.message);
727
+ }
728
+
729
+ }
730
+
731
+ const DEFAULT_CURRENT_COLOR_TARGET = '#858585';
732
+ const svgo = new Svgo__default["default"]({
733
+ plugins: [// NOTICE: SVGO は「svg 内のすべての fill を currentColor に変える」機能しかない
734
+ // icons-cli に必要なのは「特定の黒っぽい色だけ currentColor に変える」機能
735
+ // なので、convertColors plugin は使わない
736
+ // { convertColors: { currentColor: true } },
737
+ {
738
+ removeViewBox: true
739
+ }, {
740
+ removeAttrs: {
741
+ attrs: ['stroke-opacity', 'fill-opacity']
742
+ }
743
+ }]
744
+ });
745
+ async function optimizeSvg(input, convertedColor) {
746
+ const {
747
+ data
748
+ } = await svgo.optimize(input);
749
+ const {
750
+ document
751
+ } = new jsdom.JSDOM(data).window;
752
+ const svg = document.querySelector('svg');
753
+
754
+ if (!svg) {
755
+ throw new Error('optimizeSvg: input string seems not to have <svg>');
756
+ }
757
+
758
+ addViewboxToRootSvg(svg);
759
+ convertToCurrentColor(svg, convertedColor);
760
+ return svg.outerHTML;
761
+ }
762
+ const TARGET_ATTRS = ['fill', 'stroke'];
763
+
764
+ function convertToCurrentColor(svg, convertedColor) {
765
+ const targetColor = parseColor(convertedColor);
766
+
767
+ if (!targetColor) {
768
+ throw new Error(`${convertedColor} is not a valid color`);
769
+ }
770
+
771
+ for (const attr of TARGET_ATTRS) {
772
+ const targets = Array.from(svg.querySelectorAll(`[${attr}]`));
773
+
774
+ for (const el of targets) {
775
+ const value = parseColor(el.getAttribute(attr));
776
+
777
+ if (!value) {
778
+ continue;
779
+ }
780
+
781
+ if (!colorEquals(value, targetColor)) {
782
+ continue;
783
+ }
784
+
785
+ el.setAttribute(attr, 'currentColor');
786
+ }
787
+ }
788
+ }
789
+
790
+ function parseColor(value) {
791
+ if (value == null) {
792
+ return null;
793
+ }
794
+
795
+ try {
796
+ return polished.parseToRgb(value);
797
+ } catch (_unused) {
798
+ return null;
799
+ }
800
+ }
801
+
802
+ function colorEquals(self, other) {
803
+ if (self.red !== other.red) {
804
+ return false;
805
+ }
806
+
807
+ if (self.blue !== other.blue) {
808
+ return false;
809
+ }
810
+
811
+ if (self.green !== other.green) {
812
+ return false;
813
+ }
814
+
815
+ if ('alpha' in self) {
816
+ if ('alpha' in other) {
817
+ if (self.alpha !== other.alpha) {
818
+ return false;
819
+ }
820
+ }
821
+ }
822
+
823
+ return true;
824
+ }
825
+
826
+ function addViewboxToRootSvg(svg) {
827
+ const width = svg.getAttribute('width');
828
+ const height = svg.getAttribute('height');
829
+ svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
830
+ }
831
+
832
+ /**
833
+ * Figma
834
+ */
835
+
836
+ const FIGMA_TOKEN = process.env.FIGMA_TOKEN;
837
+ const FIGMA_FILE_URL = process.env.FIGMA_FILE_URL;
838
+ const OUTPUT_ROOT_DIR = process.env.OUTPUT_ROOT_DIR;
839
+ /**
840
+ * GitLab
841
+ */
842
+
843
+ const GITLAB_ACCESS_TOKEN = process.env.GITLAB_ACCESS_TOKEN;
844
+ const GITLAB_DEFAULT_BRANCH = process.env.GITLAB_DEFAULT_BRANCH;
845
+ const GITLAB_HOST = process.env.GITLAB_HOST;
846
+ const GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID;
847
+ /**
848
+ * GitHub
849
+ */
850
+
851
+ const GITHUB_ACCESS_TOKEN = process.env.GITHUB_ACCESS_TOKEN;
852
+ const GITHUB_REPO_OWNER = process.env.GITHUB_REPO_OWNER;
853
+ const GITHUB_REPO_NAME = process.env.GITHUB_REPO_NAME;
854
+ const GITHUB_DEFAULT_BRANCH = process.env.GITHUB_DEFAULT_BRANCH;
855
+ void yargs__default["default"].scriptName('icons-cli').command('figma:export', 'Load all icons from Figma and save to files', () => yargs__default["default"].option('format', {
856
+ default: 'svg',
857
+ choices: ['svg', 'pdf']
858
+ }), ({
859
+ format
860
+ }) => {
861
+ mustBeDefined(FIGMA_FILE_URL, 'FIGMA_FILE_URL');
862
+ mustBeDefined(FIGMA_TOKEN, 'FIGMA_TOKEN');
863
+ mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR');
864
+ void FigmaFileClient.runFromCli(FIGMA_FILE_URL, FIGMA_TOKEN, OUTPUT_ROOT_DIR, format).catch(e => {
865
+ console.error(e);
866
+ process.exit(1);
867
+ });
868
+ }).command('svg:optimize', 'Optimize given .svg files and overwrite it', () => yargs__default["default"].option('file', {
869
+ demandOption: true,
870
+ type: 'string'
871
+ }).option('color', {
872
+ default: DEFAULT_CURRENT_COLOR_TARGET,
873
+ type: 'string',
874
+ defaultDescription: 'Color code that should be converted into `currentColor`'
875
+ }), ({
876
+ file,
877
+ color
878
+ }) => {
879
+ void fsExtra.readFile(file, {
880
+ encoding: 'utf-8'
881
+ }).then(content => optimizeSvg(content, color)).then(optimized => fsExtra.writeFile(file, optimized)).catch(e => {
882
+ console.error(e);
883
+ process.exit(1);
884
+ });
885
+ }).command('gitlab:mr', 'Create a merge request in the name of icons-cli', {}, () => {
886
+ mustBeDefined(GITLAB_PROJECT_ID, 'GITLAB_PROJECT_ID');
887
+ mustBeDefined(GITLAB_ACCESS_TOKEN, 'GITLAB_ACCESS_TOKEN');
888
+ void GitlabClient.runFromCli(GITLAB_HOST != null ? GITLAB_HOST : 'https://gitlab.com', Number(GITLAB_PROJECT_ID), GITLAB_ACCESS_TOKEN, GITLAB_DEFAULT_BRANCH != null ? GITLAB_DEFAULT_BRANCH : 'main').catch(e => {
889
+ console.error(e);
890
+ process.exit(1);
891
+ });
892
+ }).command('github:pr', 'Create a pull request in the name of icons-cli', {}, () => {
893
+ mustBeDefined(GITHUB_ACCESS_TOKEN, 'GITHUB_ACCESS_TOKEN');
894
+ void GithubClient.runFromCli(GITHUB_REPO_OWNER != null ? GITHUB_REPO_OWNER : 'pixiv', GITHUB_REPO_NAME != null ? GITHUB_REPO_NAME : 'charcoal', GITHUB_ACCESS_TOKEN, GITHUB_DEFAULT_BRANCH != null ? GITHUB_DEFAULT_BRANCH : 'main').catch(e => {
895
+ console.error(e);
896
+ process.exit(1);
897
+ });
898
+ }).demandCommand().help().parse();
899
+ //# sourceMappingURL=index.cjs.map