@diplodoc/cli 4.7.0 → 4.9.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/assets/app.css +4 -0
- package/assets/app.js +2 -0
- package/assets/react.js +3 -0
- package/{build/app.client.css → assets/vendor.css} +857 -19
- package/assets/vendor.js +3 -0
- package/build/index.js +317 -233
- package/build/index.js.map +4 -4
- package/build/linter.js +88 -112
- package/build/linter.js.map +4 -4
- package/package.json +8 -23
- package/src/cmd/publish/index.ts +16 -4
- package/src/constants.ts +4 -6
- package/src/models.ts +1 -0
- package/src/resolvers/md2html.ts +4 -0
- package/src/services/contributors.ts +46 -2
- package/src/services/metadata.ts +42 -2
- package/src/services/tocs.ts +10 -0
- package/src/steps/processAssets.ts +3 -5
- package/src/utils/markup.ts +18 -7
- package/src/utils/path.ts +0 -8
- package/src/utils/singlePage.ts +10 -5
- package/src/vcs-connector/connector-models.ts +2 -0
- package/src/vcs-connector/github.ts +42 -1
- package/build/app.client.js +0 -3
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"author": "Yandex Data UI Team <data-ui@yandex-team.ru>",
|
|
4
4
|
"description": "Make documentation using yfm-docs in Markdown and HTML formats",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.9.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git@github.com:diplodoc-platform/cli.git"
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"main": "build/index.js",
|
|
15
15
|
"files": [
|
|
16
16
|
"build",
|
|
17
|
+
"assets",
|
|
17
18
|
"src"
|
|
18
19
|
],
|
|
19
20
|
"scripts": {
|
|
@@ -21,20 +22,21 @@
|
|
|
21
22
|
"start": "node build/index.js",
|
|
22
23
|
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
|
|
23
24
|
"lint:fix": "npm run lint -- --fix",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
24
26
|
"prepublishOnly": "npm run lint && npm run build",
|
|
25
27
|
"git:head": "git checkout master && git pull"
|
|
26
28
|
},
|
|
27
29
|
"engines": {
|
|
28
|
-
"node": ">=18.*"
|
|
29
|
-
"npm:": ">=9.*"
|
|
30
|
+
"node": ">=18.*"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
33
|
"@aws-sdk/client-s3": "^3.369.0",
|
|
33
|
-
"@diplodoc/client": "^
|
|
34
|
+
"@diplodoc/client": "^2.0.0",
|
|
35
|
+
"@diplodoc/latex-extension": "^1.0.3",
|
|
34
36
|
"@diplodoc/markdown-translation": "^1.0.4",
|
|
35
37
|
"@diplodoc/mermaid-extension": "^1.2.1",
|
|
36
38
|
"@diplodoc/openapi-extension": "^1.4.10",
|
|
37
|
-
"@diplodoc/transform": "^4.
|
|
39
|
+
"@diplodoc/transform": "^4.8.1",
|
|
38
40
|
"@octokit/core": "4.2.4",
|
|
39
41
|
"@yandex-cloud/nodejs-sdk": "^2.2.2",
|
|
40
42
|
"ajv": "^8.11.0",
|
|
@@ -92,20 +94,6 @@
|
|
|
92
94
|
"git add"
|
|
93
95
|
]
|
|
94
96
|
},
|
|
95
|
-
"pkg": {
|
|
96
|
-
"scripts": [
|
|
97
|
-
"build/index.js",
|
|
98
|
-
"build/linter.js",
|
|
99
|
-
"build/plugins/*.js",
|
|
100
|
-
"build/lint-rules/*.js",
|
|
101
|
-
"build/default-lint-config.js",
|
|
102
|
-
"build/head-content.js"
|
|
103
|
-
],
|
|
104
|
-
"assets": [
|
|
105
|
-
"build/app.client.js",
|
|
106
|
-
"build/app.client.css"
|
|
107
|
-
]
|
|
108
|
-
},
|
|
109
97
|
"keywords": [
|
|
110
98
|
"markdown",
|
|
111
99
|
"yandex",
|
|
@@ -115,8 +103,5 @@
|
|
|
115
103
|
"tool",
|
|
116
104
|
"tools",
|
|
117
105
|
"generator"
|
|
118
|
-
]
|
|
119
|
-
"overrides": {
|
|
120
|
-
"markdown-it": "^13.0.2"
|
|
121
|
-
}
|
|
106
|
+
]
|
|
122
107
|
}
|
package/src/cmd/publish/index.ts
CHANGED
|
@@ -67,19 +67,31 @@ function builder<T>(argv: Argv<T>) {
|
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
type Args = {
|
|
71
|
+
input: string;
|
|
72
|
+
endpoint: string;
|
|
73
|
+
region: string;
|
|
74
|
+
bucket: string;
|
|
75
|
+
prefix: string;
|
|
76
|
+
ignore: string[];
|
|
77
|
+
accessKeyId: string;
|
|
78
|
+
secretAccessKey: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
70
81
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
71
|
-
async function handler(args: Arguments<
|
|
82
|
+
async function handler(args: Arguments<Args>) {
|
|
72
83
|
ArgvService.init({
|
|
73
84
|
...args,
|
|
74
85
|
});
|
|
75
86
|
|
|
76
|
-
const
|
|
87
|
+
const config = ArgvService.getConfig() as unknown as Args;
|
|
88
|
+
const {input, endpoint, bucket, prefix} = config;
|
|
77
89
|
|
|
78
90
|
logger.info('', `Upload artifacts from ${input} to ${join(endpoint, bucket, prefix)}`);
|
|
79
91
|
|
|
80
92
|
try {
|
|
81
|
-
await upload(
|
|
82
|
-
} catch (error) {
|
|
93
|
+
await upload(config);
|
|
94
|
+
} catch (error: any) {
|
|
83
95
|
logger.error('', error.message);
|
|
84
96
|
}
|
|
85
97
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {resolve} from 'path';
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const notes = require('@diplodoc/transform/lib/plugins/notes');
|
|
4
4
|
const anchors = require('@diplodoc/transform/lib/plugins/anchors');
|
|
@@ -19,16 +19,15 @@ const term = require('@diplodoc/transform/lib/plugins/term');
|
|
|
19
19
|
const blockAnchor = require('@diplodoc/transform/lib/plugins/block-anchor');
|
|
20
20
|
const changelog = require('@diplodoc/transform/lib/plugins/changelog');
|
|
21
21
|
const mermaid = require('@diplodoc/mermaid-extension');
|
|
22
|
+
const latex = require('@diplodoc/latex-extension');
|
|
22
23
|
const openapi = require('@diplodoc/openapi-extension');
|
|
23
24
|
|
|
24
25
|
includes.collect = require('@diplodoc/transform/lib/plugins/includes/collect');
|
|
25
26
|
images.collect = require('@diplodoc/transform/lib/plugins/images/collect');
|
|
26
27
|
changelog.collect = require('@diplodoc/transform/lib/plugins/changelog/collect');
|
|
27
28
|
|
|
28
|
-
export const
|
|
29
|
+
export const ASSETS_FOLDER = resolve(__dirname, '../assets');
|
|
29
30
|
export const BUNDLE_FOLDER = '_bundle';
|
|
30
|
-
export const BUNDLE_JS_FILENAME = 'app.client.js';
|
|
31
|
-
export const BUNDLE_CSS_FILENAME = 'app.client.css';
|
|
32
31
|
export const TMP_INPUT_FOLDER = '.tmp_input';
|
|
33
32
|
export const TMP_OUTPUT_FOLDER = '.tmp_output';
|
|
34
33
|
export const MAIN_TIMER_ID = 'Build time';
|
|
@@ -68,8 +67,6 @@ export enum ResourceType {
|
|
|
68
67
|
script = 'script',
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
export const BUILD_FOLDER_PATH = dirname(require.resolve('@diplodoc/client'));
|
|
72
|
-
|
|
73
70
|
export const YFM_PLUGINS = [
|
|
74
71
|
meta,
|
|
75
72
|
deflist,
|
|
@@ -89,6 +86,7 @@ export const YFM_PLUGINS = [
|
|
|
89
86
|
term,
|
|
90
87
|
openapi.transform(),
|
|
91
88
|
mermaid.transform(),
|
|
89
|
+
latex.transform(),
|
|
92
90
|
changelog,
|
|
93
91
|
blockAnchor,
|
|
94
92
|
];
|
package/src/models.ts
CHANGED
|
@@ -19,6 +19,7 @@ export type NestedContributorsForPathFunction = (
|
|
|
19
19
|
) => void;
|
|
20
20
|
export type UserByLoginFunction = (login: string) => Promise<Contributor | null>;
|
|
21
21
|
export type CollectionOfPluginsFunction = (output: string, options: PluginOptions) => string;
|
|
22
|
+
export type GetModifiedTimeByPathFunction = (filepath: string) => number | undefined;
|
|
22
23
|
|
|
23
24
|
interface YfmConfig {
|
|
24
25
|
varsPreset: VarsPreset;
|
package/src/resolvers/md2html.ts
CHANGED
|
@@ -50,6 +50,10 @@ export async function resolveMd2HTML(options: ResolverOptions): Promise<ResolveM
|
|
|
50
50
|
const transformFn: Function = FileTransformer[fileExtension];
|
|
51
51
|
const {result} = transformFn(content, {path: inputPath});
|
|
52
52
|
|
|
53
|
+
if (result.html) {
|
|
54
|
+
result.html = result.html.replace(/\n+/gm, '');
|
|
55
|
+
}
|
|
56
|
+
|
|
53
57
|
const updatedMetadata =
|
|
54
58
|
metadata && metadata.isContributorsEnabled
|
|
55
59
|
? await getUpdatedMetadata(metadata, content, result?.meta)
|
|
@@ -94,7 +94,10 @@ async function getContributorsForNestedFiles(
|
|
|
94
94
|
return Object.assign({}, ...includesContributors);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
function getRelativeIncludeFilePaths(
|
|
97
|
+
function getRelativeIncludeFilePaths(
|
|
98
|
+
fileData: Pick<FileData, 'tmpInputFilePath'>,
|
|
99
|
+
includeContents: string[],
|
|
100
|
+
): Set<string> {
|
|
98
101
|
const {tmpInputFilePath} = fileData;
|
|
99
102
|
const relativeIncludeFilePaths: Set<string> = new Set();
|
|
100
103
|
|
|
@@ -115,4 +118,45 @@ function getRelativeIncludeFilePaths(fileData: FileData, includeContents: string
|
|
|
115
118
|
return relativeIncludeFilePaths;
|
|
116
119
|
}
|
|
117
120
|
|
|
118
|
-
|
|
121
|
+
async function getFileIncludes(
|
|
122
|
+
fileData: Pick<FileData, 'fileContent' | 'tmpInputFilePath' | 'inputFolderPathLength'>,
|
|
123
|
+
) {
|
|
124
|
+
const {fileContent, tmpInputFilePath, inputFolderPathLength} = fileData;
|
|
125
|
+
|
|
126
|
+
const results = new Set<string>();
|
|
127
|
+
|
|
128
|
+
const includeContents = fileContent.match(REGEXP_INCLUDE_CONTENTS);
|
|
129
|
+
if (!includeContents || includeContents.length === 0) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const relativeIncludeFilePaths: Set<string> = getRelativeIncludeFilePaths(
|
|
133
|
+
{tmpInputFilePath},
|
|
134
|
+
includeContents,
|
|
135
|
+
);
|
|
136
|
+
for (const relativeIncludeFilePath of relativeIncludeFilePaths.values()) {
|
|
137
|
+
const relativeFilePath = relativeIncludeFilePath.substring(inputFolderPathLength + 1);
|
|
138
|
+
if (results.has(relativeFilePath)) continue;
|
|
139
|
+
results.add(relativeFilePath);
|
|
140
|
+
|
|
141
|
+
let contentIncludeFile: string;
|
|
142
|
+
try {
|
|
143
|
+
contentIncludeFile = await readFile(relativeIncludeFilePath, 'utf8');
|
|
144
|
+
} catch (err) {
|
|
145
|
+
if (err.code === 'ENOENT') {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const includedPaths = await getFileIncludes({
|
|
152
|
+
inputFolderPathLength,
|
|
153
|
+
fileContent: contentIncludeFile,
|
|
154
|
+
tmpInputFilePath: relativeIncludeFilePath,
|
|
155
|
+
});
|
|
156
|
+
includedPaths.forEach((path) => results.add(path));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return Array.from(results.values());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export {getFileContributorsMetadata, getFileContributorsString, getFileIncludes};
|
package/src/services/metadata.ts
CHANGED
|
@@ -7,12 +7,16 @@ import {
|
|
|
7
7
|
updateAuthorMetadataStringByAuthorLogin,
|
|
8
8
|
updateAuthorMetadataStringByFilePath,
|
|
9
9
|
} from './authors';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
getFileContributorsMetadata,
|
|
12
|
+
getFileContributorsString,
|
|
13
|
+
getFileIncludes,
|
|
14
|
+
} from './contributors';
|
|
11
15
|
import {isObject} from './utils';
|
|
12
16
|
import {сarriage} from '../utils';
|
|
13
17
|
import {REGEXP_AUTHOR, metadataBorder} from '../constants';
|
|
14
18
|
import {dirname, relative, resolve} from 'path';
|
|
15
|
-
import {ArgvService} from './index';
|
|
19
|
+
import {ArgvService, TocService} from './index';
|
|
16
20
|
|
|
17
21
|
async function getContentWithUpdatedMetadata(
|
|
18
22
|
fileContent: string,
|
|
@@ -105,6 +109,11 @@ async function getContentWithUpdatedDynamicMetadata(
|
|
|
105
109
|
newMetadatas.push(contributorsMetaData);
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
const mtimeMetadata = await getModifiedTimeMetadataString(options, fileContent);
|
|
113
|
+
if (mtimeMetadata) {
|
|
114
|
+
newMetadatas.push(mtimeMetadata);
|
|
115
|
+
}
|
|
116
|
+
|
|
108
117
|
let authorMetadata = '';
|
|
109
118
|
if (fileMetadata) {
|
|
110
119
|
const matchAuthor = fileMetadata.match(REGEXP_AUTHOR);
|
|
@@ -188,6 +197,37 @@ async function getContributorsMetadataString(
|
|
|
188
197
|
return undefined;
|
|
189
198
|
}
|
|
190
199
|
|
|
200
|
+
async function getModifiedTimeMetadataString(options: MetaDataOptions, fileContent: string) {
|
|
201
|
+
const {isContributorsEnabled, vcsConnector, fileData} = options;
|
|
202
|
+
|
|
203
|
+
const {tmpInputFilePath, inputFolderPathLength} = fileData;
|
|
204
|
+
|
|
205
|
+
const relativeFilePath = tmpInputFilePath.substring(inputFolderPathLength + 1);
|
|
206
|
+
|
|
207
|
+
if (!isContributorsEnabled || !vcsConnector) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const includedFiles = await getFileIncludes({...fileData, fileContent});
|
|
212
|
+
includedFiles.push(relativeFilePath);
|
|
213
|
+
|
|
214
|
+
const tocCopyFileMap = TocService.getCopyFileMap();
|
|
215
|
+
|
|
216
|
+
const mtimeList = includedFiles
|
|
217
|
+
.map((path) => {
|
|
218
|
+
const mappedPath = tocCopyFileMap.get(path) || path;
|
|
219
|
+
return vcsConnector.getModifiedTimeByPath(mappedPath);
|
|
220
|
+
})
|
|
221
|
+
.filter((v) => typeof v === 'number') as number[];
|
|
222
|
+
|
|
223
|
+
if (mtimeList.length) {
|
|
224
|
+
const mtime = Math.max(...mtimeList);
|
|
225
|
+
return `updatedAt: ${new Date(mtime * 1000).toISOString()}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
|
|
191
231
|
function getUpdatedMetadataString(newMetadatas: string[], defaultMetadata = ''): string {
|
|
192
232
|
const newMetadata = newMetadatas.join(сarriage) + (newMetadatas.length ? сarriage : '');
|
|
193
233
|
const preparedDefaultMetadata = defaultMetadata.trimRight();
|
package/src/services/tocs.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface TocServiceData {
|
|
|
24
24
|
const storage: TocServiceData['storage'] = new Map();
|
|
25
25
|
let navigationPaths: TocServiceData['navigationPaths'] = [];
|
|
26
26
|
const includedTocPaths: TocServiceData['includedTocPaths'] = new Set();
|
|
27
|
+
const tocFileCopyMap = new Map<string, string>();
|
|
27
28
|
|
|
28
29
|
async function add(path: string) {
|
|
29
30
|
const {
|
|
@@ -207,6 +208,10 @@ function _copyTocDir(tocPath: string, destDir: string) {
|
|
|
207
208
|
} else {
|
|
208
209
|
shell.cp(from, to);
|
|
209
210
|
}
|
|
211
|
+
|
|
212
|
+
const relFrom = relative(inputFolderPath, from);
|
|
213
|
+
const relTo = relative(inputFolderPath, to);
|
|
214
|
+
tocFileCopyMap.set(relTo, relFrom);
|
|
210
215
|
});
|
|
211
216
|
}
|
|
212
217
|
|
|
@@ -396,6 +401,10 @@ function setNavigationPaths(paths: TocServiceData['navigationPaths']) {
|
|
|
396
401
|
navigationPaths = paths;
|
|
397
402
|
}
|
|
398
403
|
|
|
404
|
+
function getCopyFileMap() {
|
|
405
|
+
return tocFileCopyMap;
|
|
406
|
+
}
|
|
407
|
+
|
|
399
408
|
export default {
|
|
400
409
|
add,
|
|
401
410
|
getForPath,
|
|
@@ -403,4 +412,5 @@ export default {
|
|
|
403
412
|
getTocDir,
|
|
404
413
|
getIncludedTocPaths,
|
|
405
414
|
setNavigationPaths,
|
|
415
|
+
getCopyFileMap,
|
|
406
416
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import walkSync from 'walk-sync';
|
|
2
2
|
import shell from 'shelljs';
|
|
3
3
|
|
|
4
|
-
import client from '../../scripts/client';
|
|
5
4
|
import {ArgvService} from '../services';
|
|
6
5
|
import {copyFiles} from '../utils';
|
|
7
6
|
|
|
7
|
+
import {ASSETS_FOLDER} from '../constants';
|
|
8
|
+
|
|
8
9
|
/**
|
|
9
10
|
* Processes assets files (everything except .yaml and .md files)
|
|
10
11
|
* @param {string} outputBundlePath
|
|
@@ -23,8 +24,5 @@ export function processAssets(outputBundlePath: string) {
|
|
|
23
24
|
|
|
24
25
|
/* Copy js bundle to user' output folder */
|
|
25
26
|
shell.mkdir('-p', outputBundlePath);
|
|
26
|
-
|
|
27
|
-
for (const path of Object.values(client.dst)) {
|
|
28
|
-
shell.cp(path, outputBundlePath);
|
|
29
|
-
}
|
|
27
|
+
shell.cp(ASSETS_FOLDER + '/*', outputBundlePath);
|
|
30
28
|
}
|
package/src/utils/markup.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import {join} from 'path';
|
|
1
2
|
import {platform} from 'process';
|
|
2
3
|
|
|
3
4
|
import {CUSTOM_STYLE, Platforms, ResourceType} from '../constants';
|
|
4
5
|
import {Resources, SinglePageResult} from '../models';
|
|
5
6
|
import {ArgvService, PluginService} from '../services';
|
|
6
7
|
import {preprocessPageHtmlForSinglePage} from './singlePage';
|
|
7
|
-
|
|
8
|
-
import
|
|
8
|
+
|
|
9
|
+
import {DocInnerProps, DocPageData, render} from '@diplodoc/client/ssr';
|
|
10
|
+
import manifest from '@diplodoc/client/manifest';
|
|
11
|
+
|
|
12
|
+
const dst = (bundlePath: string) => (target: string) => join(bundlePath, target);
|
|
9
13
|
|
|
10
14
|
export interface TitleMeta {
|
|
11
15
|
title?: string;
|
|
@@ -44,7 +48,10 @@ export function generateStaticMarkup(
|
|
|
44
48
|
height: 100vh;
|
|
45
49
|
}
|
|
46
50
|
</style>
|
|
47
|
-
|
|
51
|
+
${manifest.css
|
|
52
|
+
.map(dst(pathToBundle))
|
|
53
|
+
.map((src: string) => `<link type="text/css" rel="stylesheet" href="${src}" />`)
|
|
54
|
+
.join('\n')}
|
|
48
55
|
${PluginService.getHeadContent()}
|
|
49
56
|
${resources}
|
|
50
57
|
</head>
|
|
@@ -54,9 +61,13 @@ export function generateStaticMarkup(
|
|
|
54
61
|
window.STATIC_CONTENT = ${staticContent}
|
|
55
62
|
window.__DATA__ = ${JSON.stringify(props)};
|
|
56
63
|
</script>
|
|
57
|
-
|
|
58
|
-
pathToBundle
|
|
59
|
-
|
|
64
|
+
${manifest.js
|
|
65
|
+
.map(dst(pathToBundle))
|
|
66
|
+
.map(
|
|
67
|
+
(src: string) =>
|
|
68
|
+
`<script type="application/javascript" src="${src}"></script>`,
|
|
69
|
+
)
|
|
70
|
+
.join('\n')}
|
|
60
71
|
</body>
|
|
61
72
|
</html>
|
|
62
73
|
`;
|
|
@@ -126,7 +137,7 @@ export function joinSinglePageResults(
|
|
|
126
137
|
root: string,
|
|
127
138
|
tocDir: string,
|
|
128
139
|
): string {
|
|
129
|
-
const delimeter =
|
|
140
|
+
const delimeter = `<hr class="yfm-page__delimeter">`;
|
|
130
141
|
return singlePageResults
|
|
131
142
|
.filter(({content}) => content)
|
|
132
143
|
.map(({content, path, title}) =>
|
package/src/utils/path.ts
CHANGED
|
@@ -14,11 +14,3 @@ export function convertBackSlashToSlash(path: string): string {
|
|
|
14
14
|
|
|
15
15
|
return path;
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
export function convertSlashToWindowsBackSlashes(path: string): string {
|
|
19
|
-
if (process.platform === Platforms.WINDOWS) {
|
|
20
|
-
return path.replace(/\//g, '\\\\');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return path;
|
|
24
|
-
}
|
package/src/utils/singlePage.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {parse} from 'node-html-parser';
|
|
|
3
3
|
import {relative, resolve, sep} from 'path';
|
|
4
4
|
import {resolveRelativePath} from '@diplodoc/transform/lib/utilsFS';
|
|
5
5
|
import url from 'url';
|
|
6
|
-
import
|
|
6
|
+
import escape from 'lodash/escapeRegExp';
|
|
7
7
|
|
|
8
8
|
import {isExternalHref} from './url';
|
|
9
9
|
|
|
@@ -22,6 +22,11 @@ interface PreprocessSinglePageOptions {
|
|
|
22
22
|
|
|
23
23
|
const HEADERS_SELECTOR = 'h1, h2, h3, h4, h5, h6';
|
|
24
24
|
|
|
25
|
+
function toUrl(path: string) {
|
|
26
|
+
// replace windows backslashes
|
|
27
|
+
return path.replace(new RegExp(escape(sep), 'g'), '/');
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
function getNewNode(options: ModifyNode): HTMLElement | null {
|
|
26
31
|
const {rawTagName, innerHTML, attrEntries} = options;
|
|
27
32
|
|
|
@@ -92,7 +97,7 @@ export function replaceLinks(rootEl: HTMLElement, options: PreprocessSinglePageO
|
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
node.setAttribute('href', preparedHref);
|
|
100
|
+
node.setAttribute('href', toUrl(preparedHref));
|
|
96
101
|
});
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -110,7 +115,7 @@ export function replaceImages(rootEl: HTMLElement, options: PreprocessSinglePage
|
|
|
110
115
|
const linkFullPath = resolveRelativePath(resolvedPath, href);
|
|
111
116
|
const preparedHref = relative(tocDir, linkFullPath);
|
|
112
117
|
|
|
113
|
-
node.setAttribute('src', preparedHref);
|
|
118
|
+
node.setAttribute('src', toUrl(preparedHref));
|
|
114
119
|
});
|
|
115
120
|
}
|
|
116
121
|
|
|
@@ -129,7 +134,7 @@ function prepareAnchorAttrs(node: HTMLElement, pageId: string) {
|
|
|
129
134
|
for (const [name, value] of Object.entries(node.attributes)) {
|
|
130
135
|
const preparedValue = prepareAnchorAttr(name, value, pageId);
|
|
131
136
|
|
|
132
|
-
node.setAttribute(name, preparedValue);
|
|
137
|
+
node.setAttribute(name, toUrl(preparedValue));
|
|
133
138
|
}
|
|
134
139
|
}
|
|
135
140
|
|
|
@@ -197,7 +202,7 @@ export function getSinglePageAnchorId(args: {
|
|
|
197
202
|
resultAnchor = resultAnchor
|
|
198
203
|
.replace(root, '')
|
|
199
204
|
.replace(/\.(md|ya?ml|html)$/i, '')
|
|
200
|
-
.replace(new RegExp(
|
|
205
|
+
.replace(new RegExp(escape(sep), 'gi'), '_');
|
|
201
206
|
|
|
202
207
|
if (hash) {
|
|
203
208
|
resultAnchor = resultAnchor + '_' + hash.slice(1);
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
Contributors,
|
|
3
3
|
ContributorsByPathFunction,
|
|
4
4
|
ExternalAuthorByPathFunction,
|
|
5
|
+
GetModifiedTimeByPathFunction,
|
|
5
6
|
NestedContributorsForPathFunction,
|
|
6
7
|
UserByLoginFunction,
|
|
7
8
|
} from '../models';
|
|
@@ -31,6 +32,7 @@ export interface VCSConnector {
|
|
|
31
32
|
addNestedContributorsForPath: NestedContributorsForPathFunction;
|
|
32
33
|
getContributorsByPath: ContributorsByPathFunction;
|
|
33
34
|
getUserByLogin: UserByLoginFunction;
|
|
35
|
+
getModifiedTimeByPath: GetModifiedTimeByPathFunction;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export interface VCSConnectorConfig {
|
|
@@ -33,9 +33,10 @@ const authorByPath: Map<string, Contributor | null> = new Map();
|
|
|
33
33
|
const contributorsByPath: Map<string, FileContributors> = new Map();
|
|
34
34
|
const contributorsData: Map<string, Contributor | null> = new Map();
|
|
35
35
|
const loginUserMap: Map<string, Contributor | null> = new Map();
|
|
36
|
+
const pathMTime = new Map<string, number>();
|
|
36
37
|
|
|
37
38
|
async function getGitHubVCSConnector(): Promise<VCSConnector | undefined> {
|
|
38
|
-
const {contributors} = ArgvService.getConfig();
|
|
39
|
+
const {contributors, rootInput} = ArgvService.getConfig();
|
|
39
40
|
|
|
40
41
|
const httpClientByToken = getHttpClientByToken();
|
|
41
42
|
if (!httpClientByToken) {
|
|
@@ -49,6 +50,7 @@ async function getGitHubVCSConnector(): Promise<VCSConnector | undefined> {
|
|
|
49
50
|
authorByPath.get(path) ?? null;
|
|
50
51
|
|
|
51
52
|
if (contributors) {
|
|
53
|
+
await getFilesMTime(rootInput, pathMTime);
|
|
52
54
|
await getAllContributorsTocFiles(httpClientByToken);
|
|
53
55
|
addNestedContributorsForPath = (path: string, nestedContributors: Contributors) =>
|
|
54
56
|
addNestedContributorsForPathFunction(path, nestedContributors);
|
|
@@ -60,6 +62,7 @@ async function getGitHubVCSConnector(): Promise<VCSConnector | undefined> {
|
|
|
60
62
|
addNestedContributorsForPath,
|
|
61
63
|
getContributorsByPath,
|
|
62
64
|
getUserByLogin: (login: string) => getUserByLogin(httpClientByToken, login),
|
|
65
|
+
getModifiedTimeByPath: (filename: string) => pathMTime.get(filename),
|
|
63
66
|
};
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -364,4 +367,42 @@ function shouldAuthorBeIgnored({email, name}: ShouldAuthorBeIgnoredArgs) {
|
|
|
364
367
|
return false;
|
|
365
368
|
}
|
|
366
369
|
|
|
370
|
+
async function getFilesMTime(repoDir: string, pathMTime: Map<string, number>) {
|
|
371
|
+
const timeFiles = await simpleGit({
|
|
372
|
+
baseDir: repoDir,
|
|
373
|
+
}).raw(
|
|
374
|
+
'log',
|
|
375
|
+
'--reverse',
|
|
376
|
+
'--before=now',
|
|
377
|
+
'--diff-filter=ADMR',
|
|
378
|
+
'--pretty=format:%ct',
|
|
379
|
+
'--name-status',
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
const parts = timeFiles.split(/\n\n/);
|
|
383
|
+
parts.forEach((part) => {
|
|
384
|
+
const lines = part.trim().split(/\n/);
|
|
385
|
+
const committerDate = lines.shift();
|
|
386
|
+
const unixtime = Number(committerDate);
|
|
387
|
+
lines.forEach((line) => {
|
|
388
|
+
const [status, from, to] = line.split(/\t/);
|
|
389
|
+
switch (status[0]) {
|
|
390
|
+
case 'R': {
|
|
391
|
+
pathMTime.delete(from);
|
|
392
|
+
pathMTime.set(to, unixtime);
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
case 'D': {
|
|
396
|
+
pathMTime.delete(from);
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
default: {
|
|
400
|
+
pathMTime.set(from, unixtime);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
return pathMTime;
|
|
406
|
+
}
|
|
407
|
+
|
|
367
408
|
export default getGitHubVCSConnector;
|