Skip to content

Commit 11e7061

Browse files
committed
Add config file system to change options
1 parent 51be1b4 commit 11e7061

File tree

8 files changed

+225
-8
lines changed

8 files changed

+225
-8
lines changed

README.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ Qiita CLI、Qiita Preview から記事の削除はできません。
167167

168168
- .gitignore
169169
- GitHub Actions のワークフローファイル
170+
- ユーザー設定ファイル(qiita.config.json)
170171

171172
が生成されます。
172173

@@ -206,19 +207,37 @@ npx qiita pull
206207

207208
### version
208209

209-
qiita-cli のバージョンを確認できます。
210+
Qiita CLI のバージョンを確認できます。
210211

211212
```console
212213
npx qiita version
213214
```
214215

216+
## ユーザー設定ファイルについて
217+
218+
`npx qiita init`コマンドで生成される`qiita.config.json`について説明します。
219+
このファイルを用いて、Qiita CLI の設定を行うことができます。
220+
設定できるオプションは以下の通りです。
221+
222+
- includePrivate: 限定共有記事を含めるかどうかを選べます。デフォルトは`false`です。
223+
215224
## オプション
216225

226+
### --credential \<credential_dir>
227+
228+
Qiita CLI の認証情報(`credentials.json`)を配置する・しているディレクトリを指定できます。
229+
デフォルトでは`$XDG_CONFIG_HOME/qiita-cli`もしくは`$HOME/.config/qiita-cli`になっています。
230+
231+
```console
232+
npx qiita login ---credential ./my_conf/
233+
npx qiita preview --credential ./my_conf/
234+
```
235+
217236
### --config \<config_dir>
218237

219-
qiita-cli の設定情報(`credentials.json`)を配置する・しているディレクトリを指定できます。
238+
Qiita CLI の設定情報(`qiita.config.json`)を配置する・しているディレクトリを指定できます。
220239

221-
デフォルトでは`$XDG_CONFIG_HOME/qiita-cli`もしくは`$HOME/.config/qiita-cli`になっています
240+
デフォルトでは、カレントディレクトリになります
222241

223242
例)
224243

src/commands/help.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@ COMMAND:
1313
help ヘルプを表示
1414
1515
OPTIONS:
16-
--config <config_dir>
17-
qiita-cliの設定情報を配置するディレクトリを指定
16+
--credential <credential_dir>
17+
qiita-cliの認証情報を配置するディレクトリを指定
1818
1919
--root <root_dir>
2020
記事ファイルをダウンロードするディレクトリを指定
2121
2222
--verbose
2323
詳細表示オプションを有効
2424
25+
--config
26+
設定ファイルを配置するディレクトリを指定
27+
2528
詳細についてはReadme(https://github.com/increments/qiita-cli)をご覧ください
2629
`;
2730

src/commands/init.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from "node:fs";
22
import path from "node:path";
3+
import { config } from "../lib/config";
34

45
export const init = async () => {
56
const rootDir = process.cwd();
@@ -48,6 +49,18 @@ jobs:
4849
node_modules
4950
`;
5051
writeFile(gitignoreFilePath, gitignoreFileContent);
52+
53+
const userConfigFilePath = config.getUserConfigFilePath();
54+
const userConfigDir = config.getUserConfigDir();
55+
if (!fs.existsSync(userConfigFilePath)) {
56+
fs.mkdirSync(userConfigDir, { recursive: true });
57+
}
58+
const userConfigFileContent = JSON.stringify(
59+
await config.getUserConfig(),
60+
null,
61+
2
62+
);
63+
writeFile(userConfigFilePath, userConfigFileContent);
5164
};
5265

5366
const writeFile = (path: string, content: string) => {

src/lib/config.test.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ jest.mock("node:fs/promises", () => {
5555
mkdir: jest.fn(() => {}),
5656
};
5757
});
58+
jest.mock("node:fs", () => {
59+
return {
60+
existsSync: jest.fn((filePath: string): boolean => {
61+
const text = getFile(filePath);
62+
return !!text;
63+
}),
64+
};
65+
});
5866

