-
Notifications
You must be signed in to change notification settings - Fork 156
/
cli.ts
executable file
·152 lines (141 loc) · 4.79 KB
/
cli.ts
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
#!/usr/bin/env node
import { Readable } from 'stream';
import { createReadStream, createWriteStream, WriteStream } from 'fs';
import { xmlLint } from './lib/xmllint';
import { XMLLintUnavailable } from './lib/errors';
import {
ObjectStreamToJSON,
XMLToSitemapItemStream,
} from './lib/sitemap-parser';
import { lineSeparatedURLsToSitemapOptions } from './lib/utils';
import { SitemapStream } from './lib/sitemap-stream';
import { SitemapAndIndexStream } from './lib/sitemap-index-stream';
import { URL } from 'url';
import { createGzip, Gzip } from 'zlib';
import { ErrorLevel } from './lib/types';
import arg from 'arg';
const pickStreamOrArg = (argv: { _: string[] }): Readable => {
if (!argv._.length) {
return process.stdin;
} else {
return createReadStream(argv._[0], { encoding: 'utf8' });
}
};
const argSpec = {
'--help': Boolean,
'--version': Boolean,
'--validate': Boolean,
'--index': Boolean,
'--index-base-url': String,
'--limit': Number,
'--parse': Boolean,
'--single-line-json': Boolean,
'--prepend': String,
'--gzip': Boolean,
'-h': '--help',
};
const argv = arg(argSpec);
function getStream(): Readable {
if (argv._ && argv._.length) {
return createReadStream(argv._[0]);
} else {
console.warn(
'Reading from stdin. If you are not piping anything in, this command is not doing anything'
);
return process.stdin;
}
}
if (argv['--version']) {
import('./package.json').then(({ default: packagejson }) => {
console.log(packagejson.version);
});
} else if (argv['--help']) {
console.log(`
Turn a list of urls into a sitemap xml.
Options:
--help Print this text
--version Print the version
--validate Ensure the passed in file is conforms to the sitemap spec
--index Create an index and stream that out. Writes out sitemaps along the way.
--index-base-url Base url the sitemaps will be hosted eg. https://example.com/sitemaps/
--limit=45000 Set a custom limit to the items per sitemap
--parse Parse fed xml and spit out config
--prepend=sitemap.xml Prepend the streamed in sitemap configs to sitemap.xml
--gzip Compress output
--single-line-json When used with parse, it spits out each entry as json rather than the whole json.
# examples
Generate a sitemap index file as well as sitemaps
npx sitemap --gzip --index --index-base-url https://example.com/path/to/sitemaps/ < listofurls.txt > sitemap-index.xml.gz
Add to a sitemap
npx sitemap --prepend sitemap.xml < listofurls.json
Turn an existing sitemap into configuration understood by the sitemap library
npx sitemap --parse sitemap.xml
Use XMLLib to validate your sitemap (requires xmllib)
npx sitemap --validate sitemap.xml
`);
} else if (argv['--parse']) {
let oStream: ObjectStreamToJSON | Gzip = getStream()
.pipe(new XMLToSitemapItemStream({ level: ErrorLevel.THROW }))
.pipe(
new ObjectStreamToJSON({ lineSeparated: !argv['--single-line-json'] })
);
if (argv['--gzip']) {
oStream = oStream.pipe(createGzip());
}
oStream.pipe(process.stdout);
} else if (argv['--validate']) {
xmlLint(getStream())
.then((): void => console.log('valid'))
.catch(([error, stderr]: [Error | null, Buffer]): void => {
if (error instanceof XMLLintUnavailable) {
console.error(error.message);
return;
} else {
console.log(stderr);
}
});
} else if (argv['--index']) {
const limit: number | undefined = argv['--limit'];
const baseURL: string | undefined = argv['--index-base-url'];
if (!baseURL) {
throw new Error(
"You must specify where the sitemaps will be hosted. use --index-base-url 'https://example.com/path'"
);
}
const sms = new SitemapAndIndexStream({
limit,
getSitemapStream: (i: number): [string, SitemapStream, WriteStream] => {
const sm = new SitemapStream();
const path = `./sitemap-${i}.xml`;
let ws: WriteStream;
if (argv['--gzip']) {
ws = sm.pipe(createGzip()).pipe(createWriteStream(path));
} else {
ws = sm.pipe(createWriteStream(path));
}
return [new URL(path, baseURL).toString(), sm, ws];
},
});
let oStream: SitemapAndIndexStream | Gzip = lineSeparatedURLsToSitemapOptions(
pickStreamOrArg(argv)
).pipe(sms);
if (argv['--gzip']) {
oStream = oStream.pipe(createGzip());
}
oStream.pipe(process.stdout);
} else {
const sms = new SitemapStream();
if (argv['--prepend']) {
createReadStream(argv['--prepend'])
.pipe(new XMLToSitemapItemStream())
.pipe(sms);
}
const oStream: SitemapStream = lineSeparatedURLsToSitemapOptions(
pickStreamOrArg(argv)
).pipe(sms);
if (argv['--gzip']) {
oStream.pipe(createGzip()).pipe(process.stdout);
} else {
oStream.pipe(process.stdout);
}
}