15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/template/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary-color: #000000;
3 | }
4 |
5 | html, body {
6 | margin: 0;
7 | padding: 0;
8 | max-width: 100vw;
9 | overflow: hidden;
10 | }
11 |
12 | body, button {
13 | font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | -webkit-text-size-adjust: 100%;
17 | }
18 |
19 | main > article {
20 | margin: 0;
21 | padding: 10px;
22 | width: 100vw;
23 | height: 100vh;
24 | border-color: #eee;
25 | border-style: solid;
26 | border-width: 0;
27 | position: relative;
28 | box-sizing: border-box;
29 | }
30 |
31 | button.copy-to-clipboard {
32 | position: fixed;
33 | font-size: 0.9vw;
34 | padding: 0.2vw;
35 | cursor: pointer;
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020 Nextbit (https://nextbit.it/)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@nextbitlabs/onepunch",
3 | "version": "2.6.2",
4 | "description": "Command-line interface to create PDF presentations using web technology.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "xo index.js template/src/index.js",
8 | "release": "np"
9 | },
10 | "bin": {
11 | "onepunch": "index.js"
12 | },
13 | "keywords": [
14 | "html",
15 | "css",
16 | "pdf",
17 | "web",
18 | "presentations",
19 | "slides"
20 | ],
21 | "files": [
22 | "index.js",
23 | "template",
24 | "LICENSE"
25 | ],
26 | "repository": "nextbitlabs/onepunch",
27 | "bugs": "https://github.com/nextbitlabs/onepunch/issues",
28 | "homepage": "https://github.com/nextbitlabs/onepunch",
29 | "author": {
30 | "name": "Riccardo Scalco",
31 | "url": "https://riccardoscalco.github.io/"
32 | },
33 | "license": "MIT",
34 | "dependencies": {
35 | "chalk": "^3.0.0",
36 | "fs-extra": "^8.1.0",
37 | "live-server": "^1.2.1",
38 | "meow": "^6.1.1",
39 | "ora": "^4.1.1",
40 | "puppeteer": "^5.5.0"
41 | },
42 | "devDependencies": {
43 | "np": "^7.0.0",
44 | "xo": "^0.36.1"
45 | },
46 | "publishConfig": {
47 | "access": "public"
48 | },
49 | "xo": {
50 | "envs": [
51 | "browser"
52 | ],
53 | "rules": {
54 | "comma-dangle": [
55 | "error",
56 | "always-multiline"
57 | ]
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/template/readme.md:
--------------------------------------------------------------------------------
1 | # **onepunch** presentation
2 |
3 | ### Configuration
4 | The configuration file `onepunch.json` contains configuration parameters, with the following available:
5 | - `width` and `height`, numeric: slide width and height in pixels;
6 | - `progress`, string: available `line`, to show a progress line at the bottom of the page, or `none`, to suppress it;
7 | - `date`, string: presentation date, used by tags `` along the presentation;
8 | - `slideNumber`, bool: whether to visualize the slide number in tags ``.
9 |
10 | ### View the presentation
11 |
12 | Inside the project directory, run:
13 |
14 | ```sh
15 | $ onepunch serve [-i htmlfile]
16 | ```
17 |
18 | The command above starts a local server and opens the browser, use the arrow keys to see the next and previous slides.
19 | Flag `-i` (or `--input`) specifies the HTML file to open, it defaults to "index.html".
20 |
21 | ### Print the PDF
22 |
23 | Inside the project directory, run:
24 |
25 | ```sh
26 | $ onepunch print [-i htmlfile] [-o pdffile]
27 | ```
28 |
29 | Flag `-i` (or `--input`) specifies the HTML file to print, it defaults to "index.html".
30 | Flag `-o` (or `--output`) specifies the name of the PDF file in output, it defaults to "index.pdf".
31 |
32 | ### Update
33 |
34 | To align the `src` directory of a past presentation to the latest release of **onepunch**,
35 | first update **onepunch** itself, then use:
36 |
37 | ```sh
38 | $ onepunch update
39 | ```
40 | This will make any new feature available to the current presentation.
41 | Please note that any custom change inside directory `src` will be overwritten.
42 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # onepunch
2 |
3 | **onepunch** is a command-line interface to create PDF presentations using web technology.
4 |
5 | **onepunch** is designed for designers, it does not provide any default style.
6 | Designers can write CSS files and link them in the `index.html`.
7 |
8 | ### Prerequisites
9 |
10 | To use **onepunch**, you should have installed **node** and **npm** in your system.
11 | Please follow the [official instructions](https://www.npmjs.com/get-npm).
12 |
13 | ### Install
14 |
15 | Install **onepunch** globally with the following command:
16 |
17 | ```sh
18 | $ npm install -g @nextbitlabs/onepunch
19 | ```
20 |
21 | Please note that onepunch makes use of [puppeteer](https://github.com/puppeteer/puppeteer/), which will download chromium.
22 | This is necessary to print the PDF file.
23 |
24 | Update **onepunch** to the latest release with:
25 |
26 | ```sh
27 | $ npm update -g @nextbitlabs/onepunch
28 | ```
29 |
30 | ### Create a project
31 |
32 | ```sh
33 | $ onepunch init [-n directory-name]
34 | ```
35 |
36 | The command above creates the directory `directory-name` with all the files needed to bootstrap the presentation.
37 |
38 | The configuration file `onepunch.json` contains configuration parameters, with the following available:
39 | - `width` and `height`, numeric: slide width and height in pixels;
40 | - `progress`, string: available `line`, to show a progress line at the bottom of the page, or `none`, to suppress it;
41 | - `date`, string: presentation date, used by tags `` along the presentation;
42 | - `slideNumber`, bool: whether to visualize the slide number in tags ``.
43 |
44 | ### View the presentation
45 |
46 | Inside the project directory, run:
47 |
48 | ```sh
49 | $ onepunch serve [-i htmlfile]
50 | ```
51 |
52 | The command above starts a local server and opens the browser, use the arrow keys to see the next and previous slides.
53 | Flag `-i` (or `--input`) specifies the HTML file to open, it defaults to "index.html".
54 |
55 | ### Print the PDF
56 |
57 | Inside the project directory, run:
58 |
59 | ```sh
60 | $ onepunch print [-i htmlfile] [-o pdffile]
61 | ```
62 |
63 | Flag `-i` (or `--input`) specifies the HTML file to print, it defaults to "index.html".
64 | Flag `-o` (or `--output`) specifies the name of the PDF file in output, it defaults to "index.pdf".
65 |
66 | ### Update
67 | Update **onepunch** to the latest release with:
68 |
69 | ```sh
70 | $ npm update -g @nextbitlabs/onepunch
71 | ```
72 |
73 | To align the `src` directory of a past presentation to the latest release of **onepunch**,
74 | first update **onepunch** itself with the command above, then use:
75 |
76 | ```sh
77 | $ onepunch update
78 | ```
79 | This will make any new feature available to the current presentation.
80 | Please note that any custom change inside directory `src` will be overwritten.
81 |
82 | ### Create custom styles
83 |
84 | Each slide is created by means of tag `article`, for example:
85 |
86 | ```html
87 |
88 |
89 |
90 |
91 |
92 |
My Presentation Title
93 |
94 |
95 |
96 |
97 |
98 |
99 |
My Presentation Title
100 |
We use web technology to create PDF presentations.
101 |
102 |
103 |
104 | ...
105 |
106 |
107 | ```
108 |
109 | As usual, designers can define CSS classes to apply custom style.
110 | For example, the following class defines a specific grid layout:
111 |
112 | ```css
113 | .layout-1 {
114 | display: grid;
115 | grid-template-rows: minmax(50px, max-content) auto 50px;
116 | grid-template-columns: 100%;
117 | grid-template-areas:
118 | "A"
119 | "B"
120 | "C";
121 | }
122 | ```
123 |
124 | and can be used in the following way:
125 |
126 | ```html
127 |
128 |
129 | ...
130 |
131 |
132 | ..
133 |
134 |
137 |
138 | ```
139 |
--------------------------------------------------------------------------------
/template/src/index.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | const isAutomated = navigator.webdriver;
3 |
4 | /*
5 | Get configuration parameters and initialize the web page.
6 | */
7 |
8 | (async function () {
9 | const fecthResult = await fetch('./onepunch.json');
10 | const config = await fecthResult.json();
11 | init(config);
12 | })();
13 |
14 | /*
15 | Initialize services.
16 | */
17 |
18 | function init(config) {
19 | addSlideId();
20 | setHash();
21 | addEventListeners(config);
22 | setBorders(config.width, config.height);
23 | addProgress(config);
24 | addSlideNumber(config);
25 | addDate(config);
26 | if (!isAutomated) {
27 | addCopyToClipBoardButton(config);
28 | }
29 | }
30 |
31 | /*
32 | Set location hash
33 | */
34 |
35 | function setHash() {
36 | window.location.hash = window.location.hash || getSlides()[0].id;
37 | }
38 |
39 | /*
40 | Add event listeners.
41 | */
42 |
43 | function addEventListeners(config) {
44 | window.addEventListener(
45 | 'keydown',
46 | handleOnkeydown,
47 | );
48 | window.addEventListener(
49 | 'resize',
50 | () => {
51 | setBorders(config.width, config.height);
52 | if (!isAutomated) {
53 | setCopyToClipBoardButtonPosition(config);
54 | }
55 |
56 | updateLocationHash(getSlideId());
57 | },
58 | );
59 | }
60 |
61 | /*
62 | Set article borders.
63 | */
64 |
65 | function setBorders(width, height) {
66 | const articles = getSlides();
67 | const borderLeft = Math.max(0, (window.innerWidth - width) / 2);
68 | const borderTop = Math.max(0, (window.innerHeight - height) / 2);
69 | articles.forEach(elt => {
70 | elt.style.borderWidth = `${borderTop}px ${borderLeft}px`;
71 | });
72 | }
73 |
74 | /*
75 | Change slide with arrow keys.
76 | */
77 |
78 | function handleOnkeydown(event) {
79 | const {keyCode} = event;
80 | const LEFT = 37;
81 | const UP = 38;
82 | const RIGHT = 39;
83 | const DOWN = 40;
84 | switch (keyCode) {
85 | case RIGHT:
86 | case DOWN:
87 | goToNextPage();
88 | break;
89 | case UP:
90 | case LEFT:
91 | goToPreviousPage();
92 | break;
93 | // No default
94 | }
95 | }
96 |
97 | function goToNextPage() {
98 | updateUrl(+1);
99 | }
100 |
101 | function goToPreviousPage() {
102 | updateUrl(-1);
103 | }
104 |
105 | /*
106 | Add progress to each slide based on config.
107 | */
108 |
109 | function addProgress(config) {
110 | if (!config.progress || config.progress === 'none') {
111 | return;
112 | }
113 |
114 | const articles = getSlides();
115 |
116 | if (config.progress === 'line') {
117 | articles.forEach((article, index) => {
118 | const line = document.createElement('div');
119 | line.classList.add('progress-line');
120 | article.append(line);
121 |
122 | line.style.backgroundColor = 'var(--primary-color, #000)';
123 |
124 | line.style.height = '3px';
125 | line.style.position = 'absolute';
126 |
127 | line.style.bottom = '0';
128 | line.style.left = '0';
129 |
130 | const normIndex = index / (articles.length - 1);
131 |
132 | line.style.width = `${config.width * normIndex}px`;
133 | });
134 | }
135 | }
136 |
137 | /*
138 | Update location hash.
139 | */
140 |
141 | function updateLocationHash(pageId) {
142 | window.location.hash = `#${pageId}`;
143 | }
144 |
145 | /*
146 | Get the id of the current slide
147 | */
148 |
149 | function getSlideId() {
150 | return window.location.hash.slice(1);
151 | }
152 |
153 | /*
154 | Update url.
155 | */
156 |
157 | function updateUrl(increment) {
158 | const index = getPageIndex();
159 | if (
160 | (increment === +1 && index < numberOfSlides() - 1) ||
161 | (increment === -1 && index > 0)
162 | ) {
163 | const slideId = getSlides()[index + increment].id;
164 | updateLocationHash(slideId);
165 | }
166 | }
167 |
168 | /*
169 | Return the number of slides.
170 | */
171 |
172 | function numberOfSlides() {
173 | return getSlides().length;
174 | }
175 |
176 | /*
177 | Get page index location hash.
178 | */
179 |
180 | function getPageIndex() {
181 | return getSlides()
182 | .map(page => page.id)
183 | .findIndex(id => id === getSlideId());
184 | }
185 |
186 | /*
187 | Return the list of slides.
188 | */
189 |
190 | function getSlides() {
191 | return [...document.querySelectorAll('main > article')];
192 | }
193 |
194 | /*
195 | Add slide numbers.
196 | */
197 |
198 | function addSlideNumber(config) {
199 | const slides = getSlides();
200 | slides.forEach((slide, index) => {
201 | const element = slide.querySelector('[data-onepunch="slide-number"]');
202 | if (element) {
203 | if (config.slideNumber) {
204 | element.textContent = index + 1;
205 | } else {
206 | element.style.display = 'none';
207 | }
208 | }
209 | });
210 | }
211 |
212 | /*
213 | Add date.
214 | */
215 |
216 | function addDate(config) {
217 | const slides = getSlides();
218 | slides.forEach(slide => {
219 | const element = slide.querySelector('[data-onepunch="date"]');
220 | if (element) {
221 | if (config.date) {
222 | element.textContent = config.date;
223 | } else {
224 | element.style.display = 'none';
225 | }
226 | }
227 | });
228 | }
229 |
230 | /*
231 | Add slide id
232 | */
233 |
234 | function addSlideId() {
235 | const slides = getSlides();
236 | slides.forEach((slide, index) => {
237 | slide.id = slide.id || `${index + 1}`;
238 | });
239 | }
240 |
241 | /*
242 | Add copy-to-clipboard button
243 | */
244 | function addCopyToClipBoardButton(config) {
245 | appendCopyToClipBoardButton();
246 | setCopyToClipBoardButtonPosition(config);
247 | }
248 |
249 | function copyToClipBoard() {
250 | fetch(window.location.href)
251 | .then(response => response.text())
252 | .then(text => {
253 | const element = document.createElement('html');
254 | element.innerHTML = text;
255 | const articles = element.querySelectorAll('main > article');
256 | const htmlSnippet = articles[getPageIndex()].outerHTML;
257 | navigator.clipboard.writeText(htmlSnippet).then(() => {
258 | // Console.log('Copied to clipboard.');
259 | }, () => {
260 | // Console.log('Error, not copied.');
261 | });
262 | });
263 | }
264 |
265 | function appendCopyToClipBoardButton() {
266 | const button = document.createElement('button');
267 | button.className = 'copy-to-clipboard';
268 | button.textContent = 'copy to clipboard';
269 | button.addEventListener('click', copyToClipBoard);
270 | document.body.append(button);
271 | }
272 |
273 | function setCopyToClipBoardButtonPosition(config) {
274 | const button = document.querySelectorAll('.copy-to-clipboard')[0];
275 | const bottom = Math.max(0, (window.innerHeight - config.height) / 2);
276 | const right = Math.max(0, (window.innerWidth - config.width) / 2);
277 | button.style.bottom = `calc(${bottom}px - 2.5vw)`;
278 | button.style.right = `${right}px`;
279 | }
280 | })();
281 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const path = require('path');
4 | const fs = require('fs-extra');
5 | const meow = require('meow');
6 | const puppeteer = require('puppeteer');
7 | const liveServer = require('live-server');
8 | const chalk = require('chalk');
9 | const ora = require('ora');
10 |
11 | const cli = meow({
12 | description: false,
13 | help: `
14 | ${chalk.bold('NAME')}
15 |
16 | ${chalk.bold('onepunch')} -- create presentations with web technology
17 |
18 | ${chalk.bold('DESCRIPTION')}
19 |
20 | ${chalk.bold('onepunch')} is a command-line interface useful to create presentations with web technology.
21 | Moreover, thanks to puppeteer, ${chalk.bold('onepunch')} can print the presentation in a PDF file.
22 |
23 | ${chalk.bold('onepunch')} is designed for designers, it does not provide any default style.
24 | Designers can define custom styles by writing CSS files and linking them in the ${chalk.underline('index.html')}.
25 |
26 | ${chalk.bold('onepunch')} is open source software licensed under the MIT License,
27 | please visit https://github.com/nextbitlabs/onepunch for further details.
28 |
29 | ${chalk.bold('SYNOPSIS')}
30 |
31 | ${chalk.bold('onepunch init')} [${chalk.bold('-n')} ${chalk.italic('directory_name')}]
32 | Initialize a presentation.
33 |
34 | ${chalk.bold('onepunch serve')} [${chalk.bold('-i')} ${chalk.italic('htmlfile')}]
35 | Open the presentation in the browser.
36 |
37 | ${chalk.bold('onepunch print')} [${chalk.bold('-i')} ${chalk.italic('htmlfile')}] [${chalk.bold('-o')} ${chalk.italic('pdffile')}]
38 | Print the presentation in a PDF file.
39 |
40 | ${chalk.bold('onepunch update')}
41 | Update files in the ${chalk.underline('src')} directory according to the latest release.
42 | Please note that any custom change inside directory ${chalk.underline('src')} will be overwritten.
43 |
44 | ${chalk.bold('OPTIONS')}
45 |
46 | ${chalk.bold('-n')} or ${chalk.bold('--name')} ${chalk.italic('directory_name')}
47 | Specify the name of the directory where the project is initialized.
48 | Defaults to ${chalk.underline('onepunch-presentation')}.
49 |
50 | ${chalk.bold('-i')} or ${chalk.bold('--input')} ${chalk.italic('htmlfile')}
51 | Specify the HTML file to serve or print, defaults to ${chalk.underline('index.html')}.
52 |
53 | ${chalk.bold('-o')} or ${chalk.bold('--output')} ${chalk.italic('pdffile')}
54 | Specify the name of the PDF file in output, defaults to ${chalk.underline('index.pdf')}.
55 |
56 | ${chalk.bold('--version')}
57 | Display the version number.
58 |
59 | ${chalk.bold('--help')}
60 | Display the documentation.
61 |
62 | ${chalk.bold('FILES')}
63 |
64 | File ${chalk.underline('onepunch.json')} defines config settings as key-value pairs in JSON format.
65 | The following keys are provided:
66 |
67 | ${chalk.bold('width')}:
68 | Describe the slide width in pixels, defaults to 960.
69 |
70 | ${chalk.bold('height')}:
71 | Describe the slide height in pixels, defaults to 600.
72 |
73 | ${chalk.bold('progress')}:
74 | Describe the presentation progress. At the moment only values "line"
75 | and "none" are supported.
76 |
77 | ${chalk.bold('date')}:
78 | Define the text content of HTML elements with data attribute
79 | data-onepunch="date", such as .
80 |
81 | ${chalk.bold('slideNumber')}:
82 | If true, show the slide number in HTML elements with data attribute
83 | data-onepunch="slide-number", such as .
84 |
85 | ${chalk.bold('LICENSE')}
86 |
87 | Copyright 2020 Nextbit (https://nextbit.it/)
88 |
89 | Permission is hereby granted, free of charge, to any person obtaining a copy
90 | of this software and associated documentation files (the "Software"), to deal
91 | in the Software without restriction, including without limitation the rights
92 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
93 | copies of the Software, and to permit persons to whom the Software is
94 | furnished to do so, subject to the following conditions:
95 |
96 | The above copyright notice and this permission notice shall be included in
97 | all copies or substantial portions of the Software.
98 |
99 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
100 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
101 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
102 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
103 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
104 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
105 | THE SOFTWARE.
106 | `,
107 | flags: {
108 | name: {
109 | type: 'string',
110 | default: 'onepunch-presentation',
111 | alias: 'n',
112 | },
113 | input: {
114 | type: 'string',
115 | default: 'index.html',
116 | alias: 'i',
117 | },
118 | output: {
119 | type: 'string',
120 | default: 'index.pdf',
121 | alias: 'o',
122 | },
123 | },
124 | });
125 |
126 | switch (cli.input[0]) {
127 | case 'init':
128 | init(cli.flags);
129 | break;
130 | case 'serve':
131 | serve(cli.flags);
132 | break;
133 | case 'print':
134 | print(cli.flags);
135 | break;
136 | case 'update':
137 | update();
138 | break;
139 | default:
140 | cli.showHelp();
141 | break;
142 | }
143 |
144 | function serve(flags) {
145 | liveServer.start({
146 | port: 8180,
147 | root: process.cwd(),
148 | open: `/${flags.input}`,
149 | logLevel: 1,
150 | });
151 | }
152 |
153 | function init(flags) {
154 | const spinner = ora('Initializing ...').start();
155 | const {name} = flags;
156 | const presentationPath = path.resolve(name);
157 |
158 | if (fs.existsSync(presentationPath)) {
159 | abort(`The directory ${chalk.underline(name)} is already existing.`, spinner);
160 | }
161 |
162 | fs.mkdirSync(presentationPath);
163 | fs.copySync(path.resolve(__dirname, 'template'), presentationPath);
164 | spinner.succeed(`Directory ${chalk.underline(name)} has been initialized.`);
165 | }
166 |
167 | function update() {
168 | const spinner = ora('Updating ...').start();
169 | const file = 'onepunch.json';
170 |
171 | if (!fs.existsSync(file)) {
172 | abort(`File ${chalk.underline('onepunch.json')} is not present. Are you sure this is the right directory?`, spinner);
173 | }
174 |
175 | fs.copySync(
176 | path.resolve(__dirname, 'template/src'),
177 | 'src',
178 | {overwrite: true},
179 | );
180 | spinner.succeed(`Directory ${chalk.underline('src')} has been updated to release ${cli.pkg.version}.`);
181 | }
182 |
183 | function print(flags) {
184 | const spinner = ora('Printing ...').start();
185 | const {input, output} = flags;
186 |
187 | liveServer.start({
188 | port: 8181,
189 | root: process.cwd(),
190 | open: false,
191 | logLevel: 0,
192 | });
193 |
194 | const config = JSON.parse(fs.readFileSync('onepunch.json'));
195 | const width = config.width || 960;
196 | const height = config.height || 600;
197 |
198 | (async () => {
199 | const browser = await puppeteer.launch();
200 | const page = await browser.newPage();
201 | await page.goto(
202 | `http://127.0.0.1:8181/${input}`,
203 | {waitUntil: 'networkidle0'},
204 | );
205 | await page.pdf({
206 | path: output,
207 | width,
208 | height,
209 | printBackground: true,
210 | });
211 | await browser.close();
212 | liveServer.shutdown();
213 | spinner.succeed(`File ${chalk.underline(output)} has been successfully created.`);
214 | })();
215 | }
216 |
217 | function abort(message, spinner = null) {
218 | if (spinner) {
219 | spinner.fail(message);
220 | } else {
221 | console.error(message);
222 | }
223 |
224 | console.error('Aborting.');
225 | process.exit(1);
226 | }
227 |
--------------------------------------------------------------------------------