@newly/cli 0.2.201 → 0.2.202
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/commands/support.js +67 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/commands/support.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { ApiClient, friendlyApiError } from '../api.js';
|
|
4
4
|
import { requireCredentials, resolveProject } from '../config.js';
|
|
5
5
|
import { SUPPORT_CHAT_TSX } from './supportChat.generated.js';
|
|
6
6
|
export const SUPPORT_CONFIG = 'src/lib/newly-support.json';
|
|
7
|
+
export const SUPPORT_ARTICLES_DIR = 'support/articles';
|
|
7
8
|
export const SUPPORT_CHAT = 'src/components/SupportChat.tsx';
|
|
8
9
|
export function writeSupportChat(dir, force = false) {
|
|
9
10
|
const path = join(dir, SUPPORT_CHAT);
|
|
@@ -104,6 +105,64 @@ export async function runArticlesList(client, projectId, log) {
|
|
|
104
105
|
for (const a of articles)
|
|
105
106
|
log(`${a.id} ${a.title} (updated ${a.updated_at})`);
|
|
106
107
|
}
|
|
108
|
+
const slugOf = (title) => title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 60).replace(/-+$/, '') || 'article';
|
|
109
|
+
const INDEX = 'articles.json';
|
|
110
|
+
function exportedFiles(dir) {
|
|
111
|
+
try {
|
|
112
|
+
const { articles } = JSON.parse(readFileSync(join(dir, INDEX), 'utf8'));
|
|
113
|
+
return (articles ?? []).map((a) => a.file).filter((f) => typeof f === 'string' && /^[a-z0-9-]+\.md$/.test(f));
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export const WEBSITE_ARTICLES = 'support-articles.json';
|
|
120
|
+
export async function runArticlesExport(client, projectId, dir, log, websiteDir) {
|
|
121
|
+
const { settings } = (await client.get(`${base(projectId)}/settings`));
|
|
122
|
+
const websiteCopy = websiteDir ? join(websiteDir, WEBSITE_ARTICLES) : null;
|
|
123
|
+
if (!settings?.enabled) {
|
|
124
|
+
if (websiteCopy && existsSync(websiteCopy))
|
|
125
|
+
unlinkSync(websiteCopy);
|
|
126
|
+
log('Nothing exported: the help chat is off for this app. Turn it on with newly support enable first.');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const { articles } = (await client.get(`${base(projectId)}/articles`));
|
|
130
|
+
const sorted = [...articles].sort((a, b) => a.title.localeCompare(b.title) || a.id.localeCompare(b.id));
|
|
131
|
+
const owned = new Set(exportedFiles(dir));
|
|
132
|
+
const taken = new Set();
|
|
133
|
+
const free = (file) => !taken.has(file) && (owned.has(file) || !existsSync(join(dir, file)));
|
|
134
|
+
const entries = sorted.map((a) => {
|
|
135
|
+
const slug = slugOf(a.title);
|
|
136
|
+
let file = `${slug}.md`;
|
|
137
|
+
for (let n = 2; !free(file); n++)
|
|
138
|
+
file = `${slug}-${n}.md`;
|
|
139
|
+
taken.add(file);
|
|
140
|
+
return {
|
|
141
|
+
id: a.id, file, title: a.title, body: a.body, source: a.source ?? 'manual',
|
|
142
|
+
source_name: a.source_name ?? null, source_url: a.source_url ?? null, updated_at: a.updated_at,
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
mkdirSync(dir, { recursive: true });
|
|
146
|
+
for (const stale of owned) {
|
|
147
|
+
if (!taken.has(stale) && existsSync(join(dir, stale)))
|
|
148
|
+
unlinkSync(join(dir, stale));
|
|
149
|
+
}
|
|
150
|
+
for (const e of entries) {
|
|
151
|
+
const header = [`id: ${e.id}`, `title: ${JSON.stringify(e.title)}`, `source: ${e.source}`];
|
|
152
|
+
if (e.source_name)
|
|
153
|
+
header.push(`source_name: ${JSON.stringify(e.source_name)}`);
|
|
154
|
+
if (e.source_url)
|
|
155
|
+
header.push(`source_url: ${JSON.stringify(e.source_url)}`);
|
|
156
|
+
header.push(`updated_at: ${e.updated_at}`);
|
|
157
|
+
writeFileSync(join(dir, e.file), `---\n${header.join('\n')}\n---\n\n${e.body.trimEnd()}\n`);
|
|
158
|
+
}
|
|
159
|
+
const note = 'Exported from Newly customer support. Edit articles in Newly; newly support articles export overwrites this folder.';
|
|
160
|
+
const index = `${JSON.stringify({ note, articles: entries }, null, 2)}\n`;
|
|
161
|
+
writeFileSync(join(dir, INDEX), index);
|
|
162
|
+
if (websiteDir && websiteCopy && existsSync(websiteDir))
|
|
163
|
+
writeFileSync(websiteCopy, index);
|
|
164
|
+
log(`Exported ${entries.length} ${entries.length === 1 ? 'article' : 'articles'} to ${dir}`);
|
|
165
|
+
}
|
|
107
166
|
const bodyOf = (opts) => {
|
|
108
167
|
if (opts.file)
|
|
109
168
|
return readFileSync(opts.file, 'utf8');
|
|
@@ -266,6 +325,12 @@ export function registerSupport(program) {
|
|
|
266
325
|
const c = ctx(o.project);
|
|
267
326
|
await runArticleImportUrl(c.client, c.projectId, url, Boolean(o.refresh), log);
|
|
268
327
|
}));
|
|
328
|
+
articles.command('export').description('Write the articles into the repo (markdown plus articles.json) for the website FAQ')
|
|
329
|
+
.option('--dir <dir>', 'output folder', SUPPORT_ARTICLES_DIR).option('--project <id>')
|
|
330
|
+
.action((o) => run(async () => {
|
|
331
|
+
const c = ctx(o.project);
|
|
332
|
+
await runArticlesExport(c.client, c.projectId, resolve(o.dir), log, resolve('website'));
|
|
333
|
+
}));
|
|
269
334
|
articles.command('remove <id>').option('--project <id>').action((id, o) => run(async () => {
|
|
270
335
|
const c = ctx(o.project);
|
|
271
336
|
await runArticleRemove(c.client, c.projectId, id, log);
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = '0.2.
|
|
1
|
+
export const CLI_VERSION = '0.2.202';
|