@diplodoc/cli 4.13.6 → 4.13.7

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.
@@ -0,0 +1,165 @@
1
+ import {ok} from 'assert';
2
+ import {basename, dirname, extname, resolve} from 'path';
3
+ import {readFileSync} from 'node:fs';
4
+ import glob from 'glob';
5
+
6
+ export {dumpFile, loadFile} from './fs';
7
+ export {extract, compose} from './translate';
8
+
9
+ type TranslateArgs = {
10
+ input: string;
11
+ output?: string;
12
+ source?: string;
13
+ sourceLanguage?: string;
14
+ sourceLanguageLocale?: string;
15
+ target?: string;
16
+ targetLanguage?: string;
17
+ targetLanguageLocale?: string;
18
+ auth?: string;
19
+ folder?: string;
20
+ glossary?: string;
21
+ include?: string[] | string;
22
+ exclude?: string[] | string;
23
+ dryRun?: boolean;
24
+ useSource?: boolean;
25
+ } & {
26
+ [prop: string]: any;
27
+ };
28
+
29
+ export type TranslateParams = {
30
+ input: string;
31
+ output: string;
32
+ source: [string, string];
33
+ targets: [string, string][];
34
+ auth?: string;
35
+ folder?: string;
36
+ glossary?: string;
37
+ files: string[];
38
+ dryRun: boolean;
39
+ useSource: boolean;
40
+ };
41
+
42
+ export function normalizeParams(params: TranslateArgs, exts = EXTS): TranslateParams {
43
+ const source = normalizeLocale(params, 'source')[0];
44
+ const targets = normalizeLocale(params, 'target');
45
+ const {input, files} = normalizeInput(params, source[0], exts);
46
+ const {output, auth, folder, dryRun, useSource} = params;
47
+
48
+ ok(input, 'Required param input is not configured');
49
+
50
+ return {
51
+ input,
52
+ output: output || input,
53
+ auth,
54
+ folder,
55
+ source,
56
+ targets,
57
+ files: [...new Set(files)],
58
+ dryRun: Boolean(dryRun),
59
+ useSource: Boolean(useSource),
60
+ };
61
+ }
62
+
63
+ function normalizeLocale(params: TranslateArgs, scope: 'source' | 'target'): [string, string][] {
64
+ const _scopeLanguage = params[scope + 'Language'];
65
+ const _scopeLanguageLocale = params[scope + 'LanguageLocale'];
66
+ const _scope = params[scope] || _scopeLanguageLocale || _scopeLanguage;
67
+
68
+ if (!_scope) {
69
+ return [['', '']];
70
+ }
71
+
72
+ if (Array.isArray(_scope)) {
73
+ return _scope.map((_scope) => _scope.split('-'));
74
+ }
75
+
76
+ return [_scope.split('-') as [string, string]];
77
+ }
78
+
79
+ const EXTS = ['.md', '.yaml', '.json'];
80
+
81
+ function normalizeInput(params: TranslateArgs, language: string, exts: string[]) {
82
+ let {input, include = [], exclude = []} = params;
83
+
84
+ include = ([] as string[]).concat(include);
85
+ exclude = ([] as string[]).concat(exclude);
86
+
87
+ let files: string[] | null = null;
88
+ if (extname(input) === '.list') {
89
+ const list = readFileSync(input, 'utf8').split('\n');
90
+ input = dirname(input);
91
+ files = list.map((file) => {
92
+ const absPath = resolve(input, file);
93
+
94
+ if (!absPath.startsWith(input)) {
95
+ throw new Error(`Insecure access to file out of project scope. (file: ${absPath})`);
96
+ }
97
+
98
+ if (!exts.includes(extname(file))) {
99
+ throw new Error(`Unhandles file extension. (file: ${absPath})`);
100
+ }
101
+
102
+ return absPath;
103
+ });
104
+ } else {
105
+ if (exts.includes(extname(input))) {
106
+ files = [basename(input)];
107
+ input = dirname(input);
108
+ }
109
+
110
+ if (!include.length) {
111
+ include.push('...');
112
+ }
113
+
114
+ include = include.reduce((acc, item) => {
115
+ if (item === '...') {
116
+ acc.push(...exts.map((ext) => (language || '.') + '/**/*' + ext));
117
+ } else {
118
+ acc.push(item);
119
+ }
120
+
121
+ return acc;
122
+ }, [] as string[]);
123
+
124
+ files =
125
+ files ||
126
+ ([] as string[]).concat(
127
+ ...include.map((match) =>
128
+ glob.sync(match, {
129
+ cwd: input,
130
+ ignore: exclude,
131
+ nodir: true,
132
+ }),
133
+ ),
134
+ );
135
+ }
136
+
137
+ return {input, files};
138
+ }
139
+
140
+ export function resolveSchemas(_path: string) {
141
+ return null;
142
+ }
143
+
144
+ export function flat<T>(parts: T[][]) {
145
+ return ([] as T[]).concat(...parts);
146
+ }
147
+
148
+ export class Defer {
149
+ resolve!: (text: string[]) => void;
150
+
151
+ reject!: (error: any) => void;
152
+
153
+ promise: Promise<string[]>;
154
+
155
+ constructor() {
156
+ this.promise = new Promise((resolve, reject) => {
157
+ this.resolve = resolve;
158
+ this.reject = reject;
159
+ });
160
+ }
161
+ }
162
+
163
+ export function bytes(texts: string[]) {
164
+ return texts.reduce((sum, text) => sum + text.length, 0);
165
+ }
@@ -0,0 +1,43 @@
1
+ import type {ComposeOptions, ExtractOptions} from '@diplodoc/translation';
2
+ import {compose as _compose, extract as _extract} from '@diplodoc/translation';
3
+
4
+ class ExtractError extends Error {
5
+ code: string;
6
+
7
+ constructor(error: Error) {
8
+ super(error?.message || String(error));
9
+ this.code = 'EXTRACT_ERROR';
10
+ }
11
+ }
12
+
13
+ class ComposeError extends Error {
14
+ code: string;
15
+
16
+ constructor(error: Error) {
17
+ super(error?.message || String(error));
18
+ this.code = 'COMPOSE_ERROR';
19
+ }
20
+ }
21
+
22
+ type Content = Parameters<typeof _extract>[0];
23
+
24
+ export function extract(content: Content, options: ExtractOptions) {
25
+ try {
26
+ const {units, skeleton} = _extract(content, options);
27
+
28
+ return {units, skeleton};
29
+ } catch (error: any) {
30
+ throw new ExtractError(error);
31
+ }
32
+ }
33
+
34
+ type Skeleton = Parameters<typeof _compose>[0];
35
+ type Xliff = Parameters<typeof _compose>[1];
36
+
37
+ export function compose(skeleton: Skeleton, xliff: Xliff, options: ComposeOptions) {
38
+ try {
39
+ return _compose(skeleton, xliff, options);
40
+ } catch (error: any) {
41
+ throw new ComposeError(error);
42
+ }
43
+ }
@@ -0,0 +1,41 @@
1
+ import {readFileSync} from 'fs';
2
+
3
+ type ServiceAccauntInfo = {
4
+ id: string;
5
+ service_account_id: string;
6
+ public_key: string;
7
+ private_key: string;
8
+ };
9
+
10
+ export type AuthInfo = ReturnType<typeof getYandexAuth>;
11
+
12
+ export function getYandexAuth(path: string) {
13
+ if (path.startsWith('y0_')) {
14
+ return {
15
+ oauthToken: path,
16
+ };
17
+ }
18
+
19
+ const data = readFileSync(path, 'utf8');
20
+ try {
21
+ const json = JSON.parse(data);
22
+
23
+ if (isServeseAccount(json)) {
24
+ return {
25
+ serviceAccountJson: {
26
+ serviceAccountId: json.service_account_id,
27
+ accessKeyId: json.id,
28
+ privateKey: json.private_key,
29
+ },
30
+ };
31
+ }
32
+ } catch {}
33
+
34
+ return {
35
+ oauthToken: data,
36
+ };
37
+ }
38
+
39
+ function isServeseAccount(json: any): json is ServiceAccauntInfo {
40
+ return 'private_key' in json;
41
+ }
package/src/index.ts CHANGED
@@ -5,14 +5,13 @@ import 'threads/register';
5
5
 
