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