Skip to content

Commit 4801dc5

Browse files
committedDec 29, 2022
feat(includers/unarchive): unarchive input
introduce new includer that allows to unpack tar archive before including content
1 parent 65f0297 commit 4801dc5

File tree

6 files changed

+170
-7
lines changed

6 files changed

+170
-7
lines changed
 

‎package-lock.json

+70-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"shelljs": "0.8.5",
5454
"simple-git": "2.48.0",
5555
"slugify": "^1.6.5",
56+
"tar-stream": "^2.2.0",
5657
"threads": "1.7.0",
5758
"threads-plugin": "1.4.0",
5859
"walk-sync": "2.2.0",
@@ -77,6 +78,7 @@
7778
"@types/react": "16.14.21",
7879
"@types/react-dom": "16.9.14",
7980
"@types/shelljs": "0.8.10",
81+
"@types/tar-stream": "^2.2.2",
8082
"@types/yargs": "15.0.14",
8183
"@typescript-eslint/eslint-plugin": "2.34.0",
8284
"@typescript-eslint/parser": "2.34.0",

‎src/models.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export type YfmTocIncluders = YfmTocIncluder[];
8787

8888
export type YfmTocIncluder = YfmTocIncluderName | YfmTocIncluderObject;
8989

90-
export const includersNames = ['sourcedocs', 'openapi', 'generic'] as const;
90+
export const includersNames = ['sourcedocs', 'openapi', 'generic', 'unarchive'] as const;
9191

9292
export type YfmTocIncluderName = typeof includersNames[number];
9393

Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * as generic from './generic';
22
export * as sourcedocs from './sourcedocs';
33
export * as openapi from './openapi';
4+
export * as unarchive from './unarchive';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
async function includerFunction(params: IncluderFunctionParams) {
71+
const {readBasePath, writeBasePath, tocPath, passedParams: {input, output}, index} = params;
72+
73+
if (!input?.length || !output?.length) {
74+
throw new UnarchiveIncluderError('provide includer with input parameter', tocPath);
75+
}
76+
77+
const contentPath = index === 0
78+
? join(writeBasePath, input)
79+
: join(readBasePath, input);
80+
81+
const writePath = join(writeBasePath, output);
82+
83+
try {
84+
await pipeline(contentPath, writePath);
85+
} catch (err) {
86+
throw new UnarchiveIncluderError(err.toString(), tocPath);
87+
}
88+
89+
return {input: output};
90+
}
91+
92+
export {name, includerFunction};
93+
94+
export default {name, includerFunction};

‎src/services/includers/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {isObject} from 'lodash';
55
import {ArgvService} from '../index';
66
import {IncludeMode} from '../../constants';
77
import {logger} from '../../utils/logger';
8-
import {generic, sourcedocs, openapi} from './batteries';
8+
import {generic, sourcedocs, openapi, unarchive} from './batteries';
99

1010
import type {
1111
Includer,
@@ -51,7 +51,7 @@ class IncludersError extends Error {
5151
function init(custom: Includer[] = []) {
5252
if (includersMap) { return; }
5353

54-
includersMap = {generic, sourcedocs, openapi};
54+
includersMap = {generic, sourcedocs, openapi, unarchive};
5555

5656
for (const includer of custom) {
5757
includersMap[includer.name] = includer;

0 commit comments

Comments
 (0)
Please sign in to comment.