-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcatzip.js
executable file
·165 lines (148 loc) · 4.8 KB
/
catzip.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/env node
const path = require('path');
const fs = require('fs');
const commandLineArgs = require('command-line-args');
const commandLineUsage = require('command-line-usage');
const {EOL} = require('os');
const optionDefinitions = [
{
name: 'help',
description: 'Display this usage guide.',
alias: 'h',
type: Boolean
},
{
name: 'src',
description: 'Zip file(s) to operate on. This is the default argument. Any extra arguments will be assumed to be zip files. It works best if these files end with ".zip".',
type: String,
multiple: true,
defaultOption: true,
typeLabel: '{underline zip file} ...'
},
{
name: 'save',
description: 'Save output to file. This is especially useful when specifying multiple zip files. Without this option, all of the content is printed together. With this option, the output of each zip file is saved to a different text file. Each text file is named the same as the input file, except that ".zip" is replaced with ".txt".',
alias: 's',
type: Boolean
},
{
name: 'markdown',
description: 'output in markdown',
alias: 'm',
type: Boolean
},
]
const options = commandLineArgs(optionDefinitions);
if (options.help) {
console.log(commandLineUsage([
{
header: 'catzip',
content: 'Concatenate all the file contents of a zip file. Designed for use with zip archives of simple, project code.'
},
{
header: 'Options',
optionList: optionDefinitions,
tableOptions: {
columns: [
{
name: 'option',
noWrap: true,
width: 20,
},
{
name: 'description',
width: 60,
}
]
}
},
{
header: 'Examples',
},
{
content: '{underline catzip John_Smith.zip} # print content to screen'
},
{
content: '{underline catzip --save A.zip B.zip} # print A.zip into A.txt, B.zip -> B.txt'
},
{
header: 'Quirks',
content: 'This utility was written to aid recruiters in printing and sharing code written by candidates. It has some quirks specific to that task.'
},
{
content: 'Some files inside the archive will be ignored. For example, it won\'t print the content of "package.json".'
},
{
content: 'The files in the archive are expected to be short and contain code. The lines are numbered for each file on the output.'
},
{
content: 'The name of the archive is printed on the first line, and the name of each file is printed at the beginning and end of its content.'
}
]));
return;
}
const PRINT_TO_FILES = options.save;
const USE_MARKDOWN = options.markdown;
const AdmZip = require('adm-zip');
const nameEndingsToSkip = [
'package.json',
'sandbox.config.json',
'.gif',
];
archives = options.src;
archives.forEach(processArchive);
function processArchive(archiveName) {
var zip = new AdmZip(archiveName);
var zipEntries = zip.getEntries();
var linePattern = /^.*?$/gim;
var emit = (...args) => console.log(args.join(' '));
let stream = null;
if (PRINT_TO_FILES) {
let filename = path.basename(archiveName, '.zip') + '.txt';
stream = fs.createWriteStream(filename, 'utf8');
emit = (...args) => stream.write(args.join(' ') + EOL);
}
emit('Contents of', archiveName);
zipEntries.forEach(zipEntry => {
let name = zipEntry.entryName;
if (name.endsWith('/') || name.endsWith('\\')) {
return;
}
for (ending of nameEndingsToSkip) {
if (name.endsWith(ending)) {
return;
}
}
let content = zip.readAsText(name);
if (USE_MARKDOWN) {
emit('');
emit(`**File: ${name}**`);
emit('');
emit('```' + name.split('.').pop());
} else {
emit('// Begin', name);
}
let lineNo = 1;
[...content.matchAll(linePattern)].map(matchData => matchData[0]).forEach(line => {
if (USE_MARKDOWN) {
emit(line);
} else {
emit(pad(lineNo++), line);
}
});
if (USE_MARKDOWN) {
emit('```');
} else {
emit('// End', name);
}
});
if (stream) {
stream.end();
}
}
function pad(num) {
if (num > 999) {
return num;
}
return ('000' + num).slice(-3);
}