5967
describe("config", () => {
6068
describe("#getCredentialDir", () => {
@@ -111,6 +119,101 @@ describe("config", () => {
111119
});
112120
});
113121

122+
describe("#getUserConfigDir", () => {
123+
describe("paths", () => {
124+
beforeEach(() => {
125+
config.load({});
126+
});
127+
128+
it("returns default path", () => {
129+
expect(config.getUserConfigDir()).toEqual(
130+
"/home/test-user/qiita-articles"
131+
);
132+
});
133+
134+
describe("with options", () => {
135+
beforeEach(() => {
136+
config.load({
137+
userConfigDir: "my-root",
138+
});
139+
});
140+
141+
it("returns customized path", () => {
142+
expect(config.getUserConfigDir()).toEqual(
143+
"/home/test-user/qiita-articles/my-root"
144+
);
145+
});
146+
});
147+
});
148+
});
149+
150+
describe("#getUserConfigFilePath", () => {
151+
describe("paths", () => {
152+
beforeEach(() => {
153+
config.load({});
154+
});
155+
156+
it("returns default path", () => {
157+
expect(config.getUserConfigFilePath()).toEqual(
158+
"/home/test-user/qiita-articles/qiita.config.json"
159+
);
160+
});
161+
162+
describe("with options", () => {
163+
beforeEach(() => {
164+
config.load({
165+
userConfigDir: "my-root",
166+
});
167+
});
168+
169+
it("returns customized path", () => {
170+
expect(config.getUserConfigFilePath()).toEqual(
171+
"/home/test-user/qiita-articles/my-root/qiita.config.json"
172+
);
173+
});
174+
});
175+
});
176+
});
177+
178+
describe("#getUserConfig", () => {
179+
const userConfigFilePath =
180+
"/home/test-user/qiita-articles/qiita.config.json";
181+
182+
beforeEach(() => {
183+
config.load({});
184+
});
185+
186+
describe("when user config file already exists", () => {
187+
beforeEach(() => {
188+
const userConfigData = {
189+
includePrivate: true,
190+
};
191+
resetFiles();
192+
setFile(userConfigFilePath, JSON.stringify(userConfigData, null, 2));
193+
});
194+
195+
it("returns user config", async () => {
196+
const userConfig = await config.getUserConfig();
197+
expect(userConfig).toStrictEqual({
198+
includePrivate: true,
199+
});
200+
});
201+
});
202+
203+
describe("when user config file does not exist", () => {
204+
beforeEach(() => {
205+
resetFiles();
206+
});
207+
208+
it("returns default user config", async () => {
209+
const userConfig = await config.getUserConfig();
210+
expect(userConfig).toStrictEqual({
211+
includePrivate: false,
212+
});
213+
});
214+
});
215+
});
216+
114217
describe("#getCredential", () => {
115218
const credentialFilePath =
116219
"/home/test-user/.config/qiita-cli/credentials.json";

src/lib/config.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import fs from "node:fs/promises";
2+
import fsSync from "node:fs";
23
import os from "node:os";
34
import path from "node:path";
45
import process from "node:process";
@@ -8,18 +9,29 @@ interface Options {
89
credentialDir?: string;
910
profile?: string;
1011
itemsRootDir?: string;
12+
userConfigDir?: string;
1113
}
1214

15+
type UserConfig = {
16+
includePrivate: boolean;
17+
};
18+
1319
class Config {
1420
private credentialDir?: string;
1521
private itemsRootDir?: string;
22+
private userConfigFilePath?: string;
23+
private userConfigDir?: string;
1624
private credential?: Credential;
1725

1826
constructor() {}
1927

2028
load(options: Options) {
2129
this.credentialDir = this.resolveConfigDir(options.credentialDir);
2230
this.itemsRootDir = this.resolveItemsRootDir(options.itemsRootDir);
31+
this.userConfigDir = this.resolveUserConfigDirPath(options.userConfigDir);
32+
this.userConfigFilePath = this.resolveUserConfigFilePath(
33+
options.userConfigDir
34+
);
2335
this.credential = new Credential({
2436
credentialDir: this.credentialDir,
2537
profile: options.profile,
@@ -30,6 +42,7 @@ class Config {
3042
JSON.stringify({
3143
credentialDir: this.credentialDir,
3244
itemsRootDir: this.itemsRootDir,
45+
userConfigFilePath: this.userConfigFilePath,
3346
})
3447
);
3548
}
@@ -49,6 +62,20 @@ class Config {
4962
return this.itemsRootDir;
5063
}
5164

65+
getUserConfigDir() {
66+
if (!this.userConfigDir) {
67+
throw new Error("userConfigDir is undefined");
68+
}
69+
return this.userConfigDir;
70+
}
71+
72+
getUserConfigFilePath() {
73+
if (!this.userConfigFilePath) {
74+
throw new Error("userConfigFilePath is undefined");
75+
}
76+
return this.userConfigFilePath;
77+
}
78+
5279
getCredential() {
5380
if (!this.credential) {
5481
throw new Error("credential is undefined");
@@ -63,6 +90,24 @@ class Config {
6390
return this.credential.setCredential(credential);
6491
}
6592

93+
async getUserConfig() {
94+
const defaultConfig = {
95+
includePrivate: false,
96+
} as UserConfig;
97+
98+
if (fsSync.existsSync(this.userConfigFilePath as string)) {
99+
const userConfigFileData = await fs.readFile(
100+
this.userConfigFilePath as string
101+
);
102+
const userConfig = JSON.parse(
103+
userConfigFileData.toString()
104+
) as UserConfig;
105+
return { ...defaultConfig, ...userConfig };
106+
}
107+
108+
return defaultConfig;
109+
}
110+
66111
private resolveConfigDir(credentialDirPath?: string) {
67112
const packageName = "qiita-cli";
68113

@@ -89,6 +134,22 @@ class Config {
89134
return this.resolveFullPath(dirPath);
90135
}
91136

137+
private resolveUserConfigDirPath(dirPath?: string) {
138+
if (process.env.QIITA_CLI_USER_CONFIG_DIR) {
139+
return process.env.QIITA_CLI_USER_CONFIG_DIR;
140+
}
141+
if (!dirPath) {
142+
return process.cwd();
143+
}
144+
145+
return this.resolveFullPath(dirPath);
146+
}
147+
148+
private resolveUserConfigFilePath(dirPath?: string) {
149+
const filename = "qiita.config.json";
150+
return path.join(this.resolveUserConfigDirPath(dirPath), filename);
151+
}
152+
92153
private resolveFullPath(filePath: string) {
93154
if (path.isAbsolute(filePath)) {
94155
return filePath;

src/lib/sync-articles-from-qiita.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { QiitaApi } from "../qiita-api";
22
import type { FileSystemRepo } from "./file-system-repo";
3+
import { config } from "./config";
34

45
export const syncArticlesFromQiita = async ({
56
fileSystemRepo,
@@ -9,8 +10,13 @@ export const syncArticlesFromQiita = async ({
910
qiitaApi: QiitaApi;
1011
}) => {
1112
const per = 100;
13+
const userConfig = await config.getUserConfig();
1214
for (let page = 1; page <= 100; page += 1) {
13-
const items = await qiitaApi.authenticatedUserItems(page, per);
15+
const items = await qiitaApi.authenticatedUserItems(
16+
page,
17+
per,
18+
userConfig.includePrivate
19+
);
1420
if (items.length <= 0) {
1521
break;
1622
}

src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const args = arg(
1313
{
1414
"--credential": String,
1515
"--profile": String,
16+
"--config": String,
1617
"--root": String,
1718
"--verbose": Boolean,
1819
},
@@ -32,6 +33,7 @@ config.load({
3233
credentialDir: args["--credential"],
3334
profile: args["--profile"],
3435
itemsRootDir: args["--root"],
36+
userConfigDir: args["--config"],
3537
});
3638

3739
exec(commandName, commandArgs);

src/qiita-api/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,11 @@ export class QiitaApi {
130130
return await this.get<{ id: string }>("/api/v2/authenticated_user");
131131
}
132132

133-
async authenticatedUserItems(page?: number, per?: number) {
133+
async authenticatedUserItems(
134+
page?: number,
135+
per?: number,
136+
includePrivate: boolean = false
137+
) {
134138
const params = new URLSearchParams();
135139
if (page !== undefined) {
136140
params.set("page", page.toString());
@@ -141,7 +145,13 @@ export class QiitaApi {
141145

142146
const path = `/api/v2/authenticated_user/items?${params}`;
143147

144-
return await this.get<Item[]>(path);
148+
const items = await this.get<Item[]>(path);
149+
150+
if (!includePrivate) {
151+
return items.filter((item) => !item.private);
152+
}
153+
154+
return items;
145155
}
146156

147157
async preview(rawBody: string) {

0 commit comments

Comments
 (0)