@diplodoc/cli 4.0.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +785 -0
  2. package/LICENSE +21 -0
  3. package/README.md +62 -0
  4. package/README.ru.md +63 -0
  5. package/build/app.client.css +47 -0
  6. package/build/app.client.js +3 -0
  7. package/build/index.js +3993 -0
  8. package/build/index.js.map +7 -0
  9. package/build/lib.js +3374 -0
  10. package/build/lib.js.map +7 -0
  11. package/build/linter.js +1265 -0
  12. package/build/linter.js.map +7 -0
  13. package/package.json +126 -0
  14. package/src/cmd/build/index.ts +304 -0
  15. package/src/cmd/index.ts +4 -0
  16. package/src/cmd/publish/index.ts +92 -0
  17. package/src/cmd/publish/upload.ts +61 -0
  18. package/src/cmd/translate/index.ts +261 -0
  19. package/src/cmd/xliff/compose.ts +222 -0
  20. package/src/cmd/xliff/extract.ts +237 -0
  21. package/src/cmd/xliff/index.ts +27 -0
  22. package/src/constants.ts +122 -0
  23. package/src/globals.d.ts +1 -0
  24. package/src/index.ts +54 -0
  25. package/src/models.ts +249 -0
  26. package/src/packages/credentials/index.ts +1 -0
  27. package/src/packages/credentials/yandex-oauth.ts +42 -0
  28. package/src/resolvers/index.ts +3 -0
  29. package/src/resolvers/lintPage.ts +119 -0
  30. package/src/resolvers/md2html.ts +142 -0
  31. package/src/resolvers/md2md.ts +147 -0
  32. package/src/services/argv.ts +38 -0
  33. package/src/services/authors.ts +64 -0
  34. package/src/services/contributors.ts +104 -0
  35. package/src/services/includers/batteries/common.ts +34 -0
  36. package/src/services/includers/batteries/generic.ts +130 -0
  37. package/src/services/includers/batteries/index.ts +3 -0
  38. package/src/services/includers/batteries/sourcedocs.ts +33 -0
  39. package/src/services/includers/batteries/unarchive.ts +97 -0
  40. package/src/services/includers/index.ts +157 -0
  41. package/src/services/index.ts +6 -0
  42. package/src/services/leading.ts +88 -0
  43. package/src/services/metadata.ts +249 -0
  44. package/src/services/plugins.ts +76 -0
  45. package/src/services/preset.ts +55 -0
  46. package/src/services/tocs.ts +401 -0
  47. package/src/services/utils.ts +151 -0
  48. package/src/steps/index.ts +6 -0
  49. package/src/steps/processAssets.ts +36 -0
  50. package/src/steps/processExcludedFiles.ts +47 -0
  51. package/src/steps/processLinter.ts +100 -0
  52. package/src/steps/processLogs.ts +18 -0
  53. package/src/steps/processMapFile.ts +35 -0
  54. package/src/steps/processPages.ts +312 -0
  55. package/src/steps/processServiceFiles.ts +95 -0
  56. package/src/steps/publishFilesToS3.ts +47 -0
  57. package/src/utils/file.ts +17 -0
  58. package/src/utils/glob.ts +14 -0
  59. package/src/utils/index.ts +8 -0
  60. package/src/utils/logger.ts +42 -0
  61. package/src/utils/markup.ts +125 -0
  62. package/src/utils/path.ts +24 -0
  63. package/src/utils/presets.ts +20 -0
  64. package/src/utils/singlePage.ts +228 -0
  65. package/src/utils/toc.ts +87 -0
  66. package/src/utils/url.ts +3 -0
  67. package/src/utils/worker.ts +10 -0
  68. package/src/validator.ts +150 -0
  69. package/src/vcs-connector/client/github.ts +52 -0
  70. package/src/vcs-connector/connector-models.ts +76 -0
  71. package/src/vcs-connector/connector-validator.ts +114 -0
  72. package/src/vcs-connector/github.ts +333 -0
  73. package/src/vcs-connector/index.ts +15 -0
  74. package/src/workers/linter/index.ts +62 -0
