@diplodoc/cli 4.13.4 → 4.13.6

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.
@@ -1,14 +1,18 @@
1
+ import {ok} from 'assert';
2
+ import {argvValidator} from '../../validator';
3
+ import glob from 'glob';
4
+
1
5
  const {
2
6
  promises: {readFile, writeFile, mkdir},
3
7
  } = require('fs');
4
- import {dirname, extname, join} from 'path';
8
+ import {dirname, join} from 'path';
5
9
 
6
- import markdownTranslation, {ComposeParameters} from '@diplodoc/markdown-translation';
10
+ // @ts-ignore
11
+ import {ComposeParams, compose as composeMD} from '@diplodoc/markdown-translation';
7
12
  import {Arguments, Argv} from 'yargs';
8
13
  import {eachLimit} from 'async';
9
14
 
10
15
  import {ArgvService} from '../../services';
11
- import {glob, logger} from '../../utils';
12
16
 
13
17
  const command = 'compose';
14
18
 
@@ -16,25 +20,8 @@ const description = 'compose xliff and skeleton into documentation';
16
20
 
17
21
  const compose = {command, description, handler, builder};
18
22
 
19
- const SKL_MD_GLOB = '**/*.skl.md';
20
- const XLF_GLOB = '**/*.xliff';
21
23
  const MAX_CONCURRENCY = 50;
22
24
 
