@charcoal-ui/icons-cli 4.7.2 → 4.8.1-beta.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.js CHANGED
@@ -46,6 +46,7 @@ function concurrently(tasks) {
46
46
  }
47
47
 
48
48
  // src/figma/FigmaFileClient.ts
49
+ var DRY_RUN = Boolean(process.env.DRY_RUN);
49
50
  var matchPath = (0, import_path_to_regexp.match)("/file/:fileId/:name");
50
51
  function extractParams(url) {
51
52
  const { pathname, searchParams } = new URL(url);
@@ -65,20 +66,26 @@ var iconName = /^(?:\d+|Inline)\s*\//u;
65
66
  function isIconNode(node) {
66
67
  return iconName.test(node.name);
67
68
  }
69
+ function parseV2IconName(name) {
70
+ return name.split(",").map((f) => f.split("=").map((s) => s.trim())[1]).join("/").toLowerCase();
71
+ }
68
72
  var FigmaFileClient = class {
69
73
  fileId;
70
74
  nodeId;
71
75
  exportFormat;
72
76
  client;
77
+ layoutVersion;
73
78
  components = {};
74
- static async runFromCli(url, token, outputRootDir, exportFormat) {
75
- const client = new this(url, token, exportFormat);
79
+ static async runFromCli(url, token, outputRootDir, exportFormat, layoutVersion = "v1") {
80
+ const client = new this(url, token, exportFormat, layoutVersion);
76
81
  const outputDir = import_path.default.join(process.cwd(), outputRootDir, exportFormat);
77
- console.log(`Exporting components from ${url}`);
82
+ console.log(
83
+ `Exporting components from ${url} using layout ${layoutVersion}`
84
+ );
78
85
  await client.loadSvg(outputDir);
79
86
  console.log("success!");
80
87
  }
81
- constructor(url, personalAccessToken, exportFormat) {
88
+ constructor(url, personalAccessToken, exportFormat, layoutVersion) {
82
89
  this.client = Figma.Client({
83
90
  personalAccessToken
84
91
  });
@@ -86,6 +93,7 @@ var FigmaFileClient = class {
86
93
  this.fileId = fileId;
87
94
  this.nodeId = nodeId;
88
95
  this.exportFormat = exportFormat;
96
+ this.layoutVersion = layoutVersion;
89
97
  }
90
98
  async loadSvg(outputDir) {
91
99
  await (0, import_fs_extra.remove)(outputDir);
@@ -96,10 +104,17 @@ var FigmaFileClient = class {
96
104
  }
97
105
  async loadComponents() {
98
106
  const { document } = await this.getFile();
99
- const targets = this.nodeId !== void 0 ? document.children.filter((node) => node.id === this.nodeId) : document.children;
100
- targets.forEach((child) => this.findComponentsRecursively(child));
101
- if (Object.keys(this.components).length === 0) {
107
+ if (this.layoutVersion === "v2") {
108
+ this.findComponentsV2(document);
109
+ } else {
110
+ const targets = this.nodeId !== void 0 ? document.children.filter((node) => node.id === this.nodeId) : document.children;
111
+ targets.forEach((child) => this.findComponentsRecursively(child));
112
+ }
113
+ const len = Object.keys(this.components).length;
114
+ if (len === 0) {
102
115
  throw new Error("No components found!");
116
+ } else {
117
+ console.log(`found ${len} icons`);
103
118
  }
104
119
  }
105
120
  async loadImageUrls() {
@@ -120,13 +135,17 @@ var FigmaFileClient = class {
120
135
  if (component.image === void 0) {
121
136
  return;
122
137
  }
138
+ const filename = component.variant ? `${parseV2IconName(component.variant)}/${component.name}.${this.exportFormat}` : `${filenamify(component.name)}.${this.exportFormat}`;
139
+ const fullname = import_path.default.join(outputDir, filename);
140
+ const dirname = import_path.default.dirname(fullname);
141
+ if (DRY_RUN) {
142
+ console.log(`[DRY_RUN] skip: ${filename} => \u2705 writing...`);
143
+ return;
144
+ }
123
145
  const response = await import_got.default.get(
124
146
  component.image,
125
147
  this.exportFormat == "pdf" ? { responseType: "buffer" } : {}
126
148
  );
127
- const filename = `${filenamify(component.name)}.${this.exportFormat}`;
128
- const fullname = import_path.default.join(outputDir, filename);
129
- const dirname = import_path.default.dirname(fullname);
130
149
  await (0, import_fs_extra.ensureDir)(dirname);
131
150
  console.log(`found: ${filename} => \u2705 writing...`);
132
151
  await (0, import_fs_extra.writeFile)(fullname, response.body, "utf8");
@@ -153,6 +172,29 @@ var FigmaFileClient = class {
153
172
  );
154
173
  }
155
174
  }
175
+ findComponentsV2(document) {
176
+ const iconsPage = document.children.find(
177
+ (child) => child.name === "Icons \u4E00\u89A7" && child.type === "CANVAS"
178
+ );
179
+ const iconComponentSets = iconsPage == null ? void 0 : iconsPage.children.flatMap((c) => {
180
+ if (c.type !== "FRAME")
181
+ return [];
182
+ return c.children.filter(
183
+ (c2) => c2.type === "COMPONENT_SET"
184
+ );
185
+ });
186
+ iconComponentSets == null ? void 0 : iconComponentSets.forEach((set) => {
187
+ set.children.forEach((i) => {
188
+ if (i.type !== "COMPONENT")
189
+ return null;
190
+ this.components[i.id] = {
191
+ name: set.name,
192
+ variant: i.name,
193
+ id: i.id
194
+ };
195
+ });
196
+ });
197
+ }
156
198
  };
157
199
 
158
200
  // src/GitHubClient.ts
@@ -578,9 +620,13 @@ void import_yargs.default.scriptName("icons-cli").command(
578
620
  default: "svg",
579
621
  choices: ["svg", "pdf"],
580
622
  describe: "Output format"
623
+ },
624
+ layout: {
625
+ default: "v1",
626
+ describe: "Figma icon file layout version"
581
627
  }
582
628
  },
583
- async ({ format }) => {
629
+ async ({ format, layout }) => {
584
630
  mustBeDefined(FIGMA_FILE_URL, "FIGMA_FILE_URL");
585
631
  mustBeDefined(FIGMA_TOKEN, "FIGMA_TOKEN");
586
632
  mustBeDefined(OUTPUT_ROOT_DIR, "OUTPUT_ROOT_DIR");
@@ -588,7 +634,8 @@ void import_yargs.default.scriptName("icons-cli").command(
588
634
  FIGMA_FILE_URL,
589
635
  FIGMA_TOKEN,
590
636
  OUTPUT_ROOT_DIR,
591
- format
637
+ format,
638
+ layout
592
639
  );
593
640
  }
594
641
  ).command(
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/figma/FigmaFileClient.ts","../src/concurrently.ts","../src/GitHubClient.ts","../src/getChangedFiles.ts","../src/utils.ts","../src/GitlabClient.ts","../src/svg/optimizeSvg.ts","../src/svg/optimizeSvgInDirectory.ts","../src/generateSource.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport yargs from 'yargs'\nimport { FigmaFileClient } from './figma/FigmaFileClient'\nimport { GithubClient } from './GitHubClient'\nimport { GitlabClient } from './GitlabClient'\nimport { DEFAULT_CURRENT_COLOR_TARGET } from './svg/optimizeSvg'\nimport { optimizeSvgInDirectory } from './svg/optimizeSvgInDirectory'\nimport { generateIconSource } from './generateSource'\nimport { mustBeDefined } from './utils'\n\n/**\n * Figma\n */\nconst FIGMA_TOKEN = process.env.FIGMA_TOKEN\nconst FIGMA_FILE_URL = process.env.FIGMA_FILE_URL\nconst OUTPUT_ROOT_DIR = process.env.OUTPUT_ROOT_DIR\n\n/**\n * GitLab\n */\nconst GITLAB_ACCESS_TOKEN = process.env.GITLAB_ACCESS_TOKEN\nconst GITLAB_DEFAULT_BRANCH = process.env.GITLAB_DEFAULT_BRANCH\nconst GITLAB_HOST = process.env.GITLAB_HOST\nconst GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID\n\n/**\n * GitHub\n */\nconst GITHUB_ACCESS_TOKEN = process.env.GITHUB_ACCESS_TOKEN\nconst GITHUB_REPO_OWNER = process.env.GITHUB_REPO_OWNER\nconst GITHUB_REPO_NAME = process.env.GITHUB_REPO_NAME\nconst GITHUB_DEFAULT_BRANCH = process.env.GITHUB_DEFAULT_BRANCH\n\nvoid yargs\n .scriptName('icons-cli')\n .command(\n 'figma:export',\n 'Load all icons from Figma and save to files',\n {\n format: {\n default: 'svg',\n choices: ['svg', 'pdf'],\n describe: 'Output format',\n },\n },\n async ({ format }) => {\n mustBeDefined(FIGMA_FILE_URL, 'FIGMA_FILE_URL')\n mustBeDefined(FIGMA_TOKEN, 'FIGMA_TOKEN')\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n await FigmaFileClient.runFromCli(\n FIGMA_FILE_URL,\n FIGMA_TOKEN,\n OUTPUT_ROOT_DIR,\n format as 'svg' | 'pdf'\n )\n }\n )\n .command(\n 'svg:optimize',\n 'Optimize svg files in output directory',\n {\n color: {\n default: DEFAULT_CURRENT_COLOR_TARGET,\n type: 'string',\n describe: 'Color code that should be converted into `currentColor`',\n },\n ignoreFile: {\n type: 'string',\n describe:\n 'A file that contains the list of path to SVG files that should not be optimized',\n },\n },\n async ({ color, ignoreFile }) => {\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n await optimizeSvgInDirectory(OUTPUT_ROOT_DIR, color, ignoreFile)\n }\n )\n .command(\n 'files:generate',\n 'Enumerate svg files in output directory and generate icon files',\n {},\n () => {\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n void generateIconSource(OUTPUT_ROOT_DIR).catch((e) => {\n // eslint-disable-next-line no-console\n console.error(e)\n process.exit(1)\n })\n }\n )\n .command(\n 'gitlab:mr',\n 'Create a merge request in the name of icons-cli',\n {},\n async () => {\n mustBeDefined(GITLAB_PROJECT_ID, 'GITLAB_PROJECT_ID')\n mustBeDefined(GITLAB_ACCESS_TOKEN, 'GITLAB_ACCESS_TOKEN')\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n await GitlabClient.runFromCli(\n GITLAB_HOST ?? 'https://gitlab.com',\n Number(GITLAB_PROJECT_ID),\n GITLAB_ACCESS_TOKEN,\n GITLAB_DEFAULT_BRANCH ?? 'main',\n OUTPUT_ROOT_DIR\n )\n }\n )\n .command(\n 'github:pr',\n 'Create a pull request in the name of icons-cli',\n {},\n async () => {\n mustBeDefined(GITHUB_ACCESS_TOKEN, 'GITHUB_ACCESS_TOKEN')\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n await GithubClient.runFromCli(\n GITHUB_REPO_OWNER ?? 'pixiv',\n GITHUB_REPO_NAME ?? 'charcoal',\n GITHUB_ACCESS_TOKEN,\n GITHUB_DEFAULT_BRANCH ?? 'main',\n OUTPUT_ROOT_DIR\n )\n }\n )\n .demandCommand()\n .strict()\n .help()\n .parse()\n","import path from 'path'\nimport camelCase from 'camelcase'\nimport * as Figma from 'figma-js'\nimport { ensureDir, remove, writeFile } from 'fs-extra'\nimport got from 'got'\nimport { match } from 'path-to-regexp'\nimport { concurrently } from '../concurrently'\n\nconst matchPath = match<{ fileId: string; name: string }>('/file/:fileId/:name')\n\nfunction extractParams(url: string): { fileId: string; nodeId?: string } {\n const { pathname, searchParams } = new URL(url)\n\n const result = matchPath(pathname)\n if (result === false) {\n throw new Error('No fileId found in url')\n }\n\n return {\n fileId: result.params.fileId,\n nodeId: searchParams.get('node-id') ?? undefined,\n }\n}\n\nfunction filenamify(name: string) {\n return camelCase(name, { pascalCase: true }).replace(' ', '')\n}\n\nconst iconName = /^(?:\\d+|Inline)\\s*\\//u\n\nfunction isIconNode(node: Figma.Node) {\n return iconName.test(node.name)\n}\n\ntype ExportFormat = 'svg' | 'pdf'\n\ninterface Component {\n id: string\n name: string\n image?: string\n}\n\nexport class FigmaFileClient {\n private readonly fileId: string\n private readonly nodeId?: string\n private readonly exportFormat: ExportFormat\n private readonly client: Figma.ClientInterface\n\n private components: Record<string, Component> = {}\n\n static async runFromCli(\n url: string,\n token: string,\n outputRootDir: string,\n exportFormat: ExportFormat\n ) {\n const client = new this(url, token, exportFormat)\n\n const outputDir = path.join(process.cwd(), outputRootDir, exportFormat)\n\n // eslint-disable-next-line no-console\n console.log(`Exporting components from ${url}`)\n await client.loadSvg(outputDir)\n\n // eslint-disable-next-line no-console\n console.log('success!')\n }\n\n constructor(\n url: string,\n personalAccessToken: string,\n exportFormat: ExportFormat\n ) {\n this.client = Figma.Client({\n personalAccessToken,\n })\n\n const { fileId, nodeId } = extractParams(url)\n this.fileId = fileId\n this.nodeId = nodeId\n\n this.exportFormat = exportFormat\n }\n\n async loadSvg(outputDir: string) {\n await remove(outputDir)\n await ensureDir(outputDir)\n\n await this.loadComponents()\n await this.loadImageUrls()\n await this.downloadImages(outputDir)\n }\n\n private async loadComponents() {\n const { document } = await this.getFile()\n\n // nodeIdが指定されている場合は、IDが一致するノードのみを探索対象にする\n // 指定されていない場合はドキュメント全体が探索対象\n const targets =\n this.nodeId !== undefined\n ? document.children.filter((node) => node.id === this.nodeId)\n : document.children\n\n // 対象ノードの子孫を探索してアイコンのコンポーネントを見つける\n targets.forEach((child) => this.findComponentsRecursively(child))\n\n if (Object.keys(this.components).length === 0) {\n throw new Error('No components found!')\n }\n }\n\n private async loadImageUrls() {\n // eslint-disable-next-line no-console\n console.log('Getting export urls')\n\n const { data } = await this.client.fileImages(this.fileId, {\n format: this.exportFormat,\n ids: Object.keys(this.components),\n scale: 1,\n ...(this.exportFormat == 'pdf' ? { use_absolute_bounds: true } : {}),\n })\n\n for (const [id, image] of Object.entries(data.images)) {\n this.components[id].image = image\n }\n }\n\n private async downloadImages(outputDir: string) {\n return concurrently(\n Object.values(this.components).map((component) => async () => {\n if (component.image === undefined) {\n return\n }\n\n const response = await got.get(\n component.image,\n this.exportFormat == 'pdf' ? { responseType: 'buffer' } : {}\n )\n\n const filename = `${filenamify(component.name)}.${this.exportFormat}`\n const fullname = path.join(outputDir, filename)\n const dirname = path.dirname(fullname)\n\n await ensureDir(dirname)\n\n // eslint-disable-next-line no-console\n console.log(`found: ${filename} => ✅ writing...`)\n await writeFile(fullname, response.body, 'utf8')\n })\n )\n }\n\n private async getFile() {\n // eslint-disable-next-line no-console\n console.log('Processing response')\n\n const { data } = await this.client.file(this.fileId.toString())\n\n return data\n }\n\n private findComponentsRecursively(child: Figma.Node) {\n if (child.type === 'COMPONENT') {\n const { name, id } = child\n\n if (isIconNode(child)) {\n this.components[id] = {\n name,\n id,\n }\n }\n } else if ('children' in child) {\n child.children.forEach((grandChild) =>\n this.findComponentsRecursively(grandChild)\n )\n }\n }\n}\n","import PQueue from 'p-queue'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function concurrently(tasks: (() => Promise<any>)[]) {\n const queue = new PQueue({ concurrency: 3 })\n for (const task of tasks) {\n void queue.add(task)\n }\n queue.start()\n return queue.onIdle()\n}\n","import { Octokit } from '@octokit/rest'\nimport path from 'path'\nimport { getChangedFiles } from './getChangedFiles'\n\ntype RefResponse = ReturnType<GithubClient['createBranch']> extends Promise<\n infer R\n>\n ? R\n : never\n\ninterface TreeItem {\n path?: string\n mode?: '100644' | '100755' | '040000' | '160000' | '120000'\n type?: 'blob' | 'commit' | 'tree'\n sha?: string | null\n content?: string\n}\n\nexport class GithubClient {\n private readonly api: Octokit\n private readonly now: Date\n\n static async runFromCli(\n repoOwner: string,\n repoName: string,\n token: string,\n defaultBranch: string,\n outputDir: string\n ) {\n const client = new this(repoOwner, repoName, token, defaultBranch)\n const outputDirFullPath = path.resolve(process.cwd(), outputDir)\n const diff = await client.createTreeFromDiff(outputDirFullPath)\n // eslint-disable-next-line no-console\n console.log(`${diff.length} files are changed`)\n if (diff.length === 0) {\n // eslint-disable-next-line no-console\n console.log('no changes. aborting')\n return\n }\n\n const newBranch = await client.createBranch()\n\n await client.createCommit(diff, newBranch)\n return client.createPullRequest(newBranch)\n }\n\n constructor(\n private readonly repoOwner: string,\n private readonly repoName: string,\n token: string,\n private readonly defaultBranch: string,\n now = new Date()\n ) {\n this.api = new Octokit({\n auth: token,\n })\n\n this.now = now\n }\n\n get branch() {\n return `icons/update/${this.now.getTime()}`\n }\n\n /**\n * both used for commit message or pull request title\n */\n get message() {\n return `[icons-cli] Update icons ${this.now.toDateString()}`\n }\n\n async createTreeFromDiff(outputDir: string): Promise<TreeItem[]> {\n const tree: TreeItem[] = []\n\n for await (const file of getChangedFiles(outputDir)) {\n const item = {\n path: file.relativePath,\n // 100 はファイル 644 は実行不可なファイルであるという意味\n // @see https://octokit.github.io/rest.js/v18#git-create-tree\n mode: '100644' as const,\n content: file.content,\n }\n\n if (file.status === 'deleted') {\n // https://stackoverflow.com/questions/23637961/how-do-i-mark-a-file-as-deleted-in-a-tree-using-the-github-api\n tree.push({\n ...item,\n sha: null,\n })\n } else {\n tree.push(item)\n }\n }\n\n return tree\n }\n\n async createCommit(tree: TreeItem[], targetBranch: RefResponse) {\n const parentCommit = await this.api.git.getCommit({\n owner: this.repoOwner,\n repo: this.repoName,\n\n commit_sha: targetBranch.data.object.sha,\n })\n\n const newTree = await this.api.git.createTree({\n owner: this.repoOwner,\n repo: this.repoName,\n\n base_tree: parentCommit.data.tree.sha,\n tree,\n })\n\n // この時点ではどのブランチにも属さないコミットができる\n const commit = await this.api.git.createCommit({\n owner: this.repoOwner,\n repo: this.repoName,\n message: this.message,\n tree: newTree.data.sha,\n parents: [parentCommit.data.sha],\n })\n\n // ref を更新することで、commit が targetBranch に属するようになる\n await this.api.git.updateRef({\n owner: this.repoOwner,\n repo: this.repoName,\n ref: `heads/${this.branch}`,\n sha: commit.data.sha,\n })\n\n return commit\n }\n\n async createPullRequest(targetBranch: RefResponse) {\n const defaultBranch = await this.getDefaultBranchRef()\n\n return this.api.pulls.create({\n owner: this.repoOwner,\n repo: this.repoName,\n head: targetBranch.data.ref,\n base: defaultBranch.data.ref,\n title: this.message,\n body: '',\n })\n }\n\n private getDefaultBranchRef() {\n return this.api.git.getRef({\n owner: this.repoOwner,\n repo: this.repoName,\n ref: `heads/${this.defaultBranch}`,\n })\n }\n\n async createBranch() {\n const defaultBranch = await this.getDefaultBranchRef()\n\n return this.api.git.createRef({\n owner: this.repoOwner,\n repo: this.repoName,\n ref: `refs/heads/${this.branch}`,\n sha: defaultBranch.data.object.sha,\n })\n }\n}\n","import { promises as fs, existsSync } from 'fs'\nimport path from 'path'\nimport { execp } from './utils'\n\n/**\n * dir 内で変更があったファイル情報を for await で回せるようにするやつ\n */\nexport async function* getChangedFiles(dir: string) {\n if (!existsSync(dir))\n throw new Error(`icons-cli: target directory not found (${dir})`)\n const gitStatus = await collectGitStatus()\n for (const [relativePath, status] of gitStatus) {\n const fullpath = path.resolve(process.cwd(), '../../', relativePath)\n if (!fullpath.startsWith(`${dir}/`)) {\n continue\n }\n if (!existsSync(fullpath))\n throw new Error(`icons-cli: could not load svg (${fullpath})`)\n const content = await fs.readFile(fullpath, { encoding: 'utf-8' })\n yield { relativePath, content, status }\n }\n}\n\nasync function collectGitStatus() {\n return new Map(\n /**\n * @see https://git-scm.com/docs/git-status#_porcelain_format_version_1\n */\n (await execp(`git status --porcelain`)).split('\\n').map((s) => {\n return [\n s.slice(3),\n s.startsWith(' M')\n ? 'modified'\n : s.startsWith('??')\n ? 'untracked'\n : s.startsWith(' D')\n ? 'deleted'\n : null,\n ] as const\n })\n )\n}\n","import { exec } from 'child_process'\n\n/**\n * FIXME: util.promisify を使うと node-libs-browser に入っている方が使われてしまい、壊れる\n */\nexport const execp = (command: string) =>\n new Promise<string>((resolve, reject) => {\n exec(command, (err, stdout) => {\n if (err) {\n return reject(err)\n }\n\n return resolve(stdout)\n })\n })\n\nexport function mustBeDefined<T>(\n value: T,\n name: string\n): asserts value is NonNullable<T> {\n if (typeof value === 'undefined') {\n throw new TypeError(`${name} must be defined.`)\n }\n}\n","import type { CommitAction } from '@gitbeaker/core/dist/types/services/Commits'\nimport { Gitlab } from '@gitbeaker/node'\nimport path from 'path'\nimport { getChangedFiles } from './getChangedFiles'\n\ntype GitlabApi = InstanceType<typeof Gitlab>\n\nexport class GitlabClient {\n private readonly api: GitlabApi\n private readonly now: Date\n\n static async runFromCli(\n host: string,\n projectId: number,\n privateToken: string,\n defaultBranch: string,\n outputDir: string\n ) {\n const client = new this(host, projectId, privateToken, defaultBranch)\n const outputDirFullPath = path.resolve(process.cwd(), outputDir)\n const diff = await client.createActionsFromDiff(outputDirFullPath)\n // eslint-disable-next-line no-console\n console.log(`${diff.length} files are changed`)\n if (diff.length === 0) {\n // eslint-disable-next-line no-console\n console.log('no changes. aborting')\n return\n }\n\n await client.createCommit(diff)\n return client.createMergeRequest()\n }\n\n constructor(\n private readonly host: string,\n private readonly projectId: number,\n privateToken: string,\n private readonly defaultBranch: string,\n now = new Date()\n ) {\n this.api = new Gitlab({\n host: this.host,\n token: privateToken,\n })\n this.now = now\n }\n\n get branch() {\n return `icons/update/${this.now.getTime()}`\n }\n\n /**\n * both used for commit message or merge request title\n */\n get message() {\n return `[icons-cli] Update icons ${this.now.toDateString()}`\n }\n\n async createActionsFromDiff(outputDir: string): Promise<CommitAction[]> {\n const actions: CommitAction[] = []\n\n for await (const file of getChangedFiles(outputDir)) {\n actions.push({\n action:\n file.status === 'untracked'\n ? 'create'\n : file.status === 'deleted'\n ? 'delete'\n : 'update',\n filePath: file.relativePath,\n content: file.content,\n })\n }\n\n return actions\n }\n\n async createCommit(diff: CommitAction[]) {\n return this.api.Commits.create(\n this.projectId,\n this.branch,\n this.message,\n diff,\n {\n start_branch: this.defaultBranch,\n }\n )\n }\n\n createMergeRequest() {\n return this.api.MergeRequests.create(\n this.projectId,\n this.branch,\n this.defaultBranch,\n this.message\n )\n }\n}\n","import { JSDOM } from 'jsdom'\nimport { parseToRgb } from 'polished'\nimport type { RgbColor, RgbaColor } from 'polished/lib/types/color'\nimport Svgo from 'svgo'\n\nexport const DEFAULT_CURRENT_COLOR_TARGET = '#858585'\n\nconst svgo = new Svgo({\n plugins: [\n // NOTICE: SVGO は「svg 内のすべての fill を currentColor に変える」機能しかない\n // icons-cli に必要なのは「特定の黒っぽい色だけ currentColor に変える」機能\n // なので、convertColors plugin は使わない\n // { convertColors: { currentColor: true } },\n { removeViewBox: false },\n { removeAttrs: { attrs: ['stroke-opacity', 'fill-opacity'] } },\n ],\n})\n\n/**\n * SVGを最適化するオプション\n */\ninterface Options {\n /**\n * currentColorに置換する色 #ffffff\n */\n convertedColor: string\n /**\n * svgoによる最適化を行わない\n */\n withoutOptimizeBySVGO?: boolean\n}\n\nexport async function optimizeSvg(input: string, options: Options) {\n const { document } = new JSDOM(input).window\n const svg = document.querySelector('svg')\n if (!svg) {\n throw new Error('optimizeSvg: input string seems not to have <svg>')\n }\n\n addViewboxToRootSvg(svg)\n convertToCurrentColor(svg, options.convertedColor)\n\n if (options.withoutOptimizeBySVGO === true) {\n return svg.outerHTML\n } else {\n return (await svgo.optimize(svg.outerHTML)).data\n }\n}\n\nconst TARGET_ATTRS = ['fill', 'stroke']\n\nfunction convertToCurrentColor(svg: SVGSVGElement, convertedColor: string) {\n const targetColor = parseColor(convertedColor)\n if (!targetColor) {\n throw new Error(`${convertedColor} is not a valid color`)\n }\n\n for (const attr of TARGET_ATTRS) {\n const targets = Array.from(svg.querySelectorAll<SVGElement>(`[${attr}]`))\n\n for (const el of targets) {\n const value = parseColor(el.getAttribute(attr))\n if (!value) {\n continue\n }\n\n if (!colorEquals(value, targetColor)) {\n continue\n }\n\n el.setAttribute(attr, 'currentColor')\n }\n }\n}\n\nfunction parseColor(value: string | null) {\n if (value == null) {\n return null\n }\n\n try {\n return parseToRgb(value)\n } catch {\n return null\n }\n}\n\nfunction colorEquals(self: RgbColor | RgbaColor, other: RgbColor | RgbaColor) {\n if (self.red !== other.red) {\n return false\n }\n\n if (self.blue !== other.blue) {\n return false\n }\n\n if (self.green !== other.green) {\n return false\n }\n\n if ('alpha' in self) {\n if ('alpha' in other) {\n if (self.alpha !== other.alpha) {\n return false\n }\n }\n }\n\n return true\n}\n\nfunction addViewboxToRootSvg(svg: SVGSVGElement) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const width = svg.getAttribute('width')!\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const height = svg.getAttribute('height')!\n\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`)\n}\n","import path from 'path'\nimport glob from 'fast-glob'\nimport fs from 'fs-extra'\nimport { concurrently } from '../concurrently'\nimport { optimizeSvg } from './optimizeSvg'\n\n/* eslint-disable no-console */\n\nexport const optimizeSvgInDirectory = async (\n outputDir: string,\n replaceColor: string,\n ignoreFile?: string\n) => {\n const rootDir = path.join(outputDir, 'svg')\n\n const ignorePatterns =\n ignoreFile !== undefined\n ? (await fs.readFile(ignoreFile, 'utf8')).trim().split(/\\r?\\n/u)\n : []\n\n const files = await glob('**/*.svg', {\n cwd: rootDir,\n })\n\n await concurrently(\n files.map((file) => async () => {\n console.log(`Optimizing ${file}...`)\n const fullPath = path.join(rootDir, file)\n\n const originalSvg = await fs.readFile(fullPath, 'utf8')\n const optimizedSvg = await optimizeSvg(originalSvg, {\n convertedColor: replaceColor,\n withoutOptimizeBySVGO: ignorePatterns.includes(file),\n })\n await fs.writeFile(fullPath, optimizedSvg)\n })\n )\n}\n","import path from 'path'\nimport glob from 'fast-glob'\nimport fs from 'fs-extra'\n\nconst generateIconSvgEmbeddedSource = (svgString: string) => {\n const str = svgString.replace(/\\r?\\n/g, '')\n\n return `/** This file is auto generated. DO NOT EDIT BY HAND. */\nexport default '${str}'\n`\n}\n\nconst generateMjsEntrypoint = (\n icons: string[]\n) => `/** This file is auto generated. DO NOT EDIT BY HAND. */\n\nexport default {\n${icons\n .map((it) => ` '${it}': () => import('./${it}.js').then(m => m.default)`)\n .join(',\\n')}\n}\n`\n\nconst generateCjsEntrypoint = (\n icons: string[]\n) => `/** This file is auto generated. DO NOT EDIT BY HAND. */\n\nmodule.exports = {\n${icons\n .map((it) => ` '${it}': () => import('./${it}.js').then(m => m.default)`)\n .join(',\\n')}\n}\n`\n\nconst generateTypeDefinitionEntrypoint = (\n icons: string[]\n) => `/** This file is auto generated. DO NOt EDIT BY HAND. */\n\ndeclare var _default: {\n${icons.map((it) => ` '${it}': () => Promise<string>`).join(';\\n')}\n};\nexport default _default;\n`\n\nexport const generateEntrypoint = async (\n outputDir: string,\n icons: string[]\n) => {\n const srcRoot = path.join(outputDir, 'src')\n const mjsPath = path.join(srcRoot, 'index.js')\n await fs.ensureFile(mjsPath)\n await fs.writeFile(mjsPath, generateMjsEntrypoint(icons))\n\n const cjsPath = path.join(srcRoot, 'index.cjs')\n await fs.ensureFile(cjsPath)\n await fs.writeFile(cjsPath, generateCjsEntrypoint(icons))\n\n const dtsPath = path.join(srcRoot, 'index.d.ts')\n await fs.ensureFile(dtsPath)\n await fs.writeFile(dtsPath, generateTypeDefinitionEntrypoint(icons))\n}\n\nexport const generateIconSource = async (outputDir: string) => {\n const svgRoot = path.join(outputDir, 'svg')\n const srcRoot = path.join(outputDir, 'src')\n const icons = (await glob('**/*.svg', { cwd: svgRoot }))\n .map(\n (path) => path.slice(0, -4) // e.g. '16/Add.svg' -> '16/Add'\n )\n .sort()\n\n for (const it of icons) {\n const data = await fs.readFile(path.join(svgRoot, `${it}.svg`))\n const outputPath = path.join(srcRoot, `${it}.js`)\n await fs.ensureFile(outputPath)\n await fs.writeFile(\n outputPath,\n generateIconSvgEmbeddedSource(data.toString())\n )\n }\n\n await generateEntrypoint(outputDir, icons)\n}\n"],"mappings":"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","names":["PQueue","camelCase","path","got","import_path","import_path","path","fs","path","import_path","path","Svgo","import_path","import_fs_extra","path","fs","glob","import_path","import_fast_glob","import_fs_extra","path","fs","glob","yargs"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/figma/FigmaFileClient.ts","../src/concurrently.ts","../src/GitHubClient.ts","../src/getChangedFiles.ts","../src/utils.ts","../src/GitlabClient.ts","../src/svg/optimizeSvg.ts","../src/svg/optimizeSvgInDirectory.ts","../src/generateSource.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport yargs from 'yargs'\nimport { FigmaFileClient } from './figma/FigmaFileClient'\nimport { GithubClient } from './GitHubClient'\nimport { GitlabClient } from './GitlabClient'\nimport { DEFAULT_CURRENT_COLOR_TARGET } from './svg/optimizeSvg'\nimport { optimizeSvgInDirectory } from './svg/optimizeSvgInDirectory'\nimport { generateIconSource } from './generateSource'\nimport { mustBeDefined } from './utils'\n\n/**\n * Figma\n */\nconst FIGMA_TOKEN = process.env.FIGMA_TOKEN\nconst FIGMA_FILE_URL = process.env.FIGMA_FILE_URL\nconst OUTPUT_ROOT_DIR = process.env.OUTPUT_ROOT_DIR\n\n/**\n * GitLab\n */\nconst GITLAB_ACCESS_TOKEN = process.env.GITLAB_ACCESS_TOKEN\nconst GITLAB_DEFAULT_BRANCH = process.env.GITLAB_DEFAULT_BRANCH\nconst GITLAB_HOST = process.env.GITLAB_HOST\nconst GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID\n\n/**\n * GitHub\n */\nconst GITHUB_ACCESS_TOKEN = process.env.GITHUB_ACCESS_TOKEN\nconst GITHUB_REPO_OWNER = process.env.GITHUB_REPO_OWNER\nconst GITHUB_REPO_NAME = process.env.GITHUB_REPO_NAME\nconst GITHUB_DEFAULT_BRANCH = process.env.GITHUB_DEFAULT_BRANCH\n\nvoid yargs\n .scriptName('icons-cli')\n .command(\n 'figma:export',\n 'Load all icons from Figma and save to files',\n {\n format: {\n default: 'svg',\n choices: ['svg', 'pdf'],\n describe: 'Output format',\n },\n layout: {\n default: 'v1',\n describe: 'Figma icon file layout version',\n },\n },\n async ({ format, layout }) => {\n mustBeDefined(FIGMA_FILE_URL, 'FIGMA_FILE_URL')\n mustBeDefined(FIGMA_TOKEN, 'FIGMA_TOKEN')\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n await FigmaFileClient.runFromCli(\n FIGMA_FILE_URL,\n FIGMA_TOKEN,\n OUTPUT_ROOT_DIR,\n format as 'svg' | 'pdf',\n layout as 'v1' | 'v2'\n )\n }\n )\n .command(\n 'svg:optimize',\n 'Optimize svg files in output directory',\n {\n color: {\n default: DEFAULT_CURRENT_COLOR_TARGET,\n type: 'string',\n describe: 'Color code that should be converted into `currentColor`',\n },\n ignoreFile: {\n type: 'string',\n describe:\n 'A file that contains the list of path to SVG files that should not be optimized',\n },\n },\n async ({ color, ignoreFile }) => {\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n await optimizeSvgInDirectory(OUTPUT_ROOT_DIR, color, ignoreFile)\n }\n )\n .command(\n 'files:generate',\n 'Enumerate svg files in output directory and generate icon files',\n {},\n () => {\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n void generateIconSource(OUTPUT_ROOT_DIR).catch((e) => {\n // eslint-disable-next-line no-console\n console.error(e)\n process.exit(1)\n })\n }\n )\n .command(\n 'gitlab:mr',\n 'Create a merge request in the name of icons-cli',\n {},\n async () => {\n mustBeDefined(GITLAB_PROJECT_ID, 'GITLAB_PROJECT_ID')\n mustBeDefined(GITLAB_ACCESS_TOKEN, 'GITLAB_ACCESS_TOKEN')\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n await GitlabClient.runFromCli(\n GITLAB_HOST ?? 'https://gitlab.com',\n Number(GITLAB_PROJECT_ID),\n GITLAB_ACCESS_TOKEN,\n GITLAB_DEFAULT_BRANCH ?? 'main',\n OUTPUT_ROOT_DIR\n )\n }\n )\n .command(\n 'github:pr',\n 'Create a pull request in the name of icons-cli',\n {},\n async () => {\n mustBeDefined(GITHUB_ACCESS_TOKEN, 'GITHUB_ACCESS_TOKEN')\n mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')\n\n await GithubClient.runFromCli(\n GITHUB_REPO_OWNER ?? 'pixiv',\n GITHUB_REPO_NAME ?? 'charcoal',\n GITHUB_ACCESS_TOKEN,\n GITHUB_DEFAULT_BRANCH ?? 'main',\n OUTPUT_ROOT_DIR\n )\n }\n )\n .demandCommand()\n .strict()\n .help()\n .parse()\n","/* eslint-disable no-console */\nimport path from 'path'\nimport camelCase from 'camelcase'\nimport * as Figma from 'figma-js'\nimport { ensureDir, remove, writeFile } from 'fs-extra'\nimport got from 'got'\nimport { match } from 'path-to-regexp'\nimport { concurrently } from '../concurrently'\n\nconst DRY_RUN = Boolean(process.env.DRY_RUN)\n\nconst matchPath = match<{ fileId: string; name: string }>('/file/:fileId/:name')\n\nfunction extractParams(url: string): { fileId: string; nodeId?: string } {\n const { pathname, searchParams } = new URL(url)\n\n const result = matchPath(pathname)\n if (result === false) {\n throw new Error('No fileId found in url')\n }\n\n return {\n fileId: result.params.fileId,\n nodeId: searchParams.get('node-id') ?? undefined,\n }\n}\n\nfunction filenamify(name: string) {\n return camelCase(name, { pascalCase: true }).replace(' ', '')\n}\n\nconst iconName = /^(?:\\d+|Inline)\\s*\\//u\n\nfunction isIconNode(node: Figma.Node) {\n return iconName.test(node.name)\n}\n\nfunction parseV2IconName(name: string) {\n return name\n .split(',')\n .map((f) => f.split('=').map((s) => s.trim())[1])\n .join('/')\n .toLowerCase()\n}\n\ntype ExportFormat = 'svg' | 'pdf'\n\ninterface Component {\n id: string\n name: string\n image?: string\n variant?: string\n}\n\ntype FigmaIconFileLayoutVersion = 'v1' | 'v2'\n\nexport class FigmaFileClient {\n private readonly fileId: string\n private readonly nodeId?: string\n private readonly exportFormat: ExportFormat\n private readonly client: Figma.ClientInterface\n private readonly layoutVersion: FigmaIconFileLayoutVersion\n\n private components: Record<string, Component> = {}\n\n static async runFromCli(\n url: string,\n token: string,\n outputRootDir: string,\n exportFormat: ExportFormat,\n layoutVersion: FigmaIconFileLayoutVersion = 'v1'\n ) {\n const client = new this(url, token, exportFormat, layoutVersion)\n\n const outputDir = path.join(process.cwd(), outputRootDir, exportFormat)\n\n console.log(\n `Exporting components from ${url} using layout ${layoutVersion}`\n )\n await client.loadSvg(outputDir)\n\n console.log('success!')\n }\n\n constructor(\n url: string,\n personalAccessToken: string,\n exportFormat: ExportFormat,\n layoutVersion: FigmaIconFileLayoutVersion\n ) {\n this.client = Figma.Client({\n personalAccessToken,\n })\n\n const { fileId, nodeId } = extractParams(url)\n this.fileId = fileId\n this.nodeId = nodeId\n\n this.exportFormat = exportFormat\n this.layoutVersion = layoutVersion\n }\n\n async loadSvg(outputDir: string) {\n await remove(outputDir)\n await ensureDir(outputDir)\n\n await this.loadComponents()\n await this.loadImageUrls()\n await this.downloadImages(outputDir)\n }\n\n private async loadComponents() {\n const { document } = await this.getFile()\n // const { document } = (\n // await import('../../../../blob-report/scripts/doc.json')\n // ).default as Figma.FileResponse\n\n if (this.layoutVersion === 'v2') {\n this.findComponentsV2(document)\n } else {\n // nodeIdが指定されている場合は、IDが一致するノードのみを探索対象にする\n // 指定されていない場合はドキュメント全体が探索対象\n const targets =\n this.nodeId !== undefined\n ? document.children.filter((node) => node.id === this.nodeId)\n : document.children\n\n // 対象ノードの子孫を探索してアイコンのコンポーネントを見つける\n targets.forEach((child) => this.findComponentsRecursively(child))\n }\n\n const len = Object.keys(this.components).length\n if (len === 0) {\n throw new Error('No components found!')\n } else {\n console.log(`found ${len} icons`)\n }\n }\n\n private async loadImageUrls() {\n console.log('Getting export urls')\n\n const { data } = await this.client.fileImages(this.fileId, {\n format: this.exportFormat,\n ids: Object.keys(this.components),\n scale: 1,\n ...(this.exportFormat == 'pdf' ? { use_absolute_bounds: true } : {}),\n })\n\n for (const [id, image] of Object.entries(data.images)) {\n this.components[id].image = image\n }\n }\n\n private async downloadImages(outputDir: string) {\n return concurrently(\n Object.values(this.components).map((component) => async () => {\n if (component.image === undefined) {\n return\n }\n\n const filename = component.variant\n ? `${parseV2IconName(component.variant)}/${component.name}.${\n this.exportFormat\n }`\n : `${filenamify(component.name)}.${this.exportFormat}`\n const fullname = path.join(outputDir, filename)\n const dirname = path.dirname(fullname)\n\n if (DRY_RUN) {\n console.log(`[DRY_RUN] skip: ${filename} => ✅ writing...`)\n return\n }\n\n const response = await got.get(\n component.image,\n this.exportFormat == 'pdf' ? { responseType: 'buffer' } : {}\n )\n\n await ensureDir(dirname)\n\n console.log(`found: ${filename} => ✅ writing...`)\n await writeFile(fullname, response.body, 'utf8')\n })\n )\n }\n\n private async getFile() {\n console.log('Processing response')\n\n const { data } = await this.client.file(this.fileId.toString())\n\n return data\n }\n\n private findComponentsRecursively(child: Figma.Node) {\n if (child.type === 'COMPONENT') {\n const { name, id } = child\n\n if (isIconNode(child)) {\n this.components[id] = {\n name,\n id,\n }\n }\n } else if ('children' in child) {\n child.children.forEach((grandChild) =>\n this.findComponentsRecursively(grandChild)\n )\n }\n }\n\n private findComponentsV2(document: Figma.Document) {\n const iconsPage = document.children.find(\n (child): child is Figma.Canvas =>\n child.name === 'Icons 一覧' && child.type === 'CANVAS'\n )\n const iconComponentSets = iconsPage?.children.flatMap((c) => {\n if (c.type !== 'FRAME') return []\n return c.children.filter(\n (c): c is Figma.ComponentSet => c.type === 'COMPONENT_SET'\n )\n })\n iconComponentSets?.forEach((set) => {\n set.children.forEach((i) => {\n if (i.type !== 'COMPONENT') return null\n this.components[i.id] = {\n name: set.name,\n variant: i.name,\n id: i.id,\n }\n })\n })\n }\n}\n","import PQueue from 'p-queue'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function concurrently(tasks: (() => Promise<any>)[]) {\n const queue = new PQueue({ concurrency: 3 })\n for (const task of tasks) {\n void queue.add(task)\n }\n queue.start()\n return queue.onIdle()\n}\n","import { Octokit } from '@octokit/rest'\nimport path from 'path'\nimport { getChangedFiles } from './getChangedFiles'\n\ntype RefResponse = ReturnType<GithubClient['createBranch']> extends Promise<\n infer R\n>\n ? R\n : never\n\ninterface TreeItem {\n path?: string\n mode?: '100644' | '100755' | '040000' | '160000' | '120000'\n type?: 'blob' | 'commit' | 'tree'\n sha?: string | null\n content?: string\n}\n\nexport class GithubClient {\n private readonly api: Octokit\n private readonly now: Date\n\n static async runFromCli(\n repoOwner: string,\n repoName: string,\n token: string,\n defaultBranch: string,\n outputDir: string\n ) {\n const client = new this(repoOwner, repoName, token, defaultBranch)\n const outputDirFullPath = path.resolve(process.cwd(), outputDir)\n const diff = await client.createTreeFromDiff(outputDirFullPath)\n // eslint-disable-next-line no-console\n console.log(`${diff.length} files are changed`)\n if (diff.length === 0) {\n // eslint-disable-next-line no-console\n console.log('no changes. aborting')\n return\n }\n\n const newBranch = await client.createBranch()\n\n await client.createCommit(diff, newBranch)\n return client.createPullRequest(newBranch)\n }\n\n constructor(\n private readonly repoOwner: string,\n private readonly repoName: string,\n token: string,\n private readonly defaultBranch: string,\n now = new Date()\n ) {\n this.api = new Octokit({\n auth: token,\n })\n\n this.now = now\n }\n\n get branch() {\n return `icons/update/${this.now.getTime()}`\n }\n\n /**\n * both used for commit message or pull request title\n */\n get message() {\n return `[icons-cli] Update icons ${this.now.toDateString()}`\n }\n\n async createTreeFromDiff(outputDir: string): Promise<TreeItem[]> {\n const tree: TreeItem[] = []\n\n for await (const file of getChangedFiles(outputDir)) {\n const item = {\n path: file.relativePath,\n // 100 はファイル 644 は実行不可なファイルであるという意味\n // @see https://octokit.github.io/rest.js/v18#git-create-tree\n mode: '100644' as const,\n content: file.content,\n }\n\n if (file.status === 'deleted') {\n // https://stackoverflow.com/questions/23637961/how-do-i-mark-a-file-as-deleted-in-a-tree-using-the-github-api\n tree.push({\n ...item,\n sha: null,\n })\n } else {\n tree.push(item)\n }\n }\n\n return tree\n }\n\n async createCommit(tree: TreeItem[], targetBranch: RefResponse) {\n const parentCommit = await this.api.git.getCommit({\n owner: this.repoOwner,\n repo: this.repoName,\n\n commit_sha: targetBranch.data.object.sha,\n })\n\n const newTree = await this.api.git.createTree({\n owner: this.repoOwner,\n repo: this.repoName,\n\n base_tree: parentCommit.data.tree.sha,\n tree,\n })\n\n // この時点ではどのブランチにも属さないコミットができる\n const commit = await this.api.git.createCommit({\n owner: this.repoOwner,\n repo: this.repoName,\n message: this.message,\n tree: newTree.data.sha,\n parents: [parentCommit.data.sha],\n })\n\n // ref を更新することで、commit が targetBranch に属するようになる\n await this.api.git.updateRef({\n owner: this.repoOwner,\n repo: this.repoName,\n ref: `heads/${this.branch}`,\n sha: commit.data.sha,\n })\n\n return commit\n }\n\n async createPullRequest(targetBranch: RefResponse) {\n const defaultBranch = await this.getDefaultBranchRef()\n\n return this.api.pulls.create({\n owner: this.repoOwner,\n repo: this.repoName,\n head: targetBranch.data.ref,\n base: defaultBranch.data.ref,\n title: this.message,\n body: '',\n })\n }\n\n private getDefaultBranchRef() {\n return this.api.git.getRef({\n owner: this.repoOwner,\n repo: this.repoName,\n ref: `heads/${this.defaultBranch}`,\n })\n }\n\n async createBranch() {\n const defaultBranch = await this.getDefaultBranchRef()\n\n return this.api.git.createRef({\n owner: this.repoOwner,\n repo: this.repoName,\n ref: `refs/heads/${this.branch}`,\n sha: defaultBranch.data.object.sha,\n })\n }\n}\n","import { promises as fs, existsSync } from 'fs'\nimport path from 'path'\nimport { execp } from './utils'\n\n/**\n * dir 内で変更があったファイル情報を for await で回せるようにするやつ\n */\nexport async function* getChangedFiles(dir: string) {\n if (!existsSync(dir))\n throw new Error(`icons-cli: target directory not found (${dir})`)\n const gitStatus = await collectGitStatus()\n for (const [relativePath, status] of gitStatus) {\n const fullpath = path.resolve(process.cwd(), '../../', relativePath)\n if (!fullpath.startsWith(`${dir}/`)) {\n continue\n }\n if (!existsSync(fullpath))\n throw new Error(`icons-cli: could not load svg (${fullpath})`)\n const content = await fs.readFile(fullpath, { encoding: 'utf-8' })\n yield { relativePath, content, status }\n }\n}\n\nasync function collectGitStatus() {\n return new Map(\n /**\n * @see https://git-scm.com/docs/git-status#_porcelain_format_version_1\n */\n (await execp(`git status --porcelain`)).split('\\n').map((s) => {\n return [\n s.slice(3),\n s.startsWith(' M')\n ? 'modified'\n : s.startsWith('??')\n ? 'untracked'\n : s.startsWith(' D')\n ? 'deleted'\n : null,\n ] as const\n })\n )\n}\n","import { exec } from 'child_process'\n\n/**\n * FIXME: util.promisify を使うと node-libs-browser に入っている方が使われてしまい、壊れる\n */\nexport const execp = (command: string) =>\n new Promise<string>((resolve, reject) => {\n exec(command, (err, stdout) => {\n if (err) {\n return reject(err)\n }\n\n return resolve(stdout)\n })\n })\n\nexport function mustBeDefined<T>(\n value: T,\n name: string\n): asserts value is NonNullable<T> {\n if (typeof value === 'undefined') {\n throw new TypeError(`${name} must be defined.`)\n }\n}\n","import type { CommitAction } from '@gitbeaker/core/dist/types/services/Commits'\nimport { Gitlab } from '@gitbeaker/node'\nimport path from 'path'\nimport { getChangedFiles } from './getChangedFiles'\n\ntype GitlabApi = InstanceType<typeof Gitlab>\n\nexport class GitlabClient {\n private readonly api: GitlabApi\n private readonly now: Date\n\n static async runFromCli(\n host: string,\n projectId: number,\n privateToken: string,\n defaultBranch: string,\n outputDir: string\n ) {\n const client = new this(host, projectId, privateToken, defaultBranch)\n const outputDirFullPath = path.resolve(process.cwd(), outputDir)\n const diff = await client.createActionsFromDiff(outputDirFullPath)\n // eslint-disable-next-line no-console\n console.log(`${diff.length} files are changed`)\n if (diff.length === 0) {\n // eslint-disable-next-line no-console\n console.log('no changes. aborting')\n return\n }\n\n await client.createCommit(diff)\n return client.createMergeRequest()\n }\n\n constructor(\n private readonly host: string,\n private readonly projectId: number,\n privateToken: string,\n private readonly defaultBranch: string,\n now = new Date()\n ) {\n this.api = new Gitlab({\n host: this.host,\n token: privateToken,\n })\n this.now = now\n }\n\n get branch() {\n return `icons/update/${this.now.getTime()}`\n }\n\n /**\n * both used for commit message or merge request title\n */\n get message() {\n return `[icons-cli] Update icons ${this.now.toDateString()}`\n }\n\n async createActionsFromDiff(outputDir: string): Promise<CommitAction[]> {\n const actions: CommitAction[] = []\n\n for await (const file of getChangedFiles(outputDir)) {\n actions.push({\n action:\n file.status === 'untracked'\n ? 'create'\n : file.status === 'deleted'\n ? 'delete'\n : 'update',\n filePath: file.relativePath,\n content: file.content,\n })\n }\n\n return actions\n }\n\n async createCommit(diff: CommitAction[]) {\n return this.api.Commits.create(\n this.projectId,\n this.branch,\n this.message,\n diff,\n {\n start_branch: this.defaultBranch,\n }\n )\n }\n\n createMergeRequest() {\n return this.api.MergeRequests.create(\n this.projectId,\n this.branch,\n this.defaultBranch,\n this.message\n )\n }\n}\n","import { JSDOM } from 'jsdom'\nimport { parseToRgb } from 'polished'\nimport type { RgbColor, RgbaColor } from 'polished/lib/types/color'\nimport Svgo from 'svgo'\n\nexport const DEFAULT_CURRENT_COLOR_TARGET = '#858585'\n\nconst svgo = new Svgo({\n plugins: [\n // NOTICE: SVGO は「svg 内のすべての fill を currentColor に変える」機能しかない\n // icons-cli に必要なのは「特定の黒っぽい色だけ currentColor に変える」機能\n // なので、convertColors plugin は使わない\n // { convertColors: { currentColor: true } },\n { removeViewBox: false },\n { removeAttrs: { attrs: ['stroke-opacity', 'fill-opacity'] } },\n ],\n})\n\n/**\n * SVGを最適化するオプション\n */\ninterface Options {\n /**\n * currentColorに置換する色 #ffffff\n */\n convertedColor: string\n /**\n * svgoによる最適化を行わない\n */\n withoutOptimizeBySVGO?: boolean\n}\n\nexport async function optimizeSvg(input: string, options: Options) {\n const { document } = new JSDOM(input).window\n const svg = document.querySelector('svg')\n if (!svg) {\n throw new Error('optimizeSvg: input string seems not to have <svg>')\n }\n\n addViewboxToRootSvg(svg)\n convertToCurrentColor(svg, options.convertedColor)\n\n if (options.withoutOptimizeBySVGO === true) {\n return svg.outerHTML\n } else {\n return (await svgo.optimize(svg.outerHTML)).data\n }\n}\n\nconst TARGET_ATTRS = ['fill', 'stroke']\n\nfunction convertToCurrentColor(svg: SVGSVGElement, convertedColor: string) {\n const targetColor = parseColor(convertedColor)\n if (!targetColor) {\n throw new Error(`${convertedColor} is not a valid color`)\n }\n\n for (const attr of TARGET_ATTRS) {\n const targets = Array.from(svg.querySelectorAll<SVGElement>(`[${attr}]`))\n\n for (const el of targets) {\n const value = parseColor(el.getAttribute(attr))\n if (!value) {\n continue\n }\n\n if (!colorEquals(value, targetColor)) {\n continue\n }\n\n el.setAttribute(attr, 'currentColor')\n }\n }\n}\n\nfunction parseColor(value: string | null) {\n if (value == null) {\n return null\n }\n\n try {\n return parseToRgb(value)\n } catch {\n return null\n }\n}\n\nfunction colorEquals(self: RgbColor | RgbaColor, other: RgbColor | RgbaColor) {\n if (self.red !== other.red) {\n return false\n }\n\n if (self.blue !== other.blue) {\n return false\n }\n\n if (self.green !== other.green) {\n return false\n }\n\n if ('alpha' in self) {\n if ('alpha' in other) {\n if (self.alpha !== other.alpha) {\n return false\n }\n }\n }\n\n return true\n}\n\nfunction addViewboxToRootSvg(svg: SVGSVGElement) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const width = svg.getAttribute('width')!\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const height = svg.getAttribute('height')!\n\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`)\n}\n","import path from 'path'\nimport glob from 'fast-glob'\nimport fs from 'fs-extra'\nimport { concurrently } from '../concurrently'\nimport { optimizeSvg } from './optimizeSvg'\n\n/* eslint-disable no-console */\n\nexport const optimizeSvgInDirectory = async (\n outputDir: string,\n replaceColor: string,\n ignoreFile?: string\n) => {\n const rootDir = path.join(outputDir, 'svg')\n\n const ignorePatterns =\n ignoreFile !== undefined\n ? (await fs.readFile(ignoreFile, 'utf8')).trim().split(/\\r?\\n/u)\n : []\n\n const files = await glob('**/*.svg', {\n cwd: rootDir,\n })\n\n await concurrently(\n files.map((file) => async () => {\n console.log(`Optimizing ${file}...`)\n const fullPath = path.join(rootDir, file)\n\n const originalSvg = await fs.readFile(fullPath, 'utf8')\n const optimizedSvg = await optimizeSvg(originalSvg, {\n convertedColor: replaceColor,\n withoutOptimizeBySVGO: ignorePatterns.includes(file),\n })\n await fs.writeFile(fullPath, optimizedSvg)\n })\n )\n}\n","import path from 'path'\nimport glob from 'fast-glob'\nimport fs from 'fs-extra'\n\nconst generateIconSvgEmbeddedSource = (svgString: string) => {\n const str = svgString.replace(/\\r?\\n/g, '')\n\n return `/** This file is auto generated. DO NOT EDIT BY HAND. */\nexport default '${str}'\n`\n}\n\nconst generateMjsEntrypoint = (\n icons: string[]\n) => `/** This file is auto generated. DO NOT EDIT BY HAND. */\n\nexport default {\n${icons\n .map((it) => ` '${it}': () => import('./${it}.js').then(m => m.default)`)\n .join(',\\n')}\n}\n`\n\nconst generateCjsEntrypoint = (\n icons: string[]\n) => `/** This file is auto generated. DO NOT EDIT BY HAND. */\n\nmodule.exports = {\n${icons\n .map((it) => ` '${it}': () => import('./${it}.js').then(m => m.default)`)\n .join(',\\n')}\n}\n`\n\nconst generateTypeDefinitionEntrypoint = (\n icons: string[]\n) => `/** This file is auto generated. DO NOt EDIT BY HAND. */\n\ndeclare var _default: {\n${icons.map((it) => ` '${it}': () => Promise<string>`).join(';\\n')}\n};\nexport default _default;\n`\n\nexport const generateEntrypoint = async (\n outputDir: string,\n icons: string[]\n) => {\n const srcRoot = path.join(outputDir, 'src')\n const mjsPath = path.join(srcRoot, 'index.js')\n await fs.ensureFile(mjsPath)\n await fs.writeFile(mjsPath, generateMjsEntrypoint(icons))\n\n const cjsPath = path.join(srcRoot, 'index.cjs')\n await fs.ensureFile(cjsPath)\n await fs.writeFile(cjsPath, generateCjsEntrypoint(icons))\n\n const dtsPath = path.join(srcRoot, 'index.d.ts')\n await fs.ensureFile(dtsPath)\n await fs.writeFile(dtsPath, generateTypeDefinitionEntrypoint(icons))\n}\n\nexport const generateIconSource = async (outputDir: string) => {\n const svgRoot = path.join(outputDir, 'svg')\n const srcRoot = path.join(outputDir, 'src')\n const icons = (await glob('**/*.svg', { cwd: svgRoot }))\n .map(\n (path) => path.slice(0, -4) // e.g. '16/Add.svg' -> '16/Add'\n )\n .sort()\n\n for (const it of icons) {\n const data = await fs.readFile(path.join(svgRoot, `${it}.svg`))\n const outputPath = path.join(srcRoot, `${it}.js`)\n await fs.ensureFile(outputPath)\n await fs.writeFile(\n outputPath,\n generateIconSvgEmbeddedSource(data.toString())\n )\n }\n\n await generateEntrypoint(outputDir, icons)\n}\n"],"mappings":"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","names":["PQueue","camelCase","path","got","c","import_path","import_path","path","fs","path","import_path","path","Svgo","import_path","import_fs_extra","path","fs","glob","import_path","import_fast_glob","import_fs_extra","path","fs","glob","yargs"]}
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@charcoal-ui/icons-cli",
3
- "version": "4.7.2",
3
+ "version": "4.8.1-beta.0",
4
4
  "license": "Apache-2.0",
5
5
  "bin": "./dist/index.js",
6
6
  "devDependencies": {
7
7
  "@gitbeaker/core": "^25.6.0",
8
+ "@svgr/cli": "^8.1.0",
8
9
  "@types/fs-extra": "^9.0.13",
9
10
  "@types/jsdom": "^16.2.14",
10
11
  "@types/parse5": "^6.0.3",
@@ -12,6 +13,7 @@
12
13
  "@types/yargs": "^17.0.8",
13
14
  "rimraf": "^3.0.2",
14
15
  "tsup": "^6.5.0",
16
+ "tsx": "^4.19.1",
15
17
  "typescript": "^4.9.5"
16
18
  },
17
19
  "dependencies": {
@@ -44,8 +46,11 @@
44
46
  "gitHead": "e1ece2e43901ae667afdd5c178040607d939dcd5",
45
47
  "scripts": {
46
48
  "build": "FORCE_COLOR=1 tsup-node",
49
+ "build:react": "pnpm svgr ../icon-files/v2/svg/ --out-dir ../icons/src/react/v2 --typescript --ref --no-index --no-svgo && OUTPUT_ROOT_DIR=../icons/src/react/v2 pnpm tsx src/v2/transformReact.ts",
50
+ "build:css": "OUTPUT_ROOT_DIR=../icons/css/v2 pnpm tsx src/v2/transformCSS.ts",
47
51
  "typecheck": "tsc --project tsconfig.build.json --pretty --noEmit",
48
52
  "clean": "rimraf dist .tsbuildinfo",
49
- "cli:icons": "./dist/index.js"
53
+ "cli:icons": "./dist/index.js",
54
+ "icons": "tsx src/index.ts"
50
55
  }
51
56
  }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  import path from 'path'
2
3
  import camelCase from 'camelcase'
3
4
  import * as Figma from 'figma-js'
@@ -6,6 +7,8 @@ import got from 'got'
6
7
  import { match } from 'path-to-regexp'
7
8
  import { concurrently } from '../concurrently'
8
9
 
10
+ const DRY_RUN = Boolean(process.env.DRY_RUN)
11
+
9
12
  const matchPath = match<{ fileId: string; name: string }>('/file/:fileId/:name')
10
13
 
11
14
  function extractParams(url: string): { fileId: string; nodeId?: string } {
@@ -32,19 +35,31 @@ function isIconNode(node: Figma.Node) {
32
35
  return iconName.test(node.name)
33
36
  }
34
37
 
38
+ function parseV2IconName(name: string) {
39
+ return name
40
+ .split(',')
41
+ .map((f) => f.split('=').map((s) => s.trim())[1])
42
+ .join('/')
43
+ .toLowerCase()
44
+ }
45
+
35
46
  type ExportFormat = 'svg' | 'pdf'
36
47
 
37
48
  interface Component {
38
49
  id: string
39
50
  name: string
40
51
  image?: string
52
+ variant?: string
41
53
  }
42
54
 
55
+ type FigmaIconFileLayoutVersion = 'v1' | 'v2'
56
+
43
57
  export class FigmaFileClient {
44
58
  private readonly fileId: string
45
59
  private readonly nodeId?: string
46
60
  private readonly exportFormat: ExportFormat
47
61
  private readonly client: Figma.ClientInterface
62
+ private readonly layoutVersion: FigmaIconFileLayoutVersion
48
63
 
49
64
  private components: Record<string, Component> = {}
50
65
 
@@ -52,24 +67,26 @@ export class FigmaFileClient {
52
67
  url: string,
53
68
  token: string,
54
69
  outputRootDir: string,
55
- exportFormat: ExportFormat
70
+ exportFormat: ExportFormat,
71
+ layoutVersion: FigmaIconFileLayoutVersion = 'v1'
56
72
  ) {
57
- const client = new this(url, token, exportFormat)
73
+ const client = new this(url, token, exportFormat, layoutVersion)
58
74
 
59
75
  const outputDir = path.join(process.cwd(), outputRootDir, exportFormat)
60
76
 
61
- // eslint-disable-next-line no-console
62
- console.log(`Exporting components from ${url}`)
77
+ console.log(
78
+ `Exporting components from ${url} using layout ${layoutVersion}`
79
+ )
63
80
  await client.loadSvg(outputDir)
64
81
 
65
- // eslint-disable-next-line no-console
66
82
  console.log('success!')
67
83
  }
68
84
 
69
85
  constructor(
70
86
  url: string,
71
87
  personalAccessToken: string,
72
- exportFormat: ExportFormat
88
+ exportFormat: ExportFormat,
89
+ layoutVersion: FigmaIconFileLayoutVersion
73
90
  ) {
74
91
  this.client = Figma.Client({
75
92
  personalAccessToken,
@@ -80,6 +97,7 @@ export class FigmaFileClient {
80
97
  this.nodeId = nodeId
81
98
 
82
99
  this.exportFormat = exportFormat
100
+ this.layoutVersion = layoutVersion
83
101
  }
84
102
 
85
103
  async loadSvg(outputDir: string) {
@@ -93,24 +111,33 @@ export class FigmaFileClient {
93
111
 
94
112
  private async loadComponents() {
95
113
  const { document } = await this.getFile()
114
+ // const { document } = (
115
+ // await import('../../../../blob-report/scripts/doc.json')
116
+ // ).default as Figma.FileResponse
117
+
118
+ if (this.layoutVersion === 'v2') {
119
+ this.findComponentsV2(document)
120
+ } else {
121
+ // nodeIdが指定されている場合は、IDが一致するノードのみを探索対象にする
122
+ // 指定されていない場合はドキュメント全体が探索対象
123
+ const targets =
124
+ this.nodeId !== undefined
125
+ ? document.children.filter((node) => node.id === this.nodeId)
126
+ : document.children
127
+
128
+ // 対象ノードの子孫を探索してアイコンのコンポーネントを見つける
129
+ targets.forEach((child) => this.findComponentsRecursively(child))
130
+ }
96
131
 
97
- // nodeIdが指定されている場合は、IDが一致するノードのみを探索対象にする
98
- // 指定されていない場合はドキュメント全体が探索対象
99
- const targets =
100
- this.nodeId !== undefined
101
- ? document.children.filter((node) => node.id === this.nodeId)
102
- : document.children
103
-
104
- // 対象ノードの子孫を探索してアイコンのコンポーネントを見つける
105
- targets.forEach((child) => this.findComponentsRecursively(child))
106
-
107
- if (Object.keys(this.components).length === 0) {
132
+ const len = Object.keys(this.components).length
133
+ if (len === 0) {
108
134
  throw new Error('No components found!')
135
+ } else {
136
+ console.log(`found ${len} icons`)
109
137
  }
110
138
  }
111
139
 
112
140
  private async loadImageUrls() {
113
- // eslint-disable-next-line no-console
114
141
  console.log('Getting export urls')
115
142
 
116
143
  const { data } = await this.client.fileImages(this.fileId, {
@@ -132,18 +159,26 @@ export class FigmaFileClient {
132
159
  return
133
160
  }
134
161
 
162
+ const filename = component.variant
163
+ ? `${parseV2IconName(component.variant)}/${component.name}.${
164
+ this.exportFormat
165
+ }`
166
+ : `${filenamify(component.name)}.${this.exportFormat}`
167
+ const fullname = path.join(outputDir, filename)
168
+ const dirname = path.dirname(fullname)
169
+
170
+ if (DRY_RUN) {
171
+ console.log(`[DRY_RUN] skip: ${filename} => ✅ writing...`)
172
+ return
173
+ }
174
+
135
175
  const response = await got.get(
136
176
  component.image,
137
177
  this.exportFormat == 'pdf' ? { responseType: 'buffer' } : {}
138
178
  )
139
179
 
140
- const filename = `${filenamify(component.name)}.${this.exportFormat}`
141
- const fullname = path.join(outputDir, filename)
142
- const dirname = path.dirname(fullname)
143
-
144
180
  await ensureDir(dirname)
145
181
 
146
- // eslint-disable-next-line no-console
147
182
  console.log(`found: ${filename} => ✅ writing...`)
148
183
  await writeFile(fullname, response.body, 'utf8')
149
184
  })
@@ -151,7 +186,6 @@ export class FigmaFileClient {
151
186
  }
152
187
 
153
188
  private async getFile() {
154
- // eslint-disable-next-line no-console
155
189
  console.log('Processing response')
156
190
 
157
191
  const { data } = await this.client.file(this.fileId.toString())
@@ -175,4 +209,27 @@ export class FigmaFileClient {
175
209
  )
176
210
  }
177
211
  }
212
+
213
+ private findComponentsV2(document: Figma.Document) {
214
+ const iconsPage = document.children.find(
215
+ (child): child is Figma.Canvas =>
216
+ child.name === 'Icons 一覧' && child.type === 'CANVAS'
217
+ )
218
+ const iconComponentSets = iconsPage?.children.flatMap((c) => {
219
+ if (c.type !== 'FRAME') return []
220
+ return c.children.filter(
221
+ (c): c is Figma.ComponentSet => c.type === 'COMPONENT_SET'
222
+ )
223
+ })
224
+ iconComponentSets?.forEach((set) => {
225
+ set.children.forEach((i) => {
226
+ if (i.type !== 'COMPONENT') return null
227
+ this.components[i.id] = {
228
+ name: set.name,
229
+ variant: i.name,
230
+ id: i.id,
231
+ }
232
+ })
233
+ })
234
+ }
178
235
  }
package/src/index.ts CHANGED
@@ -43,8 +43,12 @@ void yargs
43
43
  choices: ['svg', 'pdf'],
44
44
  describe: 'Output format',
45
45
  },
46
+ layout: {
47
+ default: 'v1',
48
+ describe: 'Figma icon file layout version',
49
+ },
46
50
  },
47
- async ({ format }) => {
51
+ async ({ format, layout }) => {
48
52
  mustBeDefined(FIGMA_FILE_URL, 'FIGMA_FILE_URL')
49
53
  mustBeDefined(FIGMA_TOKEN, 'FIGMA_TOKEN')
50
54
  mustBeDefined(OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')
@@ -53,7 +57,8 @@ void yargs
53
57
  FIGMA_FILE_URL,
54
58
  FIGMA_TOKEN,
55
59
  OUTPUT_ROOT_DIR,
56
- format as 'svg' | 'pdf'
60
+ format as 'svg' | 'pdf',
61
+ layout as 'v1' | 'v2'
57
62
  )
58
63
  }
59
64
  )
@@ -0,0 +1,145 @@
1
+ import { mustBeDefined } from '../utils'
2
+ import glob from 'fast-glob'
3
+ import { readFile, writeFile, ensureDir } from 'fs-extra'
4
+ import path from 'path'
5
+ import { escape } from 'querystring'
6
+
7
+ async function main() {
8
+ mustBeDefined(process.env.OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')
9
+ const outDir = process.env.OUTPUT_ROOT_DIR
10
+ const inputDir = path.join(__dirname, '../../../icon-files/v2/svg')
11
+ await ensureDir(outDir)
12
+ const fileNames = await glob('**/*.svg', { cwd: inputDir })
13
+
14
+ let classNames: string[] = []
15
+ const filesWithContent = await Promise.all(
16
+ fileNames.map(async (fileName) => {
17
+ const filePath = path.join(inputDir, fileName)
18
+ const content = await readFile(filePath, 'utf-8')
19
+ const [size, variant, name] = fileName.split('/')
20
+ const cssName = [
21
+ 'charcoal-icon',
22
+ name.replace('.svg', '').replaceAll('.', '-'),
23
+ ...(variant === 'regular' ? [] : [variant]),
24
+ ...(size === '24' ? [] : [size]),
25
+ ].join('-')
26
+ classNames.push(cssName)
27
+ const css = content.includes('<def')
28
+ ? `
29
+ .${cssName} {
30
+ display: inline-block;
31
+ width: 1em;
32
+ height: 1em;
33
+ background: url('data:image/svg+xml;utf8,${escape(content).replace(
34
+ "'",
35
+ "\\'"
36
+ )}');
37
+ aspect-ratio: 1/1;
38
+ }`
39
+ : `
40
+ .${cssName} {
41
+ display: inline-block;
42
+ width: 1em;
43
+ height: 1em;
44
+ mask-image: url('data:image/svg+xml;utf8,${escape(content).replace(
45
+ "'",
46
+ "\\'"
47
+ )}');
48
+ mask-size: 100% 100%;
49
+ background: currentColor;
50
+ aspect-ratio: 1/1;
51
+ }
52
+ `
53
+
54
+ return {
55
+ filePath,
56
+ css,
57
+ cssName,
58
+ }
59
+ })
60
+ )
61
+
62
+ const cssContent = filesWithContent
63
+ .sort((a, b) => a.cssName.localeCompare(b.cssName))
64
+ .map((icon) => icon.css)
65
+ .join('\n')
66
+ await writeFile(path.join(outDir, 'index.css'), cssContent)
67
+ classNames = classNames.sort()
68
+ const html = `
69
+ <div class="icons">
70
+ ${classNames
71
+ .map(
72
+ (icon) => `
73
+ <div>
74
+ <i class="${icon}" title=".${icon}"></i>
75
+ <code>.${icon}</code>
76
+ </div>
77
+ `
78
+ )
79
+ .join('\n')}
80
+ </div>`
81
+
82
+ await writeFile(
83
+ path.join(outDir, 'index.html'),
84
+ `<link rel="stylesheet" href="./index.css"><style>
85
+ :root {
86
+ font-size: 24px;
87
+ }
88
+ .icons {
89
+ display: grid;
90
+ gap: 8px;
91
+ grid-template-columns: repeat(auto-fill, 300px);
92
+ }
93
+ .icons div {
94
+ display: inline-flex;
95
+ gap: 8px;
96
+ align-items: center;
97
+ }
98
+ code {
99
+ font-size: 14px;
100
+ }
101
+ </style>${html}`
102
+ )
103
+ await writeFile(
104
+ path.join(outDir, 'index.story.tsx'),
105
+ `export default {
106
+ title: 'Icons/v2/css',
107
+ parameters: {
108
+ storyshots: {
109
+ disable: true,
110
+ },
111
+ },
112
+ render() {
113
+ return (
114
+ <>
115
+ <style>{\`${cssContent}\`}</style>
116
+ <style>
117
+ {\`:root {
118
+ font-size: 24px;
119
+ }
120
+ .icons {
121
+ display: grid;
122
+ gap: 8px;
123
+ grid-template-columns: repeat(auto-fill, 300px);
124
+ }
125
+ .icons div {
126
+ display: inline-flex;
127
+ gap: 8px;
128
+ align-items: center;
129
+ }
130
+ code {
131
+ font-size: 14px;
132
+ }\`}
133
+ </style>
134
+ ${html.replaceAll('class', 'className')}
135
+ </>
136
+ )
137
+ },
138
+ }
139
+
140
+ export const Default = {}
141
+ `
142
+ )
143
+ }
144
+
145
+ void main()
@@ -0,0 +1,180 @@
1
+ import { mustBeDefined } from '../utils'
2
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
+ import ts from 'typescript'
4
+ import glob from 'fast-glob'
5
+ import { readFile, writeFile } from 'fs-extra'
6
+ import path from 'path'
7
+
8
+ async function main() {
9
+ mustBeDefined(process.env.OUTPUT_ROOT_DIR, 'OUTPUT_ROOT_DIR')
10
+ const workDir = process.env.OUTPUT_ROOT_DIR
11
+
12
+ const fileNames = await glob('*/**/*.tsx', { cwd: workDir })
13
+ const filesWithContent = await Promise.all(
14
+ fileNames.map(async (fileName) => {
15
+ const filePath = path.join(workDir, fileName)
16
+ const content = await readFile(filePath, 'utf-8')
17
+ return {
18
+ filePath,
19
+ fileName,
20
+ content,
21
+ }
22
+ })
23
+ )
24
+ const { transformed, catalog } = rewrite(filesWithContent)
25
+ await Promise.all(
26
+ transformed.map(async (file) => {
27
+ return writeFile(file.filePath, file.content)
28
+ })
29
+ )
30
+ const icons = Object.entries(catalog)
31
+
32
+ const iconsPair = icons.map(([iconName, iconPath]) => [
33
+ iconName,
34
+ './' + iconPath.replace('.tsx', ''),
35
+ ])
36
+
37
+ await writeFile(
38
+ path.join(workDir, 'index.tsx'),
39
+ `${iconsPair
40
+ .map(([iconName, iconPath]) => `export {${iconName}} from '${iconPath}'`)
41
+ .join('\n')}`
42
+ )
43
+
44
+ await writeFile(
45
+ path.join(workDir, 'index.story.tsx'),
46
+ `${iconsPair
47
+ .map(([iconName, iconPath]) => `import {${iconName}} from '${iconPath}'`)
48
+ .join('\n')}
49
+ import { createGlobalStyle } from 'styled-components'
50
+
51
+ export default {
52
+ title: 'Icons/v2/react',
53
+ parameters: {
54
+ storyshots: {
55
+ disable: true,
56
+ },
57
+ },
58
+ render() {
59
+ return (
60
+ <>
61
+ <div className="icons-grid">
62
+ ${icons
63
+ .sort((a, b) => a[0].localeCompare(b[0]))
64
+ .map(
65
+ ([iconName]) => `
66
+ <div>
67
+ <${iconName} />
68
+ <code>${`&lt;${iconName} /&gt;`}</code>
69
+ </div>`
70
+ )
71
+ .join('\n')}
72
+ </div>
73
+ <Global />
74
+ </>
75
+ )
76
+ },
77
+ }
78
+
79
+ export const Default = {}
80
+
81
+ const Global = createGlobalStyle\`
82
+ .icons-grid {
83
+ display: grid;
84
+ grid-template-columns: repeat(auto-fill, 300px);
85
+ font-size: 14px;
86
+ gap: 8px;
87
+ }
88
+ .icons-grid div {
89
+ overflow-wrap: anywhere;
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 8px;
93
+ }
94
+ \`
95
+ `
96
+ )
97
+ }
98
+ interface FileWithContent {
99
+ filePath: string
100
+ fileName: string
101
+ content: string
102
+ }
103
+
104
+ type Catalog = Record<string, string>
105
+
106
+ function rewrite(tsxSourceTexts: FileWithContent[]): {
107
+ catalog: Catalog
108
+ transformed: FileWithContent[]
109
+ } {
110
+ const catalog: Catalog = {}
111
+
112
+ const updateJSX: ts.TransformerFactory<ts.SourceFile> = (ctx) => (source) => {
113
+ const [name, variant, size] = source.fileName
114
+ .split('.')[0]
115
+ .split('/')
116
+ .reverse()
117
+ const newName = [
118
+ 'Icon',
119
+ name,
120
+ variant === 'regular'
121
+ ? []
122
+ : [variant.charAt(0).toUpperCase() + variant.slice(1)],
123
+ size === '24' ? [] : [size],
124
+ ].join('')
125
+ catalog[newName] = source.fileName
126
+ const visitor: ts.Visitor = (node) => {
127
+ if (ts.isIdentifier(node)) {
128
+ return transformIdent(node)
129
+ }
130
+ if (ts.isVariableStatement(node)) {
131
+ if (
132
+ node.declarationList.declarations.at(0)?.name.getText(source) ===
133
+ 'ForwardRef'
134
+ ) {
135
+ return ts.visitEachChild(
136
+ ctx.factory.updateVariableStatement(
137
+ node,
138
+ [ctx.factory.createToken(ts.SyntaxKind.ExportKeyword)],
139
+ node.declarationList
140
+ ),
141
+ visitor,
142
+ ctx
143
+ )
144
+ }
145
+ }
146
+ return ts.visitEachChild(node, visitor, ctx)
147
+ }
148
+
149
+ function transformIdent(node: ts.Identifier) {
150
+ if (node.text === 'ForwardRef') {
151
+ return ts.factory.createIdentifier(newName)
152
+ }
153
+ return node
154
+ }
155
+
156
+ return ts.visitNode(source, visitor)
157
+ }
158
+
159
+ const printer = ts.createPrinter()
160
+
161
+ const sources = tsxSourceTexts.map((sourceText) => {
162
+ return ts.createSourceFile(
163
+ sourceText.fileName,
164
+ sourceText.content,
165
+ ts.ScriptTarget.ESNext,
166
+ undefined,
167
+ ts.ScriptKind.TSX
168
+ )
169
+ })
170
+ const { transformed } = ts.transform(sources, [updateJSX])
171
+ return {
172
+ catalog,
173
+ transformed: transformed.map((t, i) => {
174
+ const printed = printer.printFile(t)
175
+ return { ...tsxSourceTexts[i], content: printed }
176
+ }),
177
+ }
178
+ }
179
+
180
+ void main()