@@ -0,0 +1,100 @@
1
+ import log from '@doc-tools/transform/lib/log';
2
+ import {spawn, Worker, Thread} from 'threads';
3
+ import {extname} from 'path';
4
+
5
+ import {ArgvService, TocService, PresetService, PluginService} from '../services';
6
+ import {ProcessLinterWorker} from '../workers/linter';
7
+ import {logger} from '../utils';
8
+ import {LINTING_FINISHED, WORKERS_COUNT, MIN_CHUNK_SIZE} from '../constants';
9
+ import {lintPage} from '../resolvers';
10
+ import {splitOnChunks} from '../utils/worker';
11
+
12
+ let processLinterWorkers: (ProcessLinterWorker & Thread)[];
13
+ let navigationPathsChunks: (string[])[];
14
+
15
+ export async function processLinter(): Promise<void> {
16
+ const argvConfig = ArgvService.getConfig();
17
+
18
+ const navigationPaths = TocService.getNavigationPaths();
19
+
20
+ if (!processLinterWorkers) {
21
+ lintPagesFallback(navigationPaths);
22
+
23
+ return;
24
+ }
25
+
26
+ const presetStorage = PresetService.getPresetStorage();
27
+
28
+ /* Subscribe on the linted page event */
29
+ processLinterWorkers.forEach((worker) => {
30
+ worker.getProcessedPages().subscribe((pathToFile) => {
31
+ logger.info(pathToFile as string, LINTING_FINISHED);
32
+ });
33
+ });
34
+
35
+ /* Run processing the linter */
36
+ await Promise.all(
37
+ processLinterWorkers.map((worker, i) => {
38
+ const navigationPathsChunk = navigationPathsChunks[i];
39
+
40
+ return worker.run({
41
+ argvConfig,
42
+ presetStorage,
43
+ navigationPaths: navigationPathsChunk,
44
+ });
45
+ }),
46
+ );
47
+
48
+ /* Unsubscribe from workers */
49
+ await Promise.all(
50
+ processLinterWorkers.map((worker) => {
51
+ return worker.finish().then((logs) => {
52
+ log.add(logs);
53
+ });
54
+ }),
55
+ );
56
+
57
+ /* Terminate workers */
58
+ await Promise.all(
59
+ processLinterWorkers.map((worker) => {
60
+ return Thread.terminate(worker);
61
+ }),
62
+ );
63
+ }
64
+
65
+ export async function initLinterWorkers() {
66
+ const navigationPaths = TocService.getNavigationPaths();
67
+ const chunkSize = getChunkSize(navigationPaths);
68
+
69
+ if (process.env.DISABLE_PARALLEL_BUILD || chunkSize < MIN_CHUNK_SIZE || WORKERS_COUNT <= 0) {
70
+ return;
71
+ }
72
+
73
+ navigationPathsChunks = splitOnChunks(navigationPaths, chunkSize)
74
+ .filter((arr) => arr.length);
75
+
76
+ const workersCount = navigationPathsChunks.length;
77
+
78
+ processLinterWorkers = await Promise.all((new Array(workersCount)).fill(null).map(() => {
79
+ // TODO: get linter path from env
80
+ return spawn<ProcessLinterWorker>(new Worker('./linter'), {timeout: 60000});
81
+ }));
82
+ }
83
+
84
+ function getChunkSize(arr: string[]) {
85
+ return Math.ceil(arr.length / WORKERS_COUNT);
86
+ }
87
+
88
+ function lintPagesFallback(navigationPaths: string[]) {
89
+ PluginService.setPlugins();
90
+
91
+ navigationPaths.forEach((pathToFile) => {
92
+ lintPage({
93
+ inputPath: pathToFile,
94
+ fileExtension: extname(pathToFile),
95
+ onFinish: () => {
96
+ logger.info(pathToFile, LINTING_FINISHED);
97
+ },
98
+ });
99
+ });
100
+ }
@@ -0,0 +1,18 @@
1
+ import log from '@doc-tools/transform/lib/log';
2
+ import _uniq from 'lodash/uniq';
3
+
4
+ export function processLogs(inputFolder: string) {
5
+ const replacementRegExp = new RegExp(inputFolder, 'ig');
6
+ const {info, warn, error} = log.get();
7
+ const outputLogs = _uniq([
8
+ '', ...info,
9
+ '', ...warn,
10
+ '', ...error,
11
+ '',
12
+ ]);
13
+
14
+ for (const outputLog of outputLogs) {
15
+ const preparedLog = outputLog.replace(replacementRegExp, '');
16
+ console.log(preparedLog);
17
+ }
18
+ }
@@ -0,0 +1,35 @@
1
+ import {writeFileSync} from 'fs';
2
+ import {extname, join} from 'path';
3
+
4
+ import {ArgvService, TocService} from '../services';
5
+ import {convertBackSlashToSlash} from '../utils';
6
+
7
+ type TocItem = {
8
+ name: string;
9
+ items?: TocItems;
10
+ href?: string;
11
+ };
12
+
13
+ type TocItems = TocItem[];
14
+
15
+ export function prepareMapFile(): void {
16
+ const {
17
+ output: outputFolderPath,
18
+ } = ArgvService.getConfig();
19
+
20
+ const navigationPathsWithoutExtensions =
21
+ TocService.getNavigationPaths().map((path) => {
22
+ let preparedPath = convertBackSlashToSlash(path.replace(extname(path), ''));
23
+
24
+ if (preparedPath.endsWith('/index')) {
25
+ preparedPath = preparedPath.substring(0, preparedPath.length - 5);
26
+ }
27
+
28
+ return preparedPath;
29
+ });
30
+ const navigationPaths = {files: [...new Set(navigationPathsWithoutExtensions)]};
31
+ const filesMapBuffer = Buffer.from(JSON.stringify(navigationPaths, null, '\t'), 'utf8');
32
+ const mapFile = join(outputFolderPath, 'files.json');
33
+
34
+ writeFileSync(mapFile, filesMapBuffer);
35
+ }
@@ -0,0 +1,312 @@
1
+ import type {DocInnerProps} from '@diplodoc/client';
2
+ import {basename, dirname, extname, resolve, join, relative} from 'path';
3
+ import shell from 'shelljs';
4
+ import {copyFileSync, readFileSync, writeFileSync} from 'fs';
5
+ import {bold} from 'chalk';
6
+ import {dump, load} from 'js-yaml';
7
+ import {mapLimit, asyncify} from 'async';
8
+
9
+ import log from '@doc-tools/transform/lib/log';
10
+
11
+ import {ArgvService, LeadingService, TocService, PluginService} from '../services';
12
+ import {resolveMd2HTML, resolveMd2Md} from '../resolvers';
13
+ import {generateStaticMarkup, joinSinglePageResults, logger, transformTocForSinglePage} from '../utils';
14
+ import {
15
+ MetaDataOptions,
16
+ SinglePageResult,
17
+ PathData,
18
+ YfmToc,
19
+ Resources,
20
+ LeadingPage,
21
+ } from '../models';
22
+ import {VCSConnector} from '../vcs-connector/connector-models';
23
+ import {getVCSConnector} from '../vcs-connector';
24
+ import {
25
+ SINGLE_PAGE_FILENAME,
26
+ SINGLE_PAGE_DATA_FILENAME,
27
+ Lang, ResourceType,
28
+ } from '../constants';
29
+
30
+ const singlePageResults: Record<string, SinglePageResult[]> = {};
31
+ const singlePagePaths: Record<string, Set<string>> = {};
32
+
33
+ // Processes files of documentation (like index.yaml, *.md)
34
+ export async function processPages(outputBundlePath: string): Promise<void> {
35
+ const {
36
+ input: inputFolderPath,
37
+ output: outputFolderPath,
38
+ outputFormat,
39
+ singlePage,
40
+ resolveConditions,
41
+ } = ArgvService.getConfig();
42
+
43
+ const vcsConnector = await getVCSConnector();
44
+
45
+ PluginService.setPlugins();
46
+
47
+ const navigationPaths = TocService.getNavigationPaths();
48
+ const concurrency = 500;
49
+
50
+ await mapLimit(navigationPaths, concurrency, asyncify(async (pathToFile: string) => {
51
+ const pathData = getPathData(pathToFile, inputFolderPath, outputFolderPath, outputFormat, outputBundlePath);
52
+
53
+ logger.proc(pathToFile);
54
+
55
+ const metaDataOptions = getMetaDataOptions(pathData, inputFolderPath.length, vcsConnector);
56
+
57
+ await preparingPagesByOutputFormat(pathData, metaDataOptions, resolveConditions, singlePage);
58
+ }));
59
+
60
+ if (singlePage) {
61
+ await saveSinglePages(outputBundlePath);
62
+ }
63
+ }
64
+
65
+ function getPathData(
66
+ pathToFile: string,
67
+ inputFolderPath: string,
68
+ outputFolderPath: string,
69
+ outputFormat: string,
70
+ outputBundlePath: string,
71
+ ): PathData {
72
+ const pathToDir: string = dirname(pathToFile);
73
+ const filename: string = basename(pathToFile);
74
+ const fileExtension: string = extname(pathToFile);
75
+ const fileBaseName: string = basename(filename, fileExtension);
76
+ const outputDir = resolve(outputFolderPath, pathToDir);
77
+ const outputFileName = `${fileBaseName}.${outputFormat}`;
78
+ const outputPath = resolve(outputDir, outputFileName);
79
+ const resolvedPathToFile = resolve(inputFolderPath, pathToFile);
80
+ const outputTocDir = TocService.getTocDir(resolvedPathToFile);
81
+
82
+ const pathData: PathData = {
83
+ pathToFile,
84
+ resolvedPathToFile,
85
+ filename,
86
+ fileBaseName,
87
+ fileExtension,
88
+ outputDir,
89
+ outputPath,
90
+ outputFormat,
91
+ outputBundlePath,
92
+ outputTocDir,
93
+ inputFolderPath,
94
+ outputFolderPath,
95
+ };
96
+
97
+ return pathData;
98
+ }
99
+
100
+ async function saveSinglePages(outputBundlePath: string) {
101
+ const {
102
+ input: inputFolderPath,
103
+ output: outputFolderPath,
104
+ lang,
105
+ resources,
106
+ } = ArgvService.getConfig();
107
+
108
+ try {
109
+ await Promise.all(Object.keys(singlePageResults).map(async (tocDir) => {
110
+ if (!singlePageResults[tocDir].length) {
111
+ return;
112
+ }
113
+
114
+ const singlePageBody = joinSinglePageResults(singlePageResults[tocDir], inputFolderPath, tocDir);
115
+ const tocPath = join(relative(inputFolderPath, tocDir), 'toc.yaml');
116
+ const toc: YfmToc|null = TocService.getForPath(tocPath) || null;
117
+ const preparedToc = transformTocForSinglePage(toc, {
118
+ root: inputFolderPath,
119
+ currentPath: join(tocDir, SINGLE_PAGE_FILENAME),
120
+ }) as YfmToc;
121
+
122
+ const pageData = {
123
+ data: {
124
+ leading: false as const,
125
+ html: singlePageBody,
126
+ headings: [],
127
+ meta: resources || {},
128
+ toc: preparedToc,
129
+ },
130
+ router: {
131
+ pathname: SINGLE_PAGE_FILENAME,
132
+ },
133
+ lang: lang || Lang.RU,
134
+ };
135
+
136
+ const outputTocDir = resolve(outputFolderPath, relative(inputFolderPath, tocDir));
137
+ const relativeOutputBundlePath = relative(outputTocDir, outputBundlePath);
138
+
139
+ // Save the full single page for viewing locally
140
+ const singlePageFn = join(tocDir, SINGLE_PAGE_FILENAME);
141
+ const singlePageDataFn = join(tocDir, SINGLE_PAGE_DATA_FILENAME);
142
+ const singlePageContent = generateStaticMarkup(pageData, relativeOutputBundlePath);
143
+
144
+ writeFileSync(singlePageFn, singlePageContent);
145
+ writeFileSync(singlePageDataFn, JSON.stringify(pageData));
146
+ }));
147
+ } catch (error) {
148
+ console.log(error);
149
+ }
150
+ }
151
+
152
+ function savePageResultForSinglePage(pageProps: DocInnerProps, pathData: PathData): void {
153
+ const {pathToFile, outputTocDir} = pathData;
154
+
155
+ if (pageProps.data.leading) {
156
+ return;
157
+ }
158
+
159
+ singlePagePaths[outputTocDir] = singlePagePaths[outputTocDir] || new Set();
160
+
161
+ if (singlePagePaths[outputTocDir].has(pathToFile)) {
162
+ return;
163
+ }
164
+
165
+ singlePagePaths[outputTocDir].add(pathToFile);
166
+
167
+ singlePageResults[outputTocDir] = singlePageResults[outputTocDir] || [];
168
+ singlePageResults[outputTocDir].push({
169
+ path: pathToFile,
170
+ content: pageProps.data.html,
171
+ title: pageProps.data.title,
172
+ });
173
+ }
174
+
175
+ function getMetaDataOptions(pathData: PathData, inputFolderPathLength: number, vcsConnector?: VCSConnector,
176
+ ): MetaDataOptions {
177
+ const {contributors, addSystemMeta, resources, allowCustomResources} = ArgvService.getConfig();
178
+
179
+ const metaDataOptions: MetaDataOptions = {
180
+ vcsConnector,
181
+ fileData: {
182
+ tmpInputFilePath: pathData.resolvedPathToFile,
183
+ inputFolderPathLength,
184
+ fileContent: '',
185
+ },
186
+ isContributorsEnabled: Boolean(contributors && vcsConnector),
187
+ addSystemMeta,
188
+ };
189
+
190
+
191
+ if (allowCustomResources && resources) {
192
+ const allowedResources = Object.entries(resources).reduce((acc: Resources, [key, val]) => {
193
+ if (Object.keys(ResourceType).includes(key)) {
194
+ acc[key as keyof typeof ResourceType] = val;
195
+ }
196
+ return acc;
197
+ }, {});
198
+
199
+ metaDataOptions.resources = allowedResources;
200
+ }
201
+
202
+
203
+ return metaDataOptions;
204
+ }
205
+
206
+ async function preparingPagesByOutputFormat(
207
+ path: PathData,
208
+ metaDataOptions: MetaDataOptions,
209
+ resolveConditions: boolean,
210
+ singlePage: boolean,
211
+ ): Promise<void> {
212
+ const {
213
+ filename,
214
+ fileExtension,
215
+ fileBaseName,
216
+ outputDir,
217
+ resolvedPathToFile,
218
+ outputFormat,
219
+ pathToFile,
220
+ } = path;
221
+ const {allowCustomResources} = ArgvService.getConfig();
222
+
223
+ try {
224
+ shell.mkdir('-p', outputDir);
225
+
226
+ const isYamlFileExtension = fileExtension === '.yaml';
227
+
228
+ if (resolveConditions && fileBaseName === 'index' && isYamlFileExtension) {
229
+ LeadingService.filterFile(pathToFile);
230
+ }
231
+
232
+ if (outputFormat === 'md' && isYamlFileExtension && allowCustomResources) {
233
+ processingYamlFile(path, metaDataOptions);
234
+ return;
235
+ }
236
+
237
+ if (outputFormat === 'md' && isYamlFileExtension ||
238
+ outputFormat === 'html' && !isYamlFileExtension && fileExtension !== '.md') {
239
+ copyFileWithoutChanges(resolvedPathToFile, outputDir, filename);
240
+ return;
241
+ }
242
+
243
+ switch (outputFormat) {
244
+ case 'md':
245
+ await processingFileToMd(path, metaDataOptions);
246
+ return;
247
+ case 'html': {
248
+ const resolvedFileProps = await processingFileToHtml(path, metaDataOptions);
249
+
250
+ if (singlePage) {
251
+ savePageResultForSinglePage(resolvedFileProps, path);
252
+ }
253
+
254
+ return;
255
+ }
256
+ }
257
+ } catch (e) {
258
+ const message = `No such file or has no access to ${bold(resolvedPathToFile)}`;
259
+ console.log(message, e);
260
+ log.error(message);
261
+ }
262
+ }
263
+ //@ts-ignore
264
+ function processingYamlFile(path: PathData, metaDataOptions: MetaDataOptions) {
265
+ const {pathToFile, outputFolderPath, inputFolderPath} = path;
266
+
267
+ const filePath = resolve(inputFolderPath, pathToFile);
268
+ const content = readFileSync(filePath, 'utf8');
269
+ const parsedContent = load(content) as LeadingPage;
270
+
271
+ if (metaDataOptions.resources) {
272
+ parsedContent.meta = {...parsedContent.meta, ...metaDataOptions.resources};
273
+ }
274
+
275
+ writeFileSync(resolve(outputFolderPath, pathToFile), dump(parsedContent));
276
+ }
277
+
278
+ function copyFileWithoutChanges(resolvedPathToFile: string, outputDir: string, filename: string): void {
279
+ const from = resolvedPathToFile;
280
+ const to = resolve(outputDir, filename);
281
+
282
+ copyFileSync(from, to);
283
+ }
284
+
285
+ async function processingFileToMd(path: PathData, metaDataOptions: MetaDataOptions): Promise<void> {
286
+ const {outputPath, pathToFile} = path;
287
+
288
+ await resolveMd2Md({
289
+ inputPath: pathToFile,
290
+ outputPath,
291
+ metadata: metaDataOptions,
292
+ });
293
+ }
294
+
295
+ async function processingFileToHtml(path: PathData, metaDataOptions: MetaDataOptions): Promise<DocInnerProps> {
296
+ const {
297
+ outputBundlePath,
298
+ filename,
299
+ fileExtension,
300
+ outputPath,
301
+ pathToFile,
302
+ } = path;
303
+
304
+ return resolveMd2HTML({
305
+ inputPath: pathToFile,
306
+ outputBundlePath,
307
+ fileExtension,
308
+ outputPath,
309
+ filename,
310
+ metadata: metaDataOptions,
311
+ });
312
+ }
@@ -0,0 +1,95 @@
1
+ import {dirname, resolve} from 'path';
2
+ import walkSync from 'walk-sync';
3
+ import {readFileSync, writeFileSync} from 'fs';
4
+ import {load, dump} from 'js-yaml';
5
+ import log from '@doc-tools/transform/lib/log';
6
+
7
+ import {ArgvService, PresetService, TocService} from '../services';
8
+ import {logger} from '../utils';
9
+ import {DocPreset} from '../models';
10
+ import shell from 'shelljs';
11
+
12
+ type GetFilePathsByGlobalsFunction = (globs: string[]) => string[];
13
+
14
+ export async function processServiceFiles(): Promise<void> {
15
+ const {input: inputFolderPath, ignore = []} = ArgvService.getConfig();
16
+
17
+ const getFilePathsByGlobals = (globs: string[]): string[] => {
18
+ return walkSync(inputFolderPath, {
19
+ directories: false,
20
+ includeBasePath: false,
21
+ globs,
22
+ ignore,
23
+ });
24
+ };
25
+
26
+ preparingPresetFiles(getFilePathsByGlobals);
27
+ await preparingTocFiles(getFilePathsByGlobals);
28
+ }
29
+
30
+ function preparingPresetFiles(getFilePathsByGlobals: GetFilePathsByGlobalsFunction): void {
31
+ const {
32
+ input: inputFolderPath,
33
+ varsPreset = '',
34
+ outputFormat,
35
+ applyPresets,
36
+ resolveConditions,
37
+ } = ArgvService.getConfig();
38
+
39
+ try {
40
+ const presetsFilePaths = getFilePathsByGlobals(['**/presets.yaml']);
41
+
42
+ for (const path of presetsFilePaths) {
43
+ logger.proc(path);
44
+
45
+ const pathToPresetFile = resolve(inputFolderPath, path);
46
+ const content = readFileSync(pathToPresetFile, 'utf8');
47
+ const parsedPreset = load(content) as DocPreset;
48
+
49
+ PresetService.add(parsedPreset, path, varsPreset);
50
+
51
+ if (outputFormat === 'md' && (!applyPresets || !resolveConditions)) {
52
+ // Should save filtered presets.yaml only when --apply-presets=false or --resolve-conditions=false
53
+ saveFilteredPresets(path, parsedPreset);
54
+ }
55
+ }
56
+ } catch (error) {
57
+ log.error(`Preparing presets.yaml files failed. Error: ${error}`);
58
+ throw error;
59
+ }
60
+ }
61
+
62
+ function saveFilteredPresets(path: string, parsedPreset: DocPreset): void {
63
+ const {output: outputFolderPath, varsPreset = ''} = ArgvService.getConfig();
64
+
65
+ const outputPath = resolve(outputFolderPath, path);
66
+ const filteredPreset: Record<string, Object> = {
67
+ default: parsedPreset.default,
68
+ };
69
+
70
+ if (parsedPreset[varsPreset]) {
71
+ filteredPreset[varsPreset] = parsedPreset[varsPreset];
72
+ }
73
+
74
+ const outputPreset = dump(filteredPreset, {
75
+ lineWidth: 120,
76
+ });
77
+
78
+ shell.mkdir('-p', dirname(outputPath));
79
+ writeFileSync(outputPath, outputPreset);
80
+ }
81
+
82
+ async function preparingTocFiles(getFilePathsByGlobals: GetFilePathsByGlobalsFunction): Promise<void> {
83
+ try {
84
+ const tocFilePaths = getFilePathsByGlobals(['**/toc.yaml']);
85
+
86
+ for (const path of tocFilePaths) {
87
+ logger.proc(path);
88
+
89
+ await TocService.add(path);
90
+ }
91
+ } catch (error) {
92
+ log.error(`Preparing toc.yaml files failed. Error: ${error}`);
93
+ throw error;
94
+ }
95
+ }
@@ -0,0 +1,47 @@
1
+ import {readFileSync} from 'fs';
2
+ import walkSync from 'walk-sync';
3
+ import {resolve, join} from 'path';
4
+ import S3 from 'aws-sdk/clients/s3';
5
+ import mime from 'mime-types';
6
+
7
+ import {ArgvService} from '../services';
8
+ import {convertBackSlashToSlash, logger} from '../utils';
9
+
10
+ const DEFAULT_PREFIX = process.env.YFM_STORAGE_PREFIX ?? '';
11
+
12
+ export async function publishFilesToS3(): Promise<void> {
13
+ const {
14
+ output: outputFolderPath,
15
+ ignore = [],
16
+ storageEndpoint: endpoint,
17
+ storageBucket: bucket,
18
+ storagePrefix: prefix = DEFAULT_PREFIX,
19
+ storageKeyId: accessKeyId,
20
+ storageSecretKey: secretAccessKey,
21
+ } = ArgvService.getConfig();
22
+
23
+ const s3Client = new S3({
24
+ endpoint, accessKeyId, secretAccessKey,
25
+ });
26
+
27
+ const filesToPublish: string[] = walkSync(resolve(outputFolderPath), {
28
+ directories: false,
29
+ includeBasePath: false,
30
+ ignore,
31
+ });
32
+
33
+ await Promise.all(filesToPublish.map(async (pathToFile) => {
34
+ const mimeType = mime.lookup(pathToFile);
35
+
36
+ const params: S3.Types.PutObjectRequest = {
37
+ ContentType: mimeType ? mimeType : undefined,
38
+ Bucket: bucket,
39
+ Key: convertBackSlashToSlash(join(prefix, pathToFile)),
40
+ Body: readFileSync(resolve(outputFolderPath, pathToFile)),
41
+ };
42
+
43
+ logger.upload(pathToFile);
44
+
45
+ return s3Client.upload(params).promise();
46
+ }));
47
+ }
@@ -0,0 +1,17 @@
1
+ import {dirname, resolve} from 'path';
2
+ import shell from 'shelljs';
3
+ import {copyFileSync} from 'fs';
4
+ import {logger} from './logger';
5
+
6
+ export function copyFiles(inputFolderPath: string, outputFolderPath: string, files: string[]): void {
7
+ for (const pathToAsset of files) {
8
+ const outputDir: string = resolve(outputFolderPath, dirname(pathToAsset));
9
+ const from = resolve(inputFolderPath, pathToAsset);
10
+ const to = resolve(outputFolderPath, pathToAsset);
11
+
12
+ shell.mkdir('-p', outputDir);
13
+ copyFileSync(from, to);
14
+
15
+ logger.copy(pathToAsset);
16
+ }
17
+ }
@@ -0,0 +1,14 @@
1
+ import libglob, {IGlob, IOptions} from 'glob';
2
+
3
+ export type Glob = {state: IGlob};
4
+
5
+ const glob = async (pattern: string, options: IOptions): Promise<Glob> =>
6
+ new Promise((res, rej) => {
7
+ const state: IGlob = libglob(pattern, options, (err) =>
8
+ err ? rej(err) : res({state}),
9
+ );
10
+ });
11
+
12
+ export {glob};
13
+
14
+ export default {glob};
@@ -0,0 +1,8 @@
1
+ export * from './logger';
2
+ export * from './markup';
3
+ export * from './url';
4
+ export * from './path';
5
+ export * from './toc';
6
+ export * from './presets';
7
+ export * from './glob';
8
+ export * from './file';
@@ -0,0 +1,42 @@
1
+ import log from '@doc-tools/transform/lib/log';
2
+ import {blue, green, grey, red, yellow} from 'chalk';
3
+ import {ArgvService} from '../services';
4
+
5
+ function writeLog(msg: string, fatal = false) {
6
+ const {quiet} = ArgvService.getConfig();
7
+
8
+ if (quiet && !fatal) {
9
+ return;
10
+ }
11
+
12
+ console.log(msg);
13
+ }
14
+
15
+ export const logger = {
16
+ info: function (pathToFile: string, extraMessage?: string) {
17
+ writeLog(`${grey('INFO')} ${extraMessage} ${pathToFile}`);
18
+ },
19
+ proc: function (pathToFile: string) {
20
+ writeLog(`${blue('PROC')} Processing file ${pathToFile}`);
21
+ },
22
+ copy: function (pathToFile: string) {
23
+ writeLog(`${green('COPY')} Copying file ${pathToFile}`);
24
+ },
25
+ upload: function (pathToFile: string) {
26
+ writeLog(`${green('UPLOAD')} Uploading file ${pathToFile}`);
27
+ },
28
+ warn: function (pathToFile: string, extraMessage: string) {
29
+ const message = `${yellow('WARNING')} file: ${pathToFile} error: ${extraMessage}`;
30
+
31
+ writeLog(message);
32
+
33
+ log.warn(`file: ${pathToFile} ${extraMessage}`);
34
+ },
35
+ error: function (pathToFile: string, extraMessage: string) {
36
+ const message = `${red('ERROR')} file: ${pathToFile} error: ${extraMessage}`;
37
+
38
+ writeLog(message, true);
39
+
40
+ log.error(`file: ${pathToFile} ${extraMessage}`);
41
+ },
42
+ };