@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/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.7.0",
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": "^1.2.0",
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.7.3",
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
  }
@@ -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<any>) {
82
+ async function handler(args: Arguments<Args>) {
72
83
  ArgvService.init({
73
84
  ...args,
74
85
  });
75
86
 
76
- const {input, endpoint, bucket, prefix} = ArgvService.getConfig();
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(ArgvService.getConfig());
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 {dirname} from 'path';
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 BUILD_FOLDER = 'build';
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;
@@ -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(fileData: FileData, includeContents: string[]): Set<string> {
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
- export {getFileContributorsMetadata, getFileContributorsString};
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};
@@ -7,12 +7,16 @@ import {
7
7
  updateAuthorMetadataStringByAuthorLogin,
8
8
  updateAuthorMetadataStringByFilePath,
9
9
  } from './authors';
10
- import {getFileContributorsMetadata, getFileContributorsString} from './contributors';
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();
@@ -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
  }
@@ -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
- import {DocInnerProps, DocPageData, render} from '@diplodoc/client';
8
- import client from '../../scripts/client';
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
- <link type="text/css" rel="stylesheet" href="${client.bundle.css(pathToBundle)}" />
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
- <script type="application/javascript" src="${client.bundle.js(
58
- pathToBundle,
59
- )}"></script>
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 = `${сarriage}${сarriage}<hr class="yfm-page__delimeter">${сarriage}${сarriage}`;
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
- }
@@ -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 _ from 'lodash';
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(_.escapeRegExp(sep), 'gi'), '_');
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;