23
- class ComposeError extends Error {
24
- path: string;
25
-
26
- constructor(message: string, path: string) {
27
- super(message);
28
-
29
- this.path = path;
30
- }
31
- }
32
-
33
- const USAGE =
34
- 'yfm xliff compose \
35
- --input <folder-with-xliff-and-skeleton> \
36
- --ouput <folder-to-store-translated-markdown>';
37
-
38
25
  function builder<T>(argv: Argv<T>) {
39
26
  return argv
40
27
  .option('input', {
@@ -47,183 +34,90 @@ function builder<T>(argv: Argv<T>) {
47
34
  describe: 'output folder where translated markdown will be stored',
48
35
  type: 'string',
49
36
  })
50
- .demandOption(['input', 'output'], USAGE);
37
+ .option('target-language', {
38
+ alias: 'tl',
39
+ describe: 'target language',
40
+ type: 'string',
41
+ })
42
+ .option('use-source', {
43
+ describe: 'for debug',
44
+ type: 'boolean',
45
+ })
46
+ .check(argvValidator);
51
47
  }
52
48
 
49
+ type HandlerParams = {
50
+ input: string;
51
+ output: string;
52
+ targetLanguage: string;
53
+ exclude?: string[];
54
+ useSource?: boolean;
55
+ };
56
+
53
57
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
54
- async function handler(args: Arguments<any>) {
58
+ async function handler(args: Arguments<HandlerParams>) {
59
+ args = Object.assign({}, args.translate || {}, args);
60
+ delete args.translate;
61
+
55
62
  ArgvService.init({
56
63
  ...args,
57
64
  });
58
65
 
59
- const {input, output} = args;
60
-
61
- let cache = {};
62
- let skeletonPaths: string[] = [];
63
- let xliffPaths: string[] = [];
64
-
65
- try {
66
- ({
67
- state: {found: skeletonPaths, cache},
68
- } = await glob(join(input, SKL_MD_GLOB), {
69
- nosort: false,
70
- cache,
71
- }));
72
-
73
- ({
74
- state: {found: xliffPaths, cache},
75
- } = await glob(join(input, XLF_GLOB), {
76
- nosort: false,
77
- cache,
78
- }));
79
-
80
- if (xliffPaths.length !== skeletonPaths.length) {
81
- throw new ComposeError("number of xliff and skeleton files does'not match", input);
82
- }
83
- } catch (err) {
84
- if (err instanceof Error || err instanceof ComposeError) {
85
- const file = err instanceof ComposeError ? err.path : input;
86
-
87
- logger.error(file, err.message);
88
- }
89
- }
90
-
91
- const pipelineParameters = {input, output};
92
- const configuredPipeline = pipeline(pipelineParameters);
93
-
94
- try {
95
- logger.info(input, 'staring translated markdown composition pipeline');
66
+ const {
67
+ input,
68
+ output,
69
+ exclude = [],
70
+ targetLanguage,
71
+ useSource = false,
72
+ } = ArgvService.getConfig() as unknown as HandlerParams;
73
+
74
+ ok(input);
75
+ ok(output);
76
+ ok(targetLanguage);
77
+
78
+ const skeletons = glob.sync('**/*.skl', {
79
+ cwd: join(input, targetLanguage),
80
+ ignore: exclude,
81
+ });
82
+ const xliffs = glob.sync('**/*.xliff', {
83
+ cwd: join(input, targetLanguage),
84
+ ignore: exclude,
85
+ });
96
86
 
97
- await eachLimit(xliffPaths, MAX_CONCURRENCY, configuredPipeline);
87
+ ok(xliffs.length === skeletons.length, 'Inconsistent number of xliff and skeleton files.');
98
88
 
99
- logger.info(input, 'finished translated markdown composition pipeline');
100
- } catch (err) {
101
- if (err instanceof Error || err instanceof ComposeError) {
102
- const file = err instanceof ComposeError ? err.path : input;
89
+ const pipelineParameters = {input, output, targetLanguage, useSource};
90
+ const configuredPipeline = pipeline(pipelineParameters);
103
91
 
104
- logger.error(file, err.message);
105
- }
106
- }
92
+ await eachLimit(skeletons, MAX_CONCURRENCY, configuredPipeline);
107
93
  }
108
94
 
109
- export type PipelineParameters = {
110
- input: string;
111
- output: string;
112
- };
95
+ export type PipelineParameters = ComposeParams;
113
96
 
114
97
  function pipeline(params: PipelineParameters) {
115
- const {input, output} = params;
116
-
117
- return async (xliffPath: string) => {
118
- const extension = extname(xliffPath);
119
- const extensionLessPath = xliffPath.replace(extension, '');
120
- const skeletonPath = extensionLessPath + '.skl.md';
121
-
122
- const readerParameters = {xliffPath, skeletonPath};
123
- const read = await reader(readerParameters);
124
-
125
- const composerParameters = {
126
- ...read,
98
+ const {input, output, targetLanguage, useSource} = params;
99
+
100
+ return async (skeletonPath: string) => {
101
+ const fileName = skeletonPath.split('.').slice(0, -1).join('.');
102
+ const xliffPath = fileName + '.xliff';
103
+
104
+ const [skeleton, xliff] = await Promise.all<string[]>([
105
+ readFile(join(input, targetLanguage, skeletonPath), 'utf-8'),
106
+ readFile(join(input, targetLanguage, xliffPath), 'utf-8'),
107
+ ]);
108
+ const markdown = composeMD({
109
+ skeleton,
110
+ xliff,
127
111
  skeletonPath,
128
112
  xliffPath,
129
- };
130
- const {markdown} = await composer(composerParameters);
113
+ useSource,
114
+ });
131
115
 
132
- const inputRelativePath = extensionLessPath.slice(input.length);
133
- const markdownPath = join(output, inputRelativePath) + '.md';
134
-
135
- const writerParameters = {
136
- markdown,
137
- markdownPath,
138
- };
139
- await writer(writerParameters);
140
- };
141
- }
142
-
143
- export type ReaderParameters = {
144
- skeletonPath: string;
145
- xliffPath: string;
146
- };
147
-
148
- async function reader(params: ReaderParameters) {
149
- const {skeletonPath, xliffPath} = params;
150
-
151
- let skeleton;
152
- let xlf;
153
-
154
- try {
155
- logger.info(skeletonPath, 'reading skeleton file');
156
-
157
- skeleton = await readFile(skeletonPath, {encoding: 'utf-8'});
158
-
159
- logger.info(skeletonPath, 'finished reading skeleton file');
160
- } catch (err) {
161
- if (err instanceof Error) {
162
- throw new ComposeError(err.message, skeletonPath);
163
- }
164
- }
165
-
166
- try {
167
- logger.info(xliffPath, 'reading xliff file');
168
-
169
- xlf = await readFile(xliffPath, {encoding: 'utf-8'});
170
-
171
- logger.info(xliffPath, 'finished reading xliff file');
172
- } catch (err) {
173
- if (err instanceof Error) {
174
- throw new ComposeError(err.message, xliffPath);
175
- }
176
- }
177
-
178
- return {skeleton, xlf};
179
- }
180
-
181
- export type ComposerParameters = {
182
- skeletonPath: string;
183
- xliffPath: string;
184
- } & ComposeParameters;
185
-
186
- async function composer(params: ComposerParameters) {
187
- const {skeletonPath, xliffPath} = params;
188
- let markdown;
189
-
190
- try {
191
- logger.info(skeletonPath, 'composing markdown from xliff and skeleton');
192
- logger.info(xliffPath, 'composing markdown from xliff and skeleton');
193
-
194
- markdown = markdownTranslation.compose(params);
195
-
196
- logger.info(skeletonPath, 'finished composing markdown from xliff and skeleton');
197
- logger.info(xliffPath, 'finished composing markdown from xliff and skeleton');
198
- } catch (err) {
199
- if (err instanceof Error) {
200
- throw new ComposeError(err.message, `${xliffPath} ${skeletonPath}`);
201
- }
202
- }
203
-
204
- return {markdown};
205
- }
206
-
207
- export type WriterParameters = {
208
- markdown: string;
209
- markdownPath: string;
210
- };
211
-
212
- async function writer(params: WriterParameters) {
213
- const {markdown, markdownPath} = params;
214
-
215
- try {
216
- logger.info(markdownPath, 'writing markdown file');
116
+ const markdownPath = join(output, targetLanguage, fileName);
217
117
 
218
118
  await mkdir(dirname(markdownPath), {recursive: true});
219
119
  await writeFile(markdownPath, markdown);
220
-
221
- logger.info(markdownPath, 'finished writing markdown file');
222
- } catch (err) {
223
- if (err instanceof Error) {
224
- throw new ComposeError(err.message, markdownPath);
225
- }
226
- }
120
+ };
227
121
  }
228
122
 
229
123
  export {compose};
@@ -1,14 +1,18 @@
1
+ import {ok} from 'assert';
2
+ import glob from 'glob';
3
+ import {argvValidator} from '../../validator';
4
+
1
5
  const {
2
6
  promises: {readFile, writeFile, mkdir},
3
7
  } = require('fs');
4
- import {dirname, extname, join} from 'path';
8
+ import {basename, dirname, join, resolve} from 'path';
5
9
 
6
- import markdownTranslation, {ExtractParameters} from '@diplodoc/markdown-translation';
10
+ // @ts-ignore
11
+ import {ExtractParams, extract as extractMD} from '@diplodoc/markdown-translation';
7
12
  import {Arguments, Argv} from 'yargs';
8
13
  import {asyncify, eachLimit} from 'async';
9
14
 
10
15
  import {ArgvService} from '../../services';
11
- import {glob, logger} from '../../utils';
12
16
 
13
17
  const command = 'extract';
14
18
 
@@ -16,30 +20,8 @@ const description = 'extract xliff and skeleton from yfm documentation';
16
20
 
17
21
  const extract = {command, description, handler, builder};
18
22
 
19
- const MD_GLOB = '**/*.md';
20
-
21
23
  const MAX_CONCURRENCY = 50;
22
24
 
23
- class ExtractError extends Error {
24
- path: string;
25
-
26
- constructor(message: string, path: string) {
27
- super(message);
28
-
29
- this.path = path;
30
- }
31
- }
32
-
33
- const USAGE = `yfm xliff extract \
34
- --input <folder-with-markdown> \
35
- --output <folder-to-store-xlff-and-skeleton> \
36
- --sll <source-language>-<source-locale> \
37
- --tll <target-language>-<target-locale>
38
-
39
- where <source/target-language> is the language code, as described in ISO 639-1.
40
-
41
- where <source/target-locale> is the locale code in alpha-2 format, as described in ISO 3166-1`;
42
-
43
25
  function builder<T>(argv: Argv<T>) {
44
26
  return argv
45
27
  .option('source-language-locale', {
@@ -62,185 +44,132 @@ function builder<T>(argv: Argv<T>) {
62
44
  describe: 'output folder to store xliff and skeleton files',
63
45
  type: 'string',
64
46
  })
65
- .demandOption(
66
- ['source-language-locale', 'target-language-locale', 'input', 'output'],
67
- USAGE,
68
- );
47
+ .check(argvValidator);
69
48
  }
70
49
 
50
+ type HandlerParams = {
51
+ input: string;
52
+ output: string;
53
+ include?: string[];
54
+ exclude?: string[];
55
+ sourceLanguage?: string;
56
+ sourceLocale?: string;
57
+ sourceLanguageLocale?: string;
58
+ targetLanguage?: string;
59
+ targetLocale?: string;
60
+ targetLanguageLocale?: string;
61
+ };
62
+
71
63
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
- async function handler(args: Arguments<any>) {
64
+ async function handler(args: Arguments<HandlerParams>) {
65
+ args = Object.assign({}, args.translate || {}, args);
66
+ delete args.translate;
67
+
73
68
  ArgvService.init({
74
69
  ...args,
75
70
  });
76
71
 
77
- const {input, output, sourceLanguageLocale, targetLanguageLocale} = args;
78
-
79
- let source;
80
- let target;
81
-
82
- try {
83
- source = parseLanguageLocale(sourceLanguageLocale);
84
- target = parseLanguageLocale(targetLanguageLocale);
85
- } catch (err) {
86
- if (err instanceof Error) {
87
- logger.error(input, err.message);
88
- }
89
- }
90
-
91
- let cache = {};
92
- let found: string[] = [];
93
-
94
- try {
95
- ({
96
- state: {found, cache},
97
- } = await glob(join(input, MD_GLOB), {
98
- nosort: true,
99
- cache,
100
- }));
101
- } catch (err) {
102
- if (err instanceof Error) {
103
- logger.error(input, err.message);
104
- }
72
+ const {
73
+ output,
74
+ exclude = [],
75
+ sourceLanguage,
76
+ sourceLocale,
77
+ targetLanguage,
78
+ targetLocale,
79
+ } = ArgvService.getConfig() as HandlerParams;
80
+
81
+ let {
82
+ input,
83
+ include = [],
84
+ sourceLanguageLocale,
85
+ targetLanguageLocale,
86
+ } = ArgvService.getConfig() as HandlerParams;
87
+
88
+ ok(input);
89
+ ok(output);
90
+
91
+ ok(
92
+ sourceLanguageLocale || (sourceLanguage && sourceLocale),
93
+ 'Source language and locale should be configured',
94
+ );
95
+
96
+ ok(
97
+ targetLanguageLocale || (targetLanguage && targetLocale),
98
+ 'Source language and locale should be configured',
99
+ );
100
+
101
+ sourceLanguageLocale = sourceLanguageLocale || sourceLanguage + '-' + sourceLocale;
102
+ targetLanguageLocale = targetLanguageLocale || targetLanguage + '-' + targetLocale;
103
+
104
+ const source = parseLanguageLocale(sourceLanguageLocale);
105
+ const target = parseLanguageLocale(targetLanguageLocale);
106
+
107
+ if (input.endsWith('.md')) {
108
+ include = [basename(input)];
109
+ input = dirname(input);
110
+ } else if (!include.length) {
111
+ include.push('**/*');
105
112
  }
106
113
 
107
- const pipelineParameters = {source, target, input, output};
108
- const configuredPipeline = pipeline(pipelineParameters);
109
-
110
- try {
111
- logger.info(input, 'starting xliff and skeleton generation pipeline');
112
-
113
- await eachLimit(found, MAX_CONCURRENCY, asyncify(configuredPipeline));
114
-
115
- logger.info(input, 'finished xliff and skeleton generation pipeline');
116
- } catch (err) {
117
- if (err instanceof Error || err instanceof ExtractError) {
118
- const file = err instanceof ExtractError ? err.path : input;
119
-
120
- logger.error(file, err.message);
121
- }
122
- }
114
+ const files = ([] as string[]).concat(
115
+ ...include.map((match) =>
116
+ glob.sync(match, {
117
+ cwd: join(input, source.language),
118
+ ignore: exclude,
119
+ }),
120
+ ),
121
+ );
122
+ const found = [...new Set(files)];
123
+ const configuredPipeline = pipeline({source, target, input, output});
124
+
125
+ await eachLimit(found, MAX_CONCURRENCY, asyncify(configuredPipeline));
123
126
  }
124
127
 
125
128
  function parseLanguageLocale(languageLocale: string) {
126
129
  const [language, locale] = languageLocale.split('-');
127
- if (language?.length && locale?.length) {
128
- return {language, locale};
130
+ if (!language?.length || !locale?.length) {
131
+ throw new Error('invalid language-locale string');
129
132
  }
130
133
 
131
- throw new Error('invalid language-locale string');
134
+ return {language, locale};
132
135
  }
133
136
 
134
137
  export type PipelineParameters = {
135
138
  input: string;
136
139
  output: string;
137
- source: ExtractParameters['source'];
138
- target: ExtractParameters['target'];
140
+ source: ExtractParams['source'];
141
+ target: ExtractParams['target'];
139
142
  };
140
143
 
141
144
  function pipeline(params: PipelineParameters) {
142
145
  const {input, output, source, target} = params;
146
+ const inputRoot = resolve(input, source.language);
147
+ const outputRoot = resolve(output, target.language);
143
148
 
144
- return async (markdownPath: string) => {
145
- const markdown = await reader({path: markdownPath});
146
- const extension = extname(markdownPath);
149
+ return async (path: string) => {
150
+ if (!path.endsWith('.md')) {
151
+ return;
152
+ }
147
153
 
148
- const outputRelativePath = markdownPath.replace(extension, '').slice(input.length);
154
+ const inputPath = join(inputRoot, path);
155
+ const xliffPath = join(outputRoot, path + '.xliff');
156
+ const skeletonPath = join(outputRoot, path + '.skl');
157
+ const markdown = await readFile(inputPath, 'utf-8');
149
158
 
150
- const outputPath = join(output, outputRelativePath);
151
- const xlfPath = outputPath + '.xliff';
152
- const skeletonPath = outputPath + '.skl.md';
159
+ await mkdir(dirname(xliffPath), {recursive: true});
153
160
 
154
- const extractParameters = {
155
- markdownPath,
161
+ const {xliff, skeleton} = await extractMD({
162
+ markdownPath: path,
156
163
  skeletonPath,
157
164
  markdown,
158
165
  source,
159
166
  target,
160
- };
161
-
162
- const extracted = await extractor(extractParameters);
167
+ });
163
168
 
164
- const writerParameters = {
165
- ...extracted,
166
- xlfPath,
167
- skeletonPath,
168
- };
169
-
170
- await writer(writerParameters);
169
+ await Promise.all([writeFile(skeletonPath, skeleton), writeFile(xliffPath, xliff)]);
171
170
  };
172
171
  }
173
172
 
174
- export type ReaderParameters = {
175
- path: string;
176
- };
177
-
178
- async function reader(params: ReaderParameters) {
179
- const {path} = params;
180
-
181
- let markdown;
182
- try {
183
- logger.info(path, 'reading markdown file');
184
-
185
- markdown = await readFile(path, {encoding: 'utf-8'});
186
-
187
- logger.info(path, 'finished reading markdown file');
188
- } catch (err) {
189
- if (err instanceof Error) {
190
- throw new ExtractError(err.message, path);
191
- }
192
- }
193
-
194
- return markdown;
195
- }
196
-
197
- export type ExtractorParameters = {
198
- source: ExtractParameters['source'];
199
- target: ExtractParameters['target'];
200
- skeletonPath: string;
201
- markdownPath: string;
202
- markdown: string;
203
- };
204
-
205
- async function extractor(params: ExtractorParameters) {
206
- let extracted;
207
-
208
- logger.info(params.markdownPath, 'generating skeleton and xliff from markdown');
209
-
210
- try {
211
- extracted = markdownTranslation.extract(params);
212
- } catch (err) {
213
- if (err instanceof Error) {
214
- throw new ExtractError(err.message, params.markdownPath);
215
- }
216
- }
217
-
218
- logger.info(params.markdownPath, 'finished generating skeleton and xliff from markdown');
219
-
220
- return extracted;
221
- }
222
-
223
- export type WriterParameters = {
224
- skeletonPath: string;
225
- skeleton: string;
226
- xlfPath: string;
227
- xlf: string;
228
- };
229
-
230
- async function writer(params: WriterParameters) {
231
- const {xlfPath, skeletonPath, xlf, skeleton} = params;
232
-
233
- logger.info(params.xlfPath, 'writing xliff file');
234
- logger.info(params.skeletonPath, 'writing skeleton file');
235
-
236
- await mkdir(dirname(xlfPath), {recursive: true});
237
-
238
- await Promise.all([writeFile(skeletonPath, skeleton), writeFile(xlfPath, xlf)]);
239
-
240
- logger.info(params.xlfPath, 'finished writing xliff file');
241
- logger.info(params.skeletonPath, 'finished writing skeleton file');
242
- }
243
-
244
173
  export {extract};
245
174
 
246
175
  export default {extract};