6
6
  import {MAIN_TIMER_ID} from './constants';
7
7
 
8
- import {build, publish, translate, xliff} from './cmd';
8
+ import {build, publish, translate} from './cmd';
9
9
 
10
10
  console.time(MAIN_TIMER_ID);
11
11
 
12
12
  yargs
13
13
  .command(build)
14
14
  .command(publish)
15
- .command(xliff)
16
15
  .command(translate)
17
16
  .option('config', {
18
17
  alias: 'c',
package/src/models.ts CHANGED
@@ -47,8 +47,6 @@ interface YfmConfig {
47
47
  buildDisabled: boolean;
48
48
  lintConfig: LintConfig;
49
49
  resources?: Resources;
50
- yandexCloudTranslateFolderId: string;
51
- yandexCloudTranslateGlossaryPairs: YandexCloudTranslateGlossaryPair[];
52
50
  needToSanitizeHtml: boolean;
53
51
  }
54
52
 
@@ -1,125 +0,0 @@
1
- import {ok} from 'assert';
2
- import {argvValidator} from '../../validator';
3
- import glob from 'glob';
4
-
5
- const {
6
- promises: {readFile, writeFile, mkdir},
7
- } = require('fs');
8
- import {dirname, join} from 'path';
9
-
10
- // @ts-ignore
11
- import {ComposeParams, compose as composeMD} from '@diplodoc/markdown-translation';
12
- import {Arguments, Argv} from 'yargs';
13
- import {eachLimit} from 'async';
14
-
15
- import {ArgvService} from '../../services';
16
-
17
- const command = 'compose';
18
-
19
- const description = 'compose xliff and skeleton into documentation';
20
-
21
- const compose = {command, description, handler, builder};
22
-
23
- const MAX_CONCURRENCY = 50;
24
-
25
- function builder<T>(argv: Argv<T>) {
26
- return argv
27
- .option('input', {
28
- alias: 'i',
29
- describe: 'input folder with xliff and skeleton files',
30
- type: 'string',
31
- })
32
- .option('output', {
33
- alias: 'o',
34
- describe: 'output folder where translated markdown will be stored',
35
- type: 'string',
36
- })
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);
47
- }
48
-
49
- type HandlerParams = {
50
- input: string;
51
- output: string;
52
- targetLanguage: string;
53
- exclude?: string[];
54
- useSource?: boolean;
55
- };
56
-
57
- /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
58
- async function handler(args: Arguments<HandlerParams>) {
59
- args = Object.assign({}, args.translate || {}, args);
60
- delete args.translate;
61
-
62
- ArgvService.init({
63
- ...args,
64
- });
65
-
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
- });
86
-
87
- ok(xliffs.length === skeletons.length, 'Inconsistent number of xliff and skeleton files.');
88
-
89
- const pipelineParameters = {input, output, targetLanguage, useSource};
90
- const configuredPipeline = pipeline(pipelineParameters);
91
-
92
- await eachLimit(skeletons, MAX_CONCURRENCY, configuredPipeline);
93
- }
94
-
95
- export type PipelineParameters = ComposeParams;
96
-
97
- function pipeline(params: PipelineParameters) {
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,
111
- skeletonPath,
112
- xliffPath,
113
- useSource,
114
- });
115
-
116
- const markdownPath = join(output, targetLanguage, fileName);
117
-
118
- await mkdir(dirname(markdownPath), {recursive: true});
119
- await writeFile(markdownPath, markdown);
120
- };
121
- }
122
-
123
- export {compose};
124
-
125
- export default {compose};
@@ -1,175 +0,0 @@
1
- import {ok} from 'assert';
2
- import glob from 'glob';
3
- import {argvValidator} from '../../validator';
4
-
5
- const {
6
- promises: {readFile, writeFile, mkdir},
7
- } = require('fs');
8
- import {basename, dirname, join, resolve} from 'path';
9
-
10
- // @ts-ignore
11
- import {ExtractParams, extract as extractMD} from '@diplodoc/markdown-translation';
12
- import {Arguments, Argv} from 'yargs';
13
- import {asyncify, eachLimit} from 'async';
14
-
15
- import {ArgvService} from '../../services';
16
-
17
- const command = 'extract';
18
-
19
- const description = 'extract xliff and skeleton from yfm documentation';
20
-
21
- const extract = {command, description, handler, builder};
22
-
23
- const MAX_CONCURRENCY = 50;
24
-
25
- function builder<T>(argv: Argv<T>) {
26
- return argv
27
- .option('source-language-locale', {
28
- alias: 'sll',
29
- describe: 'source language and locale',
30
- type: 'string',
31
- })
32
- .option('target-language-locale', {
33
- alias: 'tll',
34
- describe: 'target language and locale',
35
- type: 'string',
36
- })
37
- .option('input', {
38
- alias: 'i',
39
- describe: 'input folder with markdown files',
40
- type: 'string',
41
- })
42
- .option('output', {
43
- alias: 'o',
44
- describe: 'output folder to store xliff and skeleton files',
45
- type: 'string',
46
- })
47
- .check(argvValidator);
48
- }
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
-
63
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
- async function handler(args: Arguments<HandlerParams>) {
65
- args = Object.assign({}, args.translate || {}, args);
66
- delete args.translate;
67
-
68
- ArgvService.init({
69
- ...args,
70
- });
71
-
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('**/*');
112
- }
113
-
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));
126
- }
127
-
128
- function parseLanguageLocale(languageLocale: string) {
129
- const [language, locale] = languageLocale.split('-');
130
- if (!language?.length || !locale?.length) {
131
- throw new Error('invalid language-locale string');
132
- }
133
-
134
- return {language, locale};
135
- }
136
-
137
- export type PipelineParameters = {
138
- input: string;
139
- output: string;
140
- source: ExtractParams['source'];
141
- target: ExtractParams['target'];
142
- };
143
-
144
- function pipeline(params: PipelineParameters) {
145
- const {input, output, source, target} = params;
146
- const inputRoot = resolve(input, source.language);
147
- const outputRoot = resolve(output, target.language);
148
-
149
- return async (path: string) => {
150
- if (!path.endsWith('.md')) {
151
- return;
152
- }
153
-
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');
158
-
159
- await mkdir(dirname(xliffPath), {recursive: true});
160
-
161
- const {xliff, skeleton} = await extractMD({
162
- markdownPath: path,
163
- skeletonPath,
164
- markdown,
165
- source,
166
- target,
167
- });
168
-
169
- await Promise.all([writeFile(skeletonPath, skeleton), writeFile(xliffPath, xliff)]);
170
- };
171
- }
172
-
173
- export {extract};
174
-
175
- export default {extract};
@@ -1,30 +0,0 @@
1
- import {extract} from './extract';
2
- import {compose} from './compose';
3
-
4
- import {Argv} from 'yargs';
5
-
6
- const command = 'xliff';
7
-
8
- const description =
9
- 'extract xliff and skeleton from documentation files\ncompose xliff and skeleton into documentation';
10
-
11
- const xliff = {
12
- command,
13
- description,
14
- handler: () => {},
15
- builder,
16
- };
17
-
18
- function builder<T>(argv: Argv<T>) {
19
- return argv
20
- .command(extract)
21
- .command(compose)
22
- .demandCommand(
23
- 1,
24
- `provide one of the folowing ${command} commands: ${extract.command}, ${compose.command}`,
25
- );
26
- }
27
-
28
- export {xliff};
29
-
30
- export default {xliff};