-
Notifications
You must be signed in to change notification settings - Fork 302
/
Copy pathmd2gslides.js
executable file
·236 lines (215 loc) · 6.44 KB
/
md2gslides.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#!/usr/bin/env node
// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* eslint-disable no-console, @typescript-eslint/no-var-requires */
require('babel-polyfill');
const Promise = require('promise');
const fs = require('fs');
const path = require('path');
const ArgumentParser = require('argparse').ArgumentParser;
const UserAuthorizer = require('../lib/auth').default;
const SlideGenerator = require('../lib/slide_generator').default;
const opener = require('opener');
const readline = require('readline');
const SCOPES = [
'https://www.googleapis.com/auth/presentations',
'https://www.googleapis.com/auth/drive',
];
const USER_HOME =
process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
const STORED_CREDENTIALS_PATH = path.join(
USER_HOME,
'.md2googleslides',
'credentials.json'
);
const STORED_CLIENT_ID_PATH = path.join(
USER_HOME,
'.md2googleslides',
'client_id.json'
);
const parser = new ArgumentParser({
version: '1.0.0',
addHelp: true,
description: 'Markdown to Slides converter',
});
parser.addArgument('file', {
help: 'Path to markdown file to convert, If omitted, reads from stdin',
nargs: '?',
});
parser.addArgument(['-u', '--user'], {
help: 'Email address of user',
required: false,
dest: 'user',
defaultValue: 'default',
});
parser.addArgument(['-a', '--append'], {
dest: 'id',
help: 'Appends slides to an existing presentation',
required: false,
});
parser.addArgument(['-e', '--erase'], {
dest: 'erase',
action: 'storeTrue',
help: 'Erase existing slides prior to appending.',
required: false,
});
parser.addArgument(['-n', '--no-browser'], {
action: 'storeTrue',
dest: 'headless',
help: 'Headless mode - do not launch browsers, just shows URLs',
required: false,
});
parser.addArgument(['-s', '--style'], {
help: 'Name of highlight.js theme for code formatting',
dest: 'style',
required: false,
defaultValue: 'default',
});
parser.addArgument(['-t', '--title'], {
help: 'Title of the presentation',
dest: 'title',
required: false,
});
parser.addArgument(['-c', '--copy'], {
help: 'Id of the presentation to copy and use as a base',
dest: 'copy',
required: false,
});
parser.addArgument(['--use-fileio'], {
help: 'Acknolwedge local and generated images are uploaded to https://file.io',
action: 'storeTrue',
dest: 'useFileio',
required: false,
});
const args = parser.parseArgs();
function handleError(err) {
console.log('Unable to generate slides:', err);
}
function prompt(url) {
if (args.headless) {
console.log('Authorize this app by visiting this url: ');
console.log(url);
} else {
console.log('Authorize this app in your browser.');
opener(url);
}
return new Promise((resolve, reject) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code here: ', code => {
rl.close();
code = code.trim();
if (code.length > 0) {
resolve(code);
} else {
reject(new Error('No code provided'));
}
});
});
}
function authorizeUser() {
// Google OAuth2 clients always have a secret, even if the client is an installed
// application/utility such as this. Of course, in such cases the "secret" is
// actually publicly known; security depends entirely on the secrecy of refresh
// tokens, which effectively become bearer tokens.
// Load and parse client ID and secret from client_id.json file. (Create
// OAuth client ID from Credentials tab at console.developers.google.com
// and download the credentials as client_id.json to ~/.md2googleslides
let data; // needs to be scoped outside of try-catch
try {
data = fs.readFileSync(STORED_CLIENT_ID_PATH);
} catch (err) {
console.log('Error loading client secret file:', err);
throw err;
}
if (data === undefined) {
console.log('Error loading client secret data');
throw 'No client secret found.';
}
const creds = JSON.parse(data).installed;
// Authorize user and get (& store) a valid access token.
const options = {
clientId: creds.client_id,
clientSecret: creds.client_secret,
filePath: STORED_CREDENTIALS_PATH,
prompt: prompt,
};
const auth = new UserAuthorizer(options);
return auth.getUserCredentials(args.user, SCOPES);
}
function buildSlideGenerator(oauth2Client) {
const title = args.title || args.file;
const presentationId = args.id;
const copyId = args.copy;
if (presentationId) {
return SlideGenerator.forPresentation(oauth2Client, presentationId);
} else if (copyId) {
return SlideGenerator.copyPresentation(oauth2Client, title, copyId);
} else {
return SlideGenerator.newPresentation(oauth2Client, title);
}
}
function eraseIfNeeded(slideGenerator) {
if (args.erase || !args.id) {
return slideGenerator.erase().then(() => {
return slideGenerator;
});
} else {
return Promise.resolve(slideGenerator);
}
}
function loadCss(theme) {
const cssPath = path.join(
require.resolve('highlight.js'),
'..',
'..',
'styles',
theme + '.css'
);
const css = fs.readFileSync(cssPath, {encoding: 'UTF-8'});
return css;
}
function generateSlides(slideGenerator) {
let source;
if (args.file) {
source = path.resolve(args.file);
// Set working directory relative to markdown file
process.chdir(path.dirname(source));
} else {
source = 0;
}
const input = fs.readFileSync(source, {encoding: 'UTF-8'});
const css = loadCss(args.style);
return slideGenerator.generateFromMarkdown(input, {
css: css,
useFileio: args.useFileio,
});
}
function displayResults(id) {
const url = 'https://docs.google.com/presentation/d/' + id;
if (args.headless) {
console.log('View your presentation at: %s', url);
} else {
console.log('Opening your presentation (%s)', url);
opener(url);
}
}
authorizeUser()
.then(buildSlideGenerator)
.then(eraseIfNeeded)
.then(generateSlides)
.then(displayResults)
.catch(handleError);