@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.
- package/CHANGELOG.md +785 -0
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/README.ru.md +63 -0
- package/build/app.client.css +47 -0
- package/build/app.client.js +3 -0
- package/build/index.js +3993 -0
- package/build/index.js.map +7 -0
- package/build/lib.js +3374 -0
- package/build/lib.js.map +7 -0
- package/build/linter.js +1265 -0
- package/build/linter.js.map +7 -0
- package/package.json +126 -0
- package/src/cmd/build/index.ts +304 -0
- package/src/cmd/index.ts +4 -0
- package/src/cmd/publish/index.ts +92 -0
- package/src/cmd/publish/upload.ts +61 -0
- package/src/cmd/translate/index.ts +261 -0
- package/src/cmd/xliff/compose.ts +222 -0
- package/src/cmd/xliff/extract.ts +237 -0
- package/src/cmd/xliff/index.ts +27 -0
- package/src/constants.ts +122 -0
- package/src/globals.d.ts +1 -0
- package/src/index.ts +54 -0
- package/src/models.ts +249 -0
- package/src/packages/credentials/index.ts +1 -0
- package/src/packages/credentials/yandex-oauth.ts +42 -0
- package/src/resolvers/index.ts +3 -0
- package/src/resolvers/lintPage.ts +119 -0
- package/src/resolvers/md2html.ts +142 -0
- package/src/resolvers/md2md.ts +147 -0
- package/src/services/argv.ts +38 -0
- package/src/services/authors.ts +64 -0
- package/src/services/contributors.ts +104 -0
- package/src/services/includers/batteries/common.ts +34 -0
- package/src/services/includers/batteries/generic.ts +130 -0
- package/src/services/includers/batteries/index.ts +3 -0
- package/src/services/includers/batteries/sourcedocs.ts +33 -0
- package/src/services/includers/batteries/unarchive.ts +97 -0
- package/src/services/includers/index.ts +157 -0
- package/src/services/index.ts +6 -0
- package/src/services/leading.ts +88 -0
- package/src/services/metadata.ts +249 -0
- package/src/services/plugins.ts +76 -0
- package/src/services/preset.ts +55 -0
- package/src/services/tocs.ts +401 -0
- package/src/services/utils.ts +151 -0
- package/src/steps/index.ts +6 -0
- package/src/steps/processAssets.ts +36 -0
- package/src/steps/processExcludedFiles.ts +47 -0
- package/src/steps/processLinter.ts +100 -0
- package/src/steps/processLogs.ts +18 -0
- package/src/steps/processMapFile.ts +35 -0
- package/src/steps/processPages.ts +312 -0
- package/src/steps/processServiceFiles.ts +95 -0
- package/src/steps/publishFilesToS3.ts +47 -0
- package/src/utils/file.ts +17 -0
- package/src/utils/glob.ts +14 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +42 -0
- package/src/utils/markup.ts +125 -0
- package/src/utils/path.ts +24 -0
- package/src/utils/presets.ts +20 -0
- package/src/utils/singlePage.ts +228 -0
- package/src/utils/toc.ts +87 -0
- package/src/utils/url.ts +3 -0
- package/src/utils/worker.ts +10 -0
- package/src/validator.ts +150 -0
- package/src/vcs-connector/client/github.ts +52 -0
- package/src/vcs-connector/connector-models.ts +76 -0
- package/src/vcs-connector/connector-validator.ts +114 -0
- package/src/vcs-connector/github.ts +333 -0
- package/src/vcs-connector/index.ts +15 -0
- package/src/workers/linter/index.ts +62 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {mkdirSync, createReadStream, createWriteStream} from 'fs';
|
|
2
|
+
import {join, dirname} from 'path';
|
|
3
|
+
import {extract, Headers} from 'tar-stream';
|
|
4
|
+
|
|
5
|
+
import type {PassThrough} from 'stream';
|
|
6
|
+
|
|
7
|
+
import {IncluderFunctionParams} from '../../../models';
|
|
8
|
+
|
|
9
|
+
const name = 'unarchive';
|
|
10
|
+
|
|
11
|
+
class UnarchiveIncluderError extends Error {
|
|
12
|
+
path: string;
|
|
13
|
+
|
|
14
|
+
constructor(message: string, path: string) {
|
|
15
|
+
super(message);
|
|
16
|
+
|
|
17
|
+
this.name = 'UnarchiveIncluderError';
|
|
18
|
+
this.path = path;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function pipeline(readPath: string, writeBasePath: string): Promise<void> {
|
|
23
|
+
return new Promise((res, rej) => {
|
|
24
|
+
const reader = createReadStream(readPath);
|
|
25
|
+
|
|
26
|
+
reader.on('error', (err: Error) => {
|
|
27
|
+
rej(err);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const extractor = extract();
|
|
31
|
+
|
|
32
|
+
extractor.on('error', (err: Error) => {
|
|
33
|
+
rej(err);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
mkdirSync(writeBasePath, {recursive: true});
|
|
37
|
+
|
|
38
|
+
extractor.on('entry', (header: Headers, stream: PassThrough, next: Function) => {
|
|
39
|
+
const {type, name} = header;
|
|
40
|
+
|
|
41
|
+
const writePath = join(writeBasePath, name);
|
|
42
|
+
|
|
43
|
+
const writeDirPath = type === 'directory' ? writePath : dirname(writePath);
|
|
44
|
+
|
|
45
|
+
mkdirSync(writeDirPath, {recursive: true});
|
|
46
|
+
|
|
47
|
+
if (type !== 'directory') {
|
|
48
|
+
const writer = createWriteStream(writePath, {flags: 'w'});
|
|
49
|
+
|
|
50
|
+
writer.on('error', (err) => {
|
|
51
|
+
rej(err);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
stream.pipe(writer);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
stream.on('end', () => {
|
|
58
|
+
next();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
stream.resume();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
reader.pipe(extractor).on('finish', () => {
|
|
65
|
+
res();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type Params = {
|
|
71
|
+
input: string;
|
|
72
|
+
output: string;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
async function includerFunction(params: IncluderFunctionParams<Params>) {
|
|
76
|
+
const {readBasePath, writeBasePath, tocPath, passedParams: {input, output}, index} = params;
|
|
77
|
+
|
|
78
|
+
if (!input?.length || !output?.length) {
|
|
79
|
+
throw new UnarchiveIncluderError('provide includer with input parameter', tocPath);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const contentPath = index === 0
|
|
83
|
+
? join(writeBasePath, input)
|
|
84
|
+
: join(readBasePath, input);
|
|
85
|
+
|
|
86
|
+
const writePath = join(writeBasePath, output);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
await pipeline(contentPath, writePath);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
throw new UnarchiveIncluderError(err.toString(), tocPath);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export {name, includerFunction};
|
|
96
|
+
|
|
97
|
+
export default {name, includerFunction};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {join} from 'path';
|
|
2
|
+
|
|
3
|
+
import {isObject} from 'lodash';
|
|
4
|
+
|
|
5
|
+
import {ArgvService} from '../index';
|
|
6
|
+
import {IncludeMode} from '../../constants';
|
|
7
|
+
import {generic, sourcedocs, unarchive} from './batteries';
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
YfmPreset,
|
|
11
|
+
Includer,
|
|
12
|
+
YfmToc,
|
|
13
|
+
YfmTocInclude,
|
|
14
|
+
YfmTocIncluder,
|
|
15
|
+
YfmTocIncluders,
|
|
16
|
+
} from '../../models';
|
|
17
|
+
|
|
18
|
+
const includersUsage = `include:
|
|
19
|
+
path: <path-where-to-include>
|
|
20
|
+
includers:
|
|
21
|
+
- name: <includer-name-0>
|
|
22
|
+
<includer-parameter>: <value-for-includer-parameter>
|
|
23
|
+
- name: <includer-name-1>
|
|
24
|
+
<includer-parameter>: <value-for-includer-parameter>
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
type IncludersMap = Record<string, Includer>;
|
|
28
|
+
|
|
29
|
+
let includersMap!: IncludersMap;
|
|
30
|
+
|
|
31
|
+
class IncludersError extends Error {
|
|
32
|
+
path: string;
|
|
33
|
+
|
|
34
|
+
constructor(message: string, path: string) {
|
|
35
|
+
super(message);
|
|
36
|
+
|
|
37
|
+
this.name = 'IncludersError';
|
|
38
|
+
this.path = path;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function init(custom: Includer[] = []) {
|
|
43
|
+
if (includersMap) { return; }
|
|
44
|
+
|
|
45
|
+
includersMap = {generic, sourcedocs, unarchive};
|
|
46
|
+
|
|
47
|
+
for (const includer of custom) {
|
|
48
|
+
includersMap[includer.name] = includer;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function applyIncluders(path: string, item: YfmToc, vars: YfmPreset) {
|
|
53
|
+
if (!item.include?.includers) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!includeValid(item.include)) {
|
|
58
|
+
throw new IncludersError('include doesn\'t comply with includers standard', path);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// normalize include mode (includers support link mode only)
|
|
62
|
+
item.include.mode = IncludeMode.LINK;
|
|
63
|
+
|
|
64
|
+
const {status, message} = includersValid(item.include.includers);
|
|
65
|
+
if (!status) {
|
|
66
|
+
throw new IncludersError(message ?? '', path);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let index = 0;
|
|
70
|
+
for (const {name, ...rest} of item.include.includers) {
|
|
71
|
+
const includer = getIncluder(name);
|
|
72
|
+
const passedParams = {...rest};
|
|
73
|
+
|
|
74
|
+
await applyIncluder({path, item, includer, passedParams, index, vars});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// contract to be fullfilled by the includer:
|
|
78
|
+
// provide builder generated toc.yaml
|
|
79
|
+
item.include.path = join(item.include.path, 'toc.yaml');
|
|
80
|
+
index++;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function includeValid(include: YfmTocInclude) {
|
|
84
|
+
return (include.mode === IncludeMode.LINK || !include.mode) && include.path?.length;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function includersValid(includers: YfmTocIncluders) {
|
|
88
|
+
for (const includer of includers) {
|
|
89
|
+
const {status, message} = includerValid(includer);
|
|
90
|
+
|
|
91
|
+
if (!status) {
|
|
92
|
+
return {status, message};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {status: true};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function includerValid(includer: YfmTocIncluder) {
|
|
100
|
+
if (isObject(includer)) {
|
|
101
|
+
if (typeof includer.name !== 'string') {
|
|
102
|
+
return {
|
|
103
|
+
status: false,
|
|
104
|
+
message: 'use string in the includer.name to specify includers name',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (includerExists(includer)) {
|
|
109
|
+
return {status: true};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {status: false, message: `includer ${includer.name} not implemented`};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
status: false,
|
|
117
|
+
message: `use appropriate includers format:\n${includersUsage}`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getIncluder(includerName: string) {
|
|
122
|
+
return includersMap[includerName];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function includerExists(includer: YfmTocIncluder) {
|
|
126
|
+
return includersMap[includer.name as keyof typeof includersMap];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type applyIncluderParams = {
|
|
130
|
+
path: string;
|
|
131
|
+
item: YfmToc;
|
|
132
|
+
includer: Includer;
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
|
+
passedParams: Record<string, any>;
|
|
135
|
+
index: number;
|
|
136
|
+
vars: YfmPreset;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
async function applyIncluder(args: applyIncluderParams) {
|
|
140
|
+
const {rootInput: readBasePath, input: writeBasePath} = ArgvService.getConfig();
|
|
141
|
+
|
|
142
|
+
const {path, item, includer, passedParams, index, vars} = args;
|
|
143
|
+
|
|
144
|
+
const params = {
|
|
145
|
+
tocPath: path,
|
|
146
|
+
passedParams,
|
|
147
|
+
index,
|
|
148
|
+
item,
|
|
149
|
+
readBasePath,
|
|
150
|
+
writeBasePath,
|
|
151
|
+
vars,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return await includer.includerFunction(params);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export {init, applyIncluders, IncludersError};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export {default as TocService} from './tocs';
|
|
2
|
+
export {default as PresetService} from './preset';
|
|
3
|
+
export {default as ArgvService} from './argv';
|
|
4
|
+
export {default as LeadingService} from './leading';
|
|
5
|
+
export * as PluginService from './plugins';
|
|
6
|
+
export * as Includers from './includers';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {dirname, resolve} from 'path';
|
|
2
|
+
import {readFileSync, writeFileSync} from 'fs';
|
|
3
|
+
import {dump, load} from 'js-yaml';
|
|
4
|
+
import log from '@doc-tools/transform/lib/log';
|
|
5
|
+
|
|
6
|
+
import {ArgvService, PresetService} from './index';
|
|
7
|
+
import {LeadingPage} from '../models';
|
|
8
|
+
import {filterTextItems, filterFiles, firstFilterTextItems, liquidFields, liquidField} from './utils';
|
|
9
|
+
|
|
10
|
+
function filterFile(path: string) {
|
|
11
|
+
const {
|
|
12
|
+
input: inputFolderPath,
|
|
13
|
+
vars,
|
|
14
|
+
} = ArgvService.getConfig();
|
|
15
|
+
|
|
16
|
+
const pathToDir = dirname(path);
|
|
17
|
+
const filePath = resolve(inputFolderPath, path);
|
|
18
|
+
const content = readFileSync(filePath, 'utf8');
|
|
19
|
+
const parsedIndex = load(content) as LeadingPage;
|
|
20
|
+
|
|
21
|
+
const combinedVars = {
|
|
22
|
+
...PresetService.get(pathToDir),
|
|
23
|
+
...vars,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/* Should remove all links with false expressions */
|
|
27
|
+
try {
|
|
28
|
+
const title = firstFilterTextItems(
|
|
29
|
+
parsedIndex.title,
|
|
30
|
+
combinedVars,
|
|
31
|
+
{resolveConditions: true},
|
|
32
|
+
);
|
|
33
|
+
parsedIndex.title = liquidField(title, combinedVars, path);
|
|
34
|
+
|
|
35
|
+
const description = filterTextItems(
|
|
36
|
+
parsedIndex.description,
|
|
37
|
+
combinedVars,
|
|
38
|
+
{resolveConditions: true},
|
|
39
|
+
);
|
|
40
|
+
parsedIndex.description = liquidFields(description, combinedVars, path);
|
|
41
|
+
|
|
42
|
+
if (parsedIndex.meta?.title) {
|
|
43
|
+
const metaTitle = firstFilterTextItems(
|
|
44
|
+
parsedIndex.meta.title,
|
|
45
|
+
combinedVars,
|
|
46
|
+
{resolveConditions: true},
|
|
47
|
+
);
|
|
48
|
+
parsedIndex.meta.title = liquidField(metaTitle, combinedVars, path);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (parsedIndex.meta?.description) {
|
|
52
|
+
const metaDescription = firstFilterTextItems(
|
|
53
|
+
parsedIndex.meta.description,
|
|
54
|
+
combinedVars,
|
|
55
|
+
{resolveConditions: true},
|
|
56
|
+
);
|
|
57
|
+
parsedIndex.meta.description = liquidField(metaDescription, combinedVars, path);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (parsedIndex.nav) {
|
|
61
|
+
const navTitle = firstFilterTextItems(
|
|
62
|
+
parsedIndex.nav.title,
|
|
63
|
+
combinedVars,
|
|
64
|
+
{resolveConditions: true},
|
|
65
|
+
);
|
|
66
|
+
parsedIndex.nav.title = liquidField(navTitle, combinedVars, path);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
parsedIndex.links = filterFiles(parsedIndex.links, 'links', combinedVars, {resolveConditions: true});
|
|
70
|
+
|
|
71
|
+
parsedIndex.links.forEach((link) => {
|
|
72
|
+
if (link.title) {
|
|
73
|
+
link.title = liquidField(link.title, combinedVars, path);
|
|
74
|
+
}
|
|
75
|
+
if (link.description) {
|
|
76
|
+
link.description = liquidField(link.description, combinedVars, path);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
writeFileSync(filePath, dump(parsedIndex));
|
|
81
|
+
} catch (error) {
|
|
82
|
+
log.error(`Error while filtering index file: ${path}. Error message: ${error}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default {
|
|
87
|
+
filterFile,
|
|
88
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {dump} from 'js-yaml';
|
|
2
|
+
|
|
3
|
+
import {VCSConnector} from '../vcs-connector/connector-models';
|
|
4
|
+
import {Metadata, MetaDataOptions, Resources} from '../models';
|
|
5
|
+
import {
|
|
6
|
+
getAuthorDetails,
|
|
7
|
+
updateAuthorMetadataStringByAuthorLogin,
|
|
8
|
+
updateAuthorMetadataStringByFilePath,
|
|
9
|
+
} from './authors';
|
|
10
|
+
import {getFileContributorsMetadata, getFileContributorsString} from './contributors';
|
|
11
|
+
import {isObject} from './utils';
|
|
12
|
+
import {сarriage} from '../utils';
|
|
13
|
+
import {metadataBorder, REGEXP_AUTHOR} from '../constants';
|
|
14
|
+
import {dirname, relative, resolve} from 'path';
|
|
15
|
+
import {ArgvService} from './index';
|
|
16
|
+
|
|
17
|
+
async function getContentWithUpdatedMetadata(
|
|
18
|
+
fileContent: string,
|
|
19
|
+
options?: MetaDataOptions,
|
|
20
|
+
systemVars?: unknown,
|
|
21
|
+
): Promise<string> {
|
|
22
|
+
let result;
|
|
23
|
+
|
|
24
|
+
result = getContentWithUpdatedStaticMetadata({
|
|
25
|
+
fileContent,
|
|
26
|
+
sourcePath: options?.fileData?.sourcePath,
|
|
27
|
+
addSystemMeta: options?.addSystemMeta,
|
|
28
|
+
addSourcePath: options?.addSourcePath,
|
|
29
|
+
resources: options?.resources,
|
|
30
|
+
systemVars,
|
|
31
|
+
});
|
|
32
|
+
result = await getContentWithUpdatedDynamicMetadata(result, options);
|
|
33
|
+
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getContentWithUpdatedStaticMetadata({
|
|
38
|
+
fileContent, sourcePath, addSystemMeta, addSourcePath, resources, systemVars,
|
|
39
|
+
}: {
|
|
40
|
+
fileContent: string;
|
|
41
|
+
sourcePath?: string;
|
|
42
|
+
addSystemMeta?: boolean;
|
|
43
|
+
addSourcePath?: boolean;
|
|
44
|
+
resources?: Resources;
|
|
45
|
+
systemVars?: unknown;
|
|
46
|
+
}): string {
|
|
47
|
+
const newMetadatas: string[] = [];
|
|
48
|
+
|
|
49
|
+
if ((!addSystemMeta || !systemVars) && !addSourcePath && !resources) {
|
|
50
|
+
return fileContent;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const matches = matchMetadata(fileContent);
|
|
54
|
+
|
|
55
|
+
if (addSystemMeta && systemVars && isObject(systemVars)) {
|
|
56
|
+
newMetadatas.push(getSystemVarsMetadataString(systemVars));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (resources) {
|
|
60
|
+
newMetadatas.push(dump(resources));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (addSourcePath && sourcePath) {
|
|
64
|
+
const sourcePathMetadataString = `sourcePath: ${sourcePath}`;
|
|
65
|
+
newMetadatas.push(sourcePathMetadataString);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (matches && matches.length > 0) {
|
|
69
|
+
const [, fileMetadata, , fileMainContent] = matches;
|
|
70
|
+
|
|
71
|
+
return `${getUpdatedMetadataString(newMetadatas, fileMetadata)}${fileMainContent}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return `${getUpdatedMetadataString(newMetadatas)}${fileContent}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function getContentWithUpdatedDynamicMetadata(
|
|
78
|
+
fileContent: string,
|
|
79
|
+
options?: MetaDataOptions,
|
|
80
|
+
): Promise<string> {
|
|
81
|
+
if (!options || !options?.isContributorsEnabled) {
|
|
82
|
+
return fileContent;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let fileMetadata: string | undefined, fileMainContent: string | undefined;
|
|
86
|
+
const matches = matchMetadata(fileContent);
|
|
87
|
+
if (matches && matches.length > 0) {
|
|
88
|
+
const [, matchedFileMetadata, , matchedFileMainContent] = matches;
|
|
89
|
+
fileMetadata = matchedFileMetadata;
|
|
90
|
+
fileMainContent = matchedFileMainContent;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const newMetadatas: string[] = [];
|
|
94
|
+
|
|
95
|
+
const {isContributorsEnabled} = options;
|
|
96
|
+
|
|
97
|
+
if (isContributorsEnabled) {
|
|
98
|
+
const contributorsMetaData = await getContributorsMetadataString(options, fileContent);
|
|
99
|
+
if (contributorsMetaData) {
|
|
100
|
+
newMetadatas.push(contributorsMetaData);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let authorMetadata = '';
|
|
104
|
+
if (fileMetadata) {
|
|
105
|
+
const matchAuthor = fileMetadata.match(REGEXP_AUTHOR);
|
|
106
|
+
if (matchAuthor) {
|
|
107
|
+
const matchedAuthor = matchAuthor[0];
|
|
108
|
+
authorMetadata = await updateAuthorMetadataStringByAuthorLogin(matchedAuthor, options.vcsConnector);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!authorMetadata) {
|
|
113
|
+
const {fileData: {tmpInputFilePath, inputFolderPathLength}} = options;
|
|
114
|
+
const relativeFilePath = tmpInputFilePath.substring(inputFolderPathLength);
|
|
115
|
+
authorMetadata = await updateAuthorMetadataStringByFilePath(relativeFilePath, options.vcsConnector);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (authorMetadata) {
|
|
119
|
+
newMetadatas.push(`author: ${authorMetadata}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (fileMetadata && fileMainContent) {
|
|
124
|
+
let updatedFileMetadata = fileMetadata;
|
|
125
|
+
const matchAuthor = fileMetadata.match(REGEXP_AUTHOR);
|
|
126
|
+
|
|
127
|
+
const isNewMetadataIncludesAuthor = newMetadatas.some((item) => /^author: /.test(item));
|
|
128
|
+
if (matchAuthor && isNewMetadataIncludesAuthor) {
|
|
129
|
+
updatedFileMetadata = updatedFileMetadata.replace(`author: ${matchAuthor[0]}`, '');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return `${getUpdatedMetadataString(newMetadatas, updatedFileMetadata)}${fileMainContent}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return `${getUpdatedMetadataString(newMetadatas)}${fileContent}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function matchMetadata(fileContent: string) {
|
|
139
|
+
if (!fileContent.startsWith('---')) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Search by format:
|
|
144
|
+
// ---
|
|
145
|
+
// metaName1: metaValue1
|
|
146
|
+
// metaName2: meta value2
|
|
147
|
+
// incorrectMetadata
|
|
148
|
+
// ---
|
|
149
|
+
const regexpMetadata = '(?<=-{3}\\r?\\n)((.*\\r?\\n)*?)(?=-{3}\\r?\\n)';
|
|
150
|
+
// Search by format:
|
|
151
|
+
// ---
|
|
152
|
+
// main content 123
|
|
153
|
+
const regexpFileContent = '-{3}((.*[\r?\n]*)*)';
|
|
154
|
+
|
|
155
|
+
const regexpParseFileContent = new RegExp(`${regexpMetadata}${regexpFileContent}`, 'gm');
|
|
156
|
+
|
|
157
|
+
return regexpParseFileContent.exec(fileContent);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function getContributorsMetadataString(
|
|
161
|
+
options: MetaDataOptions, fileContent: string,
|
|
162
|
+
): Promise<string | undefined> {
|
|
163
|
+
const {isContributorsEnabled, vcsConnector, fileData} = options;
|
|
164
|
+
|
|
165
|
+
if (isContributorsEnabled && vcsConnector) {
|
|
166
|
+
const updatedFileData = {
|
|
167
|
+
...fileData,
|
|
168
|
+
fileContent,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return getFileContributorsMetadata(updatedFileData, vcsConnector);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function getUpdatedMetadataString(newMetadatas: string[], defaultMetadata = ''): string {
|
|
178
|
+
const newMetadata = newMetadatas.join(сarriage) + (newMetadatas.length ? сarriage : '');
|
|
179
|
+
const preparedDefaultMetadata = defaultMetadata.trimRight();
|
|
180
|
+
const defaultMetadataСarriage = preparedDefaultMetadata ? сarriage : '';
|
|
181
|
+
const updatedMetadata = `${preparedDefaultMetadata}${defaultMetadataСarriage}${newMetadata}`;
|
|
182
|
+
|
|
183
|
+
return `${metadataBorder}${сarriage}${updatedMetadata}${metadataBorder}${defaultMetadata.length ? '' : сarriage}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function getUpdatedMetadata(options: MetaDataOptions, fileContent: string, meta?: Metadata): Promise<Metadata> {
|
|
187
|
+
const {vcsConnector} = options;
|
|
188
|
+
|
|
189
|
+
const newMetadata: Metadata = {
|
|
190
|
+
contributors: await getContributorsMetadata(options, fileContent),
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
if (!meta) {
|
|
194
|
+
return newMetadata;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const updatedAuthor = await getAuthorMetadata(meta as Metadata, vcsConnector);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
...meta,
|
|
201
|
+
...newMetadata,
|
|
202
|
+
author: updatedAuthor,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function getContributorsMetadata(options: MetaDataOptions, fileContent: string): Promise<string> {
|
|
207
|
+
const {isContributorsEnabled, vcsConnector, fileData} = options;
|
|
208
|
+
|
|
209
|
+
if (isContributorsEnabled && vcsConnector) {
|
|
210
|
+
const updatedFileData = {
|
|
211
|
+
...fileData,
|
|
212
|
+
fileContent,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return getFileContributorsString(updatedFileData, vcsConnector);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return JSON.stringify([]);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function getAuthorMetadata(meta: Metadata, vcsConnector?: VCSConnector): Promise<string | null> {
|
|
222
|
+
if (meta.author && vcsConnector) {
|
|
223
|
+
const updatedAuthor = await getAuthorDetails(vcsConnector, meta.author);
|
|
224
|
+
|
|
225
|
+
return updatedAuthor;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function getSystemVarsMetadataString(systemVars: object) {
|
|
232
|
+
return `__system: ${JSON.stringify(systemVars)}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
function getAssetsPublicPath(filePath: string) {
|
|
237
|
+
const {input} = ArgvService.getConfig();
|
|
238
|
+
const path: string = resolve(input, filePath);
|
|
239
|
+
|
|
240
|
+
/* Relative path from folder of .md file to root of user' output folder */
|
|
241
|
+
return relative(dirname(path), resolve(input));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export {
|
|
245
|
+
getContentWithUpdatedMetadata,
|
|
246
|
+
getContentWithUpdatedStaticMetadata,
|
|
247
|
+
getUpdatedMetadata,
|
|
248
|
+
getAssetsPublicPath,
|
|
249
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {LintRule, LintConfig} from '@doc-tools/transform/lib/yfmlint';
|
|
2
|
+
|
|
3
|
+
import {PluginOptions, Plugin, CollectionOfPluginsFunction} from '../models';
|
|
4
|
+
import {YFM_PLUGINS} from '../constants';
|
|
5
|
+
|
|
6
|
+
let plugins: Function[] | Plugin[];
|
|
7
|
+
let collectionOfPlugins: CollectionOfPluginsFunction;
|
|
8
|
+
|
|
9
|
+
export function setPlugins(): void {
|
|
10
|
+
plugins = getAllPlugins();
|
|
11
|
+
collectionOfPlugins = makeCollectOfPlugins();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getPlugins() {
|
|
15
|
+
return plugins;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getCollectOfPlugins(): CollectionOfPluginsFunction {
|
|
19
|
+
return collectionOfPlugins;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function makeCollectOfPlugins(): CollectionOfPluginsFunction {
|
|
23
|
+
const pluginsWithCollect = (plugins as Plugin[]).filter((plugin: Plugin) => {
|
|
24
|
+
return typeof plugin.collect === 'function';
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return (output: string, options: PluginOptions) => {
|
|
28
|
+
let collectsOutput = output;
|
|
29
|
+
|
|
30
|
+
pluginsWithCollect.forEach((plugin: Plugin) => {
|
|
31
|
+
const collectOutput = plugin.collect(collectsOutput, options);
|
|
32
|
+
|
|
33
|
+
collectsOutput = typeof collectOutput === 'string' ? collectOutput : collectsOutput;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return collectsOutput;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getAllPlugins(): Function[] {
|
|
41
|
+
const customPlugins = getCustomPlugins();
|
|
42
|
+
return [...YFM_PLUGINS, ...customPlugins];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getCustomPlugins(): Function[] {
|
|
46
|
+
try {
|
|
47
|
+
const customPlugins = require(require.resolve('./plugins'));
|
|
48
|
+
return Array.isArray(customPlugins) ? customPlugins : [];
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getHeadContent(): string {
|
|
55
|
+
try {
|
|
56
|
+
return require(require.resolve('./head-content.js'));
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getCustomLintRules(): LintRule[] {
|
|
63
|
+
try {
|
|
64
|
+
return require(require.resolve('./lint-rules'));
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function getDefaultLintConfig(): LintConfig | undefined {
|
|
71
|
+
try {
|
|
72
|
+
return require(require.resolve('./default-lint-config'));
|
|
73
|
+
} catch (e) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|