@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,55 @@
|
|
|
1
|
+
import {dirname, normalize} from 'path';
|
|
2
|
+
|
|
3
|
+
import {DocPreset, YfmPreset} from '../models';
|
|
4
|
+
|
|
5
|
+
export type PresetStorage = Map<string, YfmPreset>;
|
|
6
|
+
|
|
7
|
+
let presetStorage: PresetStorage = new Map();
|
|
8
|
+
|
|
9
|
+
function add(parsedPreset: DocPreset, path: string, varsPreset: string) {
|
|
10
|
+
const combinedValues: YfmPreset = {
|
|
11
|
+
...parsedPreset.default || {},
|
|
12
|
+
...parsedPreset[varsPreset] || {},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const key = dirname(normalize(path));
|
|
16
|
+
presetStorage.set(key, combinedValues);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function get(path: string): YfmPreset {
|
|
20
|
+
let combinedValues: YfmPreset = {};
|
|
21
|
+
let localPath = normalize(path);
|
|
22
|
+
|
|
23
|
+
while (localPath !== '.') {
|
|
24
|
+
const presetValues: YfmPreset = presetStorage.get(localPath) || {};
|
|
25
|
+
localPath = dirname(localPath);
|
|
26
|
+
|
|
27
|
+
combinedValues = {
|
|
28
|
+
...presetValues,
|
|
29
|
+
...combinedValues,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Add root' presets
|
|
34
|
+
combinedValues = {
|
|
35
|
+
...presetStorage.get('.'),
|
|
36
|
+
...combinedValues,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return combinedValues;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getPresetStorage(): Map<string, YfmPreset> {
|
|
43
|
+
return presetStorage;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function setPresetStorage(preset: Map<string, YfmPreset>): void {
|
|
47
|
+
presetStorage = preset;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default {
|
|
51
|
+
add,
|
|
52
|
+
get,
|
|
53
|
+
getPresetStorage,
|
|
54
|
+
setPresetStorage,
|
|
55
|
+
};
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import {dirname, extname, join, parse, resolve, relative, normalize, sep} from 'path';
|
|
2
|
+
import {copyFileSync, readFileSync, writeFileSync, existsSync} from 'fs';
|
|
3
|
+
import {load, dump} from 'js-yaml';
|
|
4
|
+
import shell from 'shelljs';
|
|
5
|
+
import walkSync from 'walk-sync';
|
|
6
|
+
import liquid from '@doc-tools/transform/lib/liquid';
|
|
7
|
+
import log from '@doc-tools/transform/lib/log';
|
|
8
|
+
import {bold} from 'chalk';
|
|
9
|
+
|
|
10
|
+
import {ArgvService, PresetService} from './index';
|
|
11
|
+
import {getContentWithUpdatedStaticMetadata} from './metadata';
|
|
12
|
+
import {YfmToc} from '../models';
|
|
13
|
+
import {Stage, IncludeMode} from '../constants';
|
|
14
|
+
import {isExternalHref, logger} from '../utils';
|
|
15
|
+
import {filterFiles, firstFilterTextItems, liquidField} from './utils';
|
|
16
|
+
import {applyIncluders, IncludersError} from './includers';
|
|
17
|
+
|
|
18
|
+
export interface TocServiceData {
|
|
19
|
+
storage: Map<string, YfmToc>;
|
|
20
|
+
navigationPaths: string[];
|
|
21
|
+
includedTocPaths: Set<string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const storage: TocServiceData['storage'] = new Map();
|
|
25
|
+
let navigationPaths: TocServiceData['navigationPaths'] = [];
|
|
26
|
+
const includedTocPaths: TocServiceData['includedTocPaths'] = new Set();
|
|
27
|
+
|
|
28
|
+
async function add(path: string) {
|
|
29
|
+
const {
|
|
30
|
+
input: inputFolderPath,
|
|
31
|
+
output: outputFolderPath,
|
|
32
|
+
outputFormat,
|
|
33
|
+
ignoreStage,
|
|
34
|
+
vars,
|
|
35
|
+
} = ArgvService.getConfig();
|
|
36
|
+
|
|
37
|
+
const pathToDir = dirname(path);
|
|
38
|
+
const content = readFileSync(resolve(inputFolderPath, path), 'utf8');
|
|
39
|
+
const parsedToc = load(content) as YfmToc;
|
|
40
|
+
|
|
41
|
+
// Should ignore toc with specified stage.
|
|
42
|
+
if (parsedToc.stage === ignoreStage) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const combinedVars = {
|
|
47
|
+
...PresetService.get(pathToDir),
|
|
48
|
+
...vars,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (parsedToc.title) {
|
|
52
|
+
parsedToc.title = firstFilterTextItems(
|
|
53
|
+
parsedToc.title,
|
|
54
|
+
combinedVars,
|
|
55
|
+
{resolveConditions: true},
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof parsedToc.title === 'string') {
|
|
60
|
+
parsedToc.title = liquidField(parsedToc.title, combinedVars, path);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
parsedToc.items = await processTocItems(
|
|
64
|
+
path,
|
|
65
|
+
parsedToc.items,
|
|
66
|
+
join(inputFolderPath, pathToDir),
|
|
67
|
+
resolve(inputFolderPath),
|
|
68
|
+
combinedVars,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
/* Store parsed toc for .md output format */
|
|
72
|
+
storage.set(path, parsedToc);
|
|
73
|
+
|
|
74
|
+
/* Store path to toc file to handle relative paths in navigation */
|
|
75
|
+
parsedToc.base = pathToDir;
|
|
76
|
+
|
|
77
|
+
if (outputFormat === 'md') {
|
|
78
|
+
/* Should copy resolved and filtered toc to output folder */
|
|
79
|
+
const outputPath = resolve(outputFolderPath, path);
|
|
80
|
+
const outputToc = dump(parsedToc);
|
|
81
|
+
shell.mkdir('-p', dirname(outputPath));
|
|
82
|
+
writeFileSync(outputPath, outputToc);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
prepareNavigationPaths(parsedToc, pathToDir);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function processTocItems(
|
|
89
|
+
path: string,
|
|
90
|
+
items: YfmToc[],
|
|
91
|
+
tocDir: string,
|
|
92
|
+
sourcesDir: string,
|
|
93
|
+
vars: Record<string, string>,
|
|
94
|
+
) {
|
|
95
|
+
const {
|
|
96
|
+
resolveConditions,
|
|
97
|
+
removeHiddenTocItems,
|
|
98
|
+
} = ArgvService.getConfig();
|
|
99
|
+
|
|
100
|
+
let preparedItems = items;
|
|
101
|
+
|
|
102
|
+
/* Should remove all links with false expressions */
|
|
103
|
+
if (resolveConditions || removeHiddenTocItems) {
|
|
104
|
+
try {
|
|
105
|
+
preparedItems = filterFiles(items, 'items', vars, {
|
|
106
|
+
resolveConditions,
|
|
107
|
+
removeHiddenTocItems,
|
|
108
|
+
});
|
|
109
|
+
} catch (error) {
|
|
110
|
+
log.error(`Error while filtering toc file: ${path}. Error message: ${error}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Should resolve all includes */
|
|
115
|
+
return _replaceIncludes(path, preparedItems, tocDir, sourcesDir, vars);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getForPath(path: string): YfmToc|undefined {
|
|
119
|
+
return storage.get(path);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getNavigationPaths(): string[] {
|
|
123
|
+
return [...navigationPaths];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function getIncludedTocPaths(): string[] {
|
|
127
|
+
return [...includedTocPaths];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function prepareNavigationPaths(parsedToc: YfmToc, dirPath: string) {
|
|
131
|
+
function processItems(items: YfmToc[], pathToDir: string) {
|
|
132
|
+
items.forEach((item) => {
|
|
133
|
+
if (!parsedToc.singlePage && item.items) {
|
|
134
|
+
const preparedSubItems = item.items.map(((yfmToc: YfmToc, index: number) => {
|
|
135
|
+
// Generate personal id for each navigation item
|
|
136
|
+
yfmToc.id = `${yfmToc.name}-${index}-${Math.random()}`;
|
|
137
|
+
return yfmToc;
|
|
138
|
+
}));
|
|
139
|
+
processItems(preparedSubItems, pathToDir);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (item.href && !isExternalHref(item.href)) {
|
|
143
|
+
const href = join(pathToDir, item.href);
|
|
144
|
+
storage.set(href, parsedToc);
|
|
145
|
+
|
|
146
|
+
const navigationPath = _normalizeHref(href);
|
|
147
|
+
navigationPaths.push(navigationPath);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
processItems([parsedToc], dirPath);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Should normalize hrefs. MD and YAML files will be ignored.
|
|
157
|
+
* @param href
|
|
158
|
+
* @return {string}
|
|
159
|
+
* @example instance-groups/create-with-coi/ -> instance-groups/create-with-coi/index.yaml
|
|
160
|
+
* @example instance-groups/create-with-coi -> instance-groups/create-with-coi.md
|
|
161
|
+
* @private
|
|
162
|
+
*/
|
|
163
|
+
function _normalizeHref(href: string): string {
|
|
164
|
+
const preparedHref = normalize(href);
|
|
165
|
+
|
|
166
|
+
if (preparedHref.endsWith('.md') || preparedHref.endsWith('.yaml')) {
|
|
167
|
+
return preparedHref;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (preparedHref.endsWith(sep)) {
|
|
171
|
+
return `${preparedHref}index.yaml`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return `${preparedHref}.md`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Copies all files of include toc to original dir.
|
|
179
|
+
* @param tocPath
|
|
180
|
+
* @param destDir
|
|
181
|
+
* @return
|
|
182
|
+
* @private
|
|
183
|
+
*/
|
|
184
|
+
function _copyTocDir(tocPath: string, destDir: string) {
|
|
185
|
+
const {input: inputFolderPath} = ArgvService.getConfig();
|
|
186
|
+
|
|
187
|
+
const {dir: tocDir} = parse(tocPath);
|
|
188
|
+
const files: string[] = walkSync(tocDir, {
|
|
189
|
+
globs: ['**/*.*'],
|
|
190
|
+
ignore: ['**/toc.yaml'],
|
|
191
|
+
directories: false,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
files.forEach((relPath) => {
|
|
195
|
+
const from = resolve(tocDir, relPath);
|
|
196
|
+
const to = resolve(destDir, relPath);
|
|
197
|
+
const fileExtension = extname(relPath);
|
|
198
|
+
const isMdFile = fileExtension === '.md';
|
|
199
|
+
|
|
200
|
+
shell.mkdir('-p', parse(to).dir);
|
|
201
|
+
|
|
202
|
+
if (isMdFile) {
|
|
203
|
+
const fileContent = readFileSync(from, 'utf8');
|
|
204
|
+
const sourcePath = relative(inputFolderPath, from);
|
|
205
|
+
const updatedFileContent = getContentWithUpdatedStaticMetadata({
|
|
206
|
+
fileContent,
|
|
207
|
+
sourcePath,
|
|
208
|
+
addSourcePath: true,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
writeFileSync(to, updatedFileContent);
|
|
212
|
+
} else {
|
|
213
|
+
copyFileSync(from, to);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Make hrefs relative to the main toc in the included toc.
|
|
220
|
+
* @param items
|
|
221
|
+
* @param includeTocDir
|
|
222
|
+
* @param tocDir
|
|
223
|
+
* @return
|
|
224
|
+
* @private
|
|
225
|
+
*/
|
|
226
|
+
function _replaceIncludesHrefs(items: YfmToc[], includeTocDir: string, tocDir: string): YfmToc[] {
|
|
227
|
+
return items.reduce((acc, tocItem) => {
|
|
228
|
+
if (tocItem.href) {
|
|
229
|
+
tocItem.href = relative(tocDir, resolve(includeTocDir, tocItem.href));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (tocItem.items) {
|
|
233
|
+
tocItem.items = _replaceIncludesHrefs(tocItem.items, includeTocDir, tocDir);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (tocItem.include) {
|
|
237
|
+
const {path} = tocItem.include;
|
|
238
|
+
tocItem.include.path = relative(tocDir, resolve(includeTocDir, path));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return acc.concat(tocItem);
|
|
242
|
+
}, [] as YfmToc[]);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Liquid substitutions in toc file.
|
|
247
|
+
* @param input
|
|
248
|
+
* @param vars
|
|
249
|
+
* @param path
|
|
250
|
+
* @return {string}
|
|
251
|
+
* @private
|
|
252
|
+
*/
|
|
253
|
+
function _liquidSubstitutions(input: string, vars: Record<string, string>, path: string) {
|
|
254
|
+
const {outputFormat, applyPresets} = ArgvService.getConfig();
|
|
255
|
+
if (outputFormat === 'md' && !applyPresets) {
|
|
256
|
+
return input;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return liquid(input, vars, path, {
|
|
260
|
+
conditions: false,
|
|
261
|
+
substitutions: true,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function addIncludeTocPath(includeTocPath: string) {
|
|
266
|
+
includedTocPaths.add(includeTocPath);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Replaces include fields in toc file by resolved toc.
|
|
271
|
+
* @param path
|
|
272
|
+
* @param items
|
|
273
|
+
* @param tocDir
|
|
274
|
+
* @param sourcesDir
|
|
275
|
+
* @param vars
|
|
276
|
+
* @return
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
async function _replaceIncludes(
|
|
280
|
+
path: string,
|
|
281
|
+
items: YfmToc[],
|
|
282
|
+
tocDir: string,
|
|
283
|
+
sourcesDir: string,
|
|
284
|
+
vars: Record<string, string>,
|
|
285
|
+
): Promise<YfmToc[]> {
|
|
286
|
+
const result: YfmToc[] = [];
|
|
287
|
+
|
|
288
|
+
for (const item of items) {
|
|
289
|
+
let includedInlineItems: YfmToc[] | null = null;
|
|
290
|
+
|
|
291
|
+
if (item.name) {
|
|
292
|
+
const tocPath = join(tocDir, 'toc.yaml');
|
|
293
|
+
|
|
294
|
+
item.name = _liquidSubstitutions(item.name, vars, tocPath);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
await applyIncluders(path, item, vars);
|
|
299
|
+
} catch (err) {
|
|
300
|
+
if (err instanceof Error || err instanceof IncludersError) {
|
|
301
|
+
const message = err.toString();
|
|
302
|
+
|
|
303
|
+
const file = err instanceof IncludersError ? err.path : path;
|
|
304
|
+
|
|
305
|
+
logger.error(file, message);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (item.include) {
|
|
310
|
+
const {mode = IncludeMode.ROOT_MERGE} = item.include;
|
|
311
|
+
const includeTocPath = mode === IncludeMode.ROOT_MERGE
|
|
312
|
+
? resolve(sourcesDir, item.include.path)
|
|
313
|
+
: resolve(tocDir, item.include.path);
|
|
314
|
+
const includeTocDir = dirname(includeTocPath);
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const includeToc = load(readFileSync(includeTocPath, 'utf8')) as YfmToc;
|
|
318
|
+
|
|
319
|
+
// Should ignore included toc with tech-preview stage.
|
|
320
|
+
if (includeToc.stage === Stage.TECH_PREVIEW) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (mode === IncludeMode.MERGE || mode === IncludeMode.ROOT_MERGE) {
|
|
325
|
+
_copyTocDir(includeTocPath, tocDir);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* Save the path to exclude toc from the output directory in the next step */
|
|
329
|
+
addIncludeTocPath(includeTocPath);
|
|
330
|
+
|
|
331
|
+
let includedTocItems = (item.items || []).concat(includeToc.items);
|
|
332
|
+
|
|
333
|
+
/* Resolve nested toc inclusions */
|
|
334
|
+
const baseTocDir = mode === IncludeMode.LINK ? includeTocDir : tocDir;
|
|
335
|
+
includedTocItems = await processTocItems(path, includedTocItems, baseTocDir, sourcesDir, vars);
|
|
336
|
+
|
|
337
|
+
/* Make hrefs relative to the main toc */
|
|
338
|
+
if (mode === IncludeMode.LINK) {
|
|
339
|
+
includedTocItems = _replaceIncludesHrefs(includedTocItems, includeTocDir, tocDir);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (item.name) {
|
|
343
|
+
item.items = includedTocItems;
|
|
344
|
+
} else {
|
|
345
|
+
includedInlineItems = includedTocItems;
|
|
346
|
+
}
|
|
347
|
+
} catch (err) {
|
|
348
|
+
const message = (
|
|
349
|
+
`Error while including toc: ${bold(includeTocPath)} to ${bold(join(tocDir, 'toc.yaml'))}`
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
log.error(message);
|
|
353
|
+
|
|
354
|
+
continue;
|
|
355
|
+
} finally {
|
|
356
|
+
delete item.include;
|
|
357
|
+
}
|
|
358
|
+
} else if (item.items) {
|
|
359
|
+
item.items = await processTocItems(path, item.items, tocDir, sourcesDir, vars);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (includedInlineItems) {
|
|
363
|
+
result.push(...includedInlineItems);
|
|
364
|
+
} else {
|
|
365
|
+
result.push(item);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return result;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function getTocDir(pagePath: string): string {
|
|
373
|
+
const {input: inputFolderPath} = ArgvService.getConfig();
|
|
374
|
+
|
|
375
|
+
const tocDir = dirname(pagePath);
|
|
376
|
+
const tocPath = resolve(tocDir, 'toc.yaml');
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
if (!tocDir.includes(inputFolderPath)) {
|
|
380
|
+
throw new Error('Error while finding toc dir');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (existsSync(tocPath)) {
|
|
384
|
+
return tocDir;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return getTocDir(tocDir);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function setNavigationPaths(paths: TocServiceData['navigationPaths']) {
|
|
391
|
+
navigationPaths = paths;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export default {
|
|
395
|
+
add,
|
|
396
|
+
getForPath,
|
|
397
|
+
getNavigationPaths,
|
|
398
|
+
getTocDir,
|
|
399
|
+
getIncludedTocPaths,
|
|
400
|
+
setNavigationPaths,
|
|
401
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import evalExp from '@doc-tools/transform/lib/liquid/evaluation';
|
|
2
|
+
import {Filter, TextItems} from '../models';
|
|
3
|
+
import liquid from '@doc-tools/transform/lib/liquid';
|
|
4
|
+
import {ArgvService} from './index';
|
|
5
|
+
|
|
6
|
+
export interface FilterFilesOptions {
|
|
7
|
+
resolveConditions?: boolean;
|
|
8
|
+
removeHiddenTocItems?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Filters file by expression and removes empty file's items.
|
|
13
|
+
* @param items
|
|
14
|
+
* @param itemsKey
|
|
15
|
+
* @param vars
|
|
16
|
+
* @param options
|
|
17
|
+
* @return {T[]}
|
|
18
|
+
*/
|
|
19
|
+
export function filterFiles<T extends Filter>(
|
|
20
|
+
items: T[],
|
|
21
|
+
itemsKey: string,
|
|
22
|
+
vars: Record<string, string>,
|
|
23
|
+
options?: FilterFilesOptions,
|
|
24
|
+
): T[] {
|
|
25
|
+
if (!Array.isArray(items)) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const reducer = (results: T[], item: T) => {
|
|
30
|
+
if (shouldProcessItem(item, vars, options)) {
|
|
31
|
+
const prop = item[itemsKey] as T[];
|
|
32
|
+
|
|
33
|
+
if (prop) {
|
|
34
|
+
const filteredProperty = filterFiles(prop, itemsKey, vars, options);
|
|
35
|
+
|
|
36
|
+
if (filteredProperty.length) {
|
|
37
|
+
results.push({
|
|
38
|
+
...item,
|
|
39
|
+
[itemsKey]: filteredProperty,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
results.push(item);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return results;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return items.reduce(reducer, []);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function filterTextItems(
|
|
54
|
+
items: undefined | TextItems,
|
|
55
|
+
vars: Record<string, string>,
|
|
56
|
+
options?: FilterFilesOptions,
|
|
57
|
+
) {
|
|
58
|
+
if (!Array.isArray(items)) {
|
|
59
|
+
return items;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return items.reduce((result: string[], item) => {
|
|
63
|
+
if (!isObject(item)) {
|
|
64
|
+
result.push(item);
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const useItem = shouldProcessItem(item, vars, options);
|
|
69
|
+
|
|
70
|
+
if (useItem) {
|
|
71
|
+
if (Array.isArray(item.text)) {
|
|
72
|
+
result.push(...item.text);
|
|
73
|
+
} else {
|
|
74
|
+
result.push(item.text);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return result;
|
|
79
|
+
}, []);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function firstFilterTextItems(
|
|
83
|
+
items: TextItems,
|
|
84
|
+
vars: Record<string, string>,
|
|
85
|
+
options?: FilterFilesOptions,
|
|
86
|
+
) {
|
|
87
|
+
const filteredItems = filterTextItems(items, vars, options);
|
|
88
|
+
|
|
89
|
+
if (!Array.isArray(filteredItems)) {
|
|
90
|
+
return filteredItems || '';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return filteredItems[0] || '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function shouldProcessItem<T extends Filter>(item: T, vars: Record<string, string>, options?: FilterFilesOptions) {
|
|
97
|
+
const {resolveConditions, removeHiddenTocItems} = options || {};
|
|
98
|
+
let useItem = true;
|
|
99
|
+
|
|
100
|
+
if (resolveConditions) {
|
|
101
|
+
const {when} = item;
|
|
102
|
+
useItem =
|
|
103
|
+
when === true || when === undefined || (typeof when === 'string' && evalExp(when, vars));
|
|
104
|
+
|
|
105
|
+
delete item.when;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (useItem && removeHiddenTocItems) {
|
|
109
|
+
useItem = !item.hidden;
|
|
110
|
+
|
|
111
|
+
delete item.hidden;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return useItem;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function liquidFields(fields: undefined | string | string[], vars: Record<string, unknown>, path: string) {
|
|
118
|
+
if (typeof fields === 'string') {
|
|
119
|
+
return liquidField(fields, vars, path);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!Array.isArray(fields)) {
|
|
123
|
+
return fields;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return fields.map((item) => {
|
|
127
|
+
if (typeof item === 'string') {
|
|
128
|
+
return liquidField(item, vars, path);
|
|
129
|
+
}
|
|
130
|
+
return item;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function liquidField(input: string, vars: Record<string, unknown>, path: string) {
|
|
135
|
+
const {applyPresets, resolveConditions} = ArgvService.getConfig();
|
|
136
|
+
|
|
137
|
+
if (!applyPresets && !resolveConditions) {
|
|
138
|
+
return input;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return liquid(input, vars, path, {
|
|
142
|
+
substitutions: applyPresets,
|
|
143
|
+
conditions: resolveConditions,
|
|
144
|
+
keepNotVar: true,
|
|
145
|
+
withSourceMap: false,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function isObject(o: unknown): o is object {
|
|
150
|
+
return typeof o === 'object' && o !== null;
|
|
151
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import walkSync from 'walk-sync';
|
|
2
|
+
import shell from 'shelljs';
|
|
3
|
+
|
|
4
|
+
import client from '../../scripts/client';
|
|
5
|
+
import {ArgvService} from '../services';
|
|
6
|
+
import {copyFiles} from '../utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Processes assets files (everything except .yaml and .md files)
|
|
10
|
+
* @param {string} outputBundlePath
|
|
11
|
+
* @return {void}
|
|
12
|
+
*/
|
|
13
|
+
export function processAssets(outputBundlePath: string) {
|
|
14
|
+
const {
|
|
15
|
+
input: inputFolderPath,
|
|
16
|
+
output: outputFolderPath,
|
|
17
|
+
} = ArgvService.getConfig();
|
|
18
|
+
|
|
19
|
+
const assetFilePath: string[] = walkSync(inputFolderPath, {
|
|
20
|
+
directories: false,
|
|
21
|
+
includeBasePath: false,
|
|
22
|
+
ignore: [
|
|
23
|
+
'**/*.yaml',
|
|
24
|
+
'**/*.md',
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
copyFiles(inputFolderPath, outputFolderPath, assetFilePath);
|
|
29
|
+
|
|
30
|
+
/* Copy js bundle to user' output folder */
|
|
31
|
+
shell.mkdir('-p', outputBundlePath);
|
|
32
|
+
|
|
33
|
+
for (const path of Object.values(client.dst)) {
|
|
34
|
+
shell.cp(path, outputBundlePath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {resolve, relative} from 'path';
|
|
2
|
+
import walkSync from 'walk-sync';
|
|
3
|
+
import shell from 'shelljs';
|
|
4
|
+
|
|
5
|
+
import {ArgvService, TocService} from '../services';
|
|
6
|
+
import {convertBackSlashToSlash} from '../utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Removes all content files that unspecified in toc files or ignored.
|
|
10
|
+
* @return {void}
|
|
11
|
+
*/
|
|
12
|
+
export function processExcludedFiles() {
|
|
13
|
+
const {
|
|
14
|
+
input: inputFolderPath,
|
|
15
|
+
output: outputFolderPath,
|
|
16
|
+
ignore,
|
|
17
|
+
} = ArgvService.getConfig();
|
|
18
|
+
|
|
19
|
+
const allContentFiles: string[] = walkSync(inputFolderPath, {
|
|
20
|
+
directories: false,
|
|
21
|
+
includeBasePath: true,
|
|
22
|
+
globs: [
|
|
23
|
+
'**/*.md',
|
|
24
|
+
'**/index.yaml',
|
|
25
|
+
...ignore,
|
|
26
|
+
],
|
|
27
|
+
// Ignores service directories like "_includes", "_templates" and etc.
|
|
28
|
+
ignore: ['**/_*/**/*'],
|
|
29
|
+
});
|
|
30
|
+
const navigationPaths = TocService.getNavigationPaths()
|
|
31
|
+
.map((filePath) => convertBackSlashToSlash(resolve(inputFolderPath, filePath)));
|
|
32
|
+
const tocSpecifiedFiles = new Set(navigationPaths);
|
|
33
|
+
const excludedFiles = allContentFiles
|
|
34
|
+
.filter((filePath) => !tocSpecifiedFiles.has(filePath));
|
|
35
|
+
|
|
36
|
+
shell.rm('-f', excludedFiles);
|
|
37
|
+
|
|
38
|
+
const includedTocPaths = TocService.getIncludedTocPaths()
|
|
39
|
+
.map((filePath) => {
|
|
40
|
+
const relativeTocPath = relative(inputFolderPath, filePath);
|
|
41
|
+
const destTocPath = resolve(outputFolderPath, relativeTocPath);
|
|
42
|
+
|
|
43
|
+
return convertBackSlashToSlash(destTocPath);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
shell.rm('-rf', includedTocPaths);
|
|
47
|
+
}
|