├── .editorconfig ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── lib ├── css │ ├── a1.css │ ├── a2.css │ ├── a3.css │ ├── a4.css │ ├── a5.css │ ├── legal.css │ ├── letter.css │ ├── pdf.css │ └── tabloid.css ├── module.defaults.js └── module.js ├── package.json ├── playground ├── .eslintrc.js ├── .gitignore ├── assets │ └── css │ │ └── tailwind.css ├── content │ └── article.md ├── jsconfig.json ├── layout │ └── default.vue ├── nuxt.config.js ├── package.json ├── pages │ ├── README.md │ ├── article.vue │ ├── docs.vue │ └── index.vue ├── tailwind.config.js └── yarn.lock └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Christian Hansen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nuxt PDF [WIP] 2 | 3 | > **Looking for new maintainer.** Since i don't work in the nuxt universe as much anymore, please write to me if you wish to take over this project. 4 | 5 | [![npm](https://img.shields.io/npm/dt/nuxt-pdfsvg?style=flat-square)](https://npmjs.com/package/nuxt-pdf) 6 | [![npm (scoped with tag)](https://img.shields.io/npm/v/nuxt-pdf/latest.svg?style=flat-square)](https://npmjs.com/package/nuxt-pdf) 7 | [![License](https://img.shields.io/npm/l/nuxt-pdf?style=flat-square)](http://standardjs.com) 8 | 9 | > Generate PDF files directly from your content on your website, can be used for offline downloadable documentation pages. 10 | 11 | ## Features 12 | 13 | - Create PDF from Vue template 14 | - Automatic PDF Generation 15 | - Customizable Metadata 16 | - Supports (A1, A2, A3, A4, A5, Letter, Legal, Tabloid) 17 | - Support dynamic routes (Nuxt Generate) 18 | - Support dynamic titles (from tag) 19 | - I18n support for specific languages 20 | - Generates as you edit (Automatic PDF regeneration) 21 | - For **NUXT 2.x** and higher 22 | 23 | ## Table of Contents 24 | 25 | - [Installation](#installation) 26 | - [Usage](#usage) 27 | - [Configuration](#configuration) 28 | - [Development](#development) 29 | - [License](#license) 30 | 31 | ## Installation 32 | 33 | ```shell 34 | npm install nuxt-pdf --save-dev 35 | ``` 36 | 37 | or 38 | 39 | ```shell 40 | yarn add -D nuxt-pdf 41 | ``` 42 | 43 | ## Usage 44 | 45 | - Add the class `.page` to your page to display when printing, for formatting, add classes: `.a1`, `.a2`, `.a3`, `.a4`, `.a5`, `.letter`, `.legal`, or `.tabloid` 46 | 47 | - Add `nuxt-pdf` to the `buildModules` section of your `nuxt.config.js` file: 48 | 49 | ```js 50 | buildModules: ['nuxt-pdf'] 51 | ``` 52 | 53 | - Add a custom configuration with the `pdf` property. 54 | 55 | You can see the available options in the example [configuration](#configuration) 56 | 57 | ```js 58 | // nuxt.config.js 59 | 60 | { 61 | buildModules: [ 62 | 'nuxt-pdf' 63 | ], 64 | pdf: { 65 | // custom configuration 66 | } 67 | } 68 | ``` 69 | 70 | ## Configuration 71 | 72 | ```javascript 73 | // nuxt.config.js 74 | 75 | { 76 | pdf: { 77 | /* 78 | * Output folder for generated pdf. 79 | */ 80 | dir: "static", 81 | 82 | /* 83 | * Function options for page.pdf([options]) 84 | * Read more: https://pptr.dev/#?product=Puppeteer&version=v2.0.0&show=api-pagepdfoptions 85 | */ 86 | pdf: { 87 | // Change the format of the pdfs. 88 | format: "A4", // This is optional 89 | printBackground: true // Include background in pdf. 90 | } 91 | 92 | /* 93 | * Function options for page.setViewport([options]) 94 | * Read more: https://pptr.dev/#?product=Puppeteer&version=v2.0.0&show=api-pagesetviewportviewport 95 | */ 96 | viewport: { 97 | // override the default viewport 98 | width: 1280, 99 | height: 800 100 | }, 101 | 102 | /* 103 | * Enable i18n support. 104 | */ 105 | i18n: false, 106 | 107 | /* 108 | * Add options to the puppeteer launch. 109 | * Read more: https://pptr.dev/#?product=Puppeteer&version=v2.0.0&show=api-puppeteerlaunchoptions 110 | */ 111 | puppeteer: { 112 | // Puppeteer options here... E.g. env: {} 113 | }, 114 | 115 | /* 116 | * PDF Meta configuration. (inspired by vue-meta) 117 | */ 118 | meta: { 119 | title: "My Module", 120 | titleTemplate: "Documentation ─ %s", 121 | 122 | author: "Christian Hansen", 123 | subject: "Example", 124 | 125 | producer: "Example Inc.", 126 | 127 | // Control the date the file is created. 128 | creationDate: new Date(), 129 | 130 | keywords: ["pdf", "nuxt"] 131 | }, 132 | 133 | /* 134 | * PDF generation routes. (expanding nuxt.generate) 135 | */ 136 | routes: [ 137 | { 138 | // Output file inside output folder. 139 | file: "downloads/documentation.pdf", 140 | 141 | // Route to content that should be converted into pdf. 142 | route: "docs", 143 | 144 | // Default option is to remove the route after generation so it is not accessible 145 | keep: true, // defaults to false 146 | 147 | // Specifify language for pdf. (Only when i18n is enabled!) 148 | locale: 'da', 149 | 150 | // Override global meta with individual meta for each pdf. 151 | meta: { 152 | title: "Home" 153 | }, 154 | pdf: { 155 | // route specific pdf options 156 | landscape: true // Include background in pdf. 157 | }, 158 | viewport: { 159 | // route specific viewport 160 | width: 1280, 161 | height: 800 162 | }, 163 | }, 164 | { 165 | // Output: static/downloads/documentation-vue.pdf 166 | file: "downloads/documentation-vue.pdf", 167 | 168 | // Will generate route https://localhost:3000/docs/vue 169 | route: "docs/vue", 170 | 171 | // Title will be Documentation - Vue 172 | meta: { 173 | title: "Vue" 174 | } 175 | } 176 | ] 177 | } 178 | } 179 | ``` 180 | 181 | - PDF generation 182 | 183 | PDFs will be generated when running `nuxt build`, `nuxt generate` or in development `nuxt dev` 184 | 185 | ## Development 186 | 187 | ```bash 188 | $ git clone https://github.com/ch99q/nuxt-pdf.git 189 | 190 | $ cd nuxt-pdf 191 | 192 | $ yarn 193 | ``` 194 | 195 | ## License 196 | 197 | [MIT License](./LICENSE) 198 | -------------------------------------------------------------------------------- /lib/css/a1.css: -------------------------------------------------------------------------------- 1 | @page { 2 | format: A1; 3 | size: 594mm 841mm; 4 | margin: 0mm 0mm; 5 | } 6 | 7 | @media print { 8 | .page { 9 | width: 594mm !important; 10 | height: 840.75mm !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/css/a2.css: -------------------------------------------------------------------------------- 1 | @page { 2 | format: A2; 3 | size: 420mm 594mm; 4 | margin: 0mm 0mm; 5 | } 6 | 7 | @media print { 8 | .page { 9 | width: 420mm !important; 10 | height: 594mm !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/css/a3.css: -------------------------------------------------------------------------------- 1 | @page { 2 | format: A3; 3 | size: 297mm 420mm; 4 | margin: 0mm 0mm; 5 | } 6 | 7 | @media print { 8 | .page { 9 | width: 297mm !important; 10 | height: 420mm !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/css/a4.css: -------------------------------------------------------------------------------- 1 | @page { 2 | format: A4; 3 | size: 210mm 297mm; 4 | margin: 0mm 0mm; 5 | } 6 | 7 | @media print { 8 | .page { 9 | width: 210mm !important; 10 | height: 296.75mm !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/css/a5.css: -------------------------------------------------------------------------------- 1 | @page { 2 | format: A5; 3 | size: 148mm 210mm; 4 | margin: 0mm 0mm; 5 | } 6 | 7 | @media print { 8 | .page { 9 | width: 148mm !important; 10 | height: 209.75mm !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/css/legal.css: -------------------------------------------------------------------------------- 1 | @page { 2 | format: Legal; 3 | size: 8.5in 14in; 4 | margin: 0in 0in; 5 | } 6 | 7 | @media print { 8 | .page { 9 | width: 8.5in !important; 10 | height: 13.97in !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/css/letter.css: -------------------------------------------------------------------------------- 1 | @page { 2 | format: Letter; 3 | size: 8.5in 11in; 4 | margin: 0in 0in; 5 | } 6 | 7 | @media print { 8 | .page { 9 | width: 8.5in !important; 10 | height: 10.97in !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/css/pdf.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | html, 3 | body { 4 | margin: 0 !important; 5 | padding: 0 !important; 6 | visibility: hidden; 7 | } 8 | 9 | .page { 10 | position: relative; 11 | overflow: hidden; 12 | 13 | visibility: visible; 14 | 15 | padding: 0 !important; 16 | margin: 0 !important; 17 | 18 | page-break-after: auto; 19 | page-break-inside: avoid !important; 20 | 21 | border: none !important; 22 | border-radius: 0 !important; 23 | box-shadow: none !important; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/css/tabloid.css: -------------------------------------------------------------------------------- 1 | @page { 2 | format: Tabloid; 3 | size: 11in 17in; 4 | margin: 0in 0in; 5 | } 6 | 7 | @media print { 8 | .page { 9 | width: 11in !important; 10 | height: 16.97in !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/module.defaults.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dir: 'static', 3 | 4 | pdf: { 5 | format: 'A4', 6 | }, 7 | 8 | i18n: false, 9 | 10 | meta: { 11 | title: '', 12 | titleTemplate: '%s', 13 | subject: '', 14 | author: '', 15 | producer: '', 16 | keywords: [], 17 | }, 18 | 19 | routes: [], 20 | } 21 | -------------------------------------------------------------------------------- /lib/module.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const { 5 | PDFDocument: Document 6 | } = require('pdf-lib') 7 | 8 | const chalk = require('chalk') 9 | const puppeteer = require('puppeteer') 10 | 11 | const defaults = require('./module.defaults') 12 | 13 | const supportedFormats = ['a1', 'a2', 'a3', 'a4', 'a5', 'letter', 'legal', 'tabloid'] 14 | 15 | export const promisifyRoute = function promisifyRoute(fn, ...args) { 16 | // If routes is an array 17 | if (Array.isArray(fn)) { 18 | return Promise.resolve(fn) 19 | } 20 | // If routes is a function expecting a callback 21 | if (fn.length === arguments.length) { 22 | return new Promise((resolve, reject) => { 23 | fn((err, routeParams) => { 24 | if (err) { 25 | reject(err) 26 | } 27 | resolve(routeParams) 28 | }, ...args) 29 | }) 30 | } 31 | let promise = fn(...args) 32 | if ( 33 | !promise || 34 | (!(promise instanceof Promise) && typeof promise.then !== 'function') 35 | ) { 36 | promise = Promise.resolve(promise) 37 | } 38 | return promise 39 | } 40 | 41 | async function timeout(ms) { 42 | return new Promise(resolve => setTimeout(resolve, ms)); 43 | } 44 | 45 | module.exports = async function PDF(moduleOptions) { 46 | 47 | const options = Object.assign({}, defaults, moduleOptions, this.options.pdf) 48 | 49 | const i18n = { 50 | enabled: options.i18n, 51 | domains: {}, 52 | options: {} 53 | } 54 | 55 | if (i18n.enabled) { 56 | i18n.options = Object.assign({}, 57 | this.options.i18n, 58 | (this.options.modules.find( 59 | x => Array.isArray(x) && x[0] === 'nuxt-i18n' 60 | ) || [('', {})])[1], 61 | (this.options.buildModules.find( 62 | x => Array.isArray(x) && x[0] === 'nuxt-i18n' 63 | ) || [('', {})])[1] 64 | ) 65 | 66 | for (const locale of i18n.options.locales) { 67 | if ('domain' in locale && 'code' in locale) { 68 | i18n.domains[locale.code] = locale.domain 69 | } 70 | } 71 | } 72 | 73 | var routeMap 74 | var url 75 | 76 | /* 77 | * Add pdf styling to render. 78 | */ 79 | this.options.css.push(path.resolve(__dirname, 'css/pdf.css')) 80 | 81 | if (options.pdf.format) { 82 | const format = options.pdf.format.toLowerCase() 83 | 84 | if (supportedFormats.includes(format)) { 85 | this.options.css.push(path.resolve(__dirname, 'css/' + format + '.css')) 86 | } else { 87 | console.error( 88 | chalk.bgRed.black(' ERROR ') + 89 | " Unable to find format ('" + 90 | options.pdf.format + 91 | "')" 92 | ) 93 | return 94 | } 95 | } 96 | 97 | this.nuxt.hook('listen', (_, router) => (url = router.url.toString())) 98 | this.nuxt.hook('build:compile', () => { 99 | routeMap = require(path.resolve(this.options.buildDir, 'routes.json')) 100 | }) 101 | 102 | /* 103 | * Extending the generated routes with pdf requested routes. 104 | */ 105 | this.nuxt.hook('generate:extendRoutes', async routes => { 106 | 107 | const generatedRoutes = await promisifyRoute(options.routes || []) 108 | 109 | for (let i = 0; i < generatedRoutes.length; i++) { 110 | const route = generatedRoutes[i] 111 | 112 | if (routes.filter(r => r.route === route.route).length > 0) { 113 | continue 114 | } 115 | 116 | routes.push({ 117 | route: route.route, 118 | payload: null 119 | }) 120 | } 121 | }) 122 | 123 | const getUrl = (path, locale) => { 124 | 125 | const chunk = routeMap.find( 126 | route => route.path == path.split('?')[0].split('#')[0] 127 | ) 128 | 129 | //if (chunk === undefined && path != '/*/') { 130 | // return getUrl('/*/', locale) 131 | //} 132 | 133 | if (chunk === undefined) 134 | throw new Error( 135 | 'Unable to find route at ' + path.split('?')[0].split('#')[0] 136 | ) 137 | 138 | const routes = routeMap.filter(route => route.chunkName == chunk.chunkName) 139 | 140 | var uri = '' 141 | var protocol = new URL(url).protocol 142 | 143 | if (i18n.enabled && typeof locale !== 'undefined') { 144 | const routesNameSeparator = i18n.options.routesNameSeparator || '___' 145 | const route = routes.find( 146 | route => 147 | route.name.endsWith( 148 | routesNameSeparator + locale + routesNameSeparator + 'default' 149 | ) || route.name.endsWith(routesNameSeparator + locale) 150 | ) 151 | 152 | if (i18n.options.differentDomains) { 153 | if (locale in i18n.domains) { 154 | uri = 155 | protocol + 156 | '//' + 157 | i18n.domains[locale].replace(/\/$/, '') + 158 | path 159 | } else { 160 | uri = url.replace(/\/$/, '') + path 161 | } 162 | } else { 163 | uri = url.replace(/\/$/, '') + path 164 | } 165 | } else { 166 | uri = url.replace(/\/$/, '') + path 167 | } 168 | return uri 169 | } 170 | 171 | async function build(buildArgs) { 172 | 173 | var nuxt 174 | var listener 175 | try { 176 | if (buildArgs.generated) { 177 | console.log('nuxt-pdf: Starting nuxt instance') 178 | let nuxt = require.resolve("nuxt-edge") ? 'nuxt-edge' : 'nuxt'; 179 | const { 180 | loadNuxt 181 | } = require(nuxt); 182 | nuxt = await loadNuxt('start') 183 | listener = await nuxt.server.listen() 184 | } 185 | 186 | url = listener.url; 187 | } catch (e) { 188 | console.error("nuxt-pdf: If this is part of npm run generate be sure to run 'npm run build first'") 189 | } 190 | 191 | const routes = await promisifyRoute(options.routes || []) 192 | 193 | for (let i = 0; i < routes.length; i++) { 194 | const route = routes[i] 195 | console.log(chalk.cyan('↻') + ` Generating PDF ${i+1}:${routes.length} at route ` + route.route) 196 | 197 | try { 198 | // Merge route meta with defaults from config. 199 | const meta = Object.assign({}, options.meta, route.meta) 200 | 201 | let browser = await puppeteer.launch( 202 | Object.assign({ 203 | headless: true 204 | }, options.puppeteer) 205 | ) 206 | 207 | let page = await browser.newPage(); 208 | await page.goto(`${url.replace(/\/$/, "")}${route.route}`, { 209 | waitUntil: 'networkidle2' 210 | }); 211 | 212 | if (options.viewport || route.viewport) { 213 | console.log("setting viewport", Object.assign({}, { 214 | ...options.viewport, 215 | ...route.viewport 216 | })) 217 | page.setViewport(Object.assign({}, { 218 | ...options.viewport, 219 | ...route.viewport 220 | })); 221 | } 222 | // Generate pdf based on dom content. (result by bytes) 223 | const bytes = await page.pdf(Object.assign({}, { 224 | ...options.pdf, 225 | ...route.pdf 226 | })) 227 | 228 | // Load bytes into pdf document, used for manipulating meta of file. 229 | const document = await Document.load(bytes) 230 | 231 | // Set the correct meta for pdf document. 232 | if ('title' in meta && meta.title !== '') { 233 | document.setTitle( 234 | (meta.titleTemplate || '%s').replace('%s', meta.title) 235 | ) 236 | } else { 237 | document.setTitle(await page.title()) 238 | } 239 | 240 | document.setAuthor(meta.author || '') 241 | document.setSubject(meta.subject || '') 242 | document.setProducer(meta.producer || '') 243 | document.setCreationDate(meta.creationDate || new Date()) 244 | document.setKeywords(meta.keywords || []) 245 | 246 | const file = path.resolve(buildArgs.generated ? 'dist' : options.dir, route.file) 247 | 248 | // Create folder where file will be stored. 249 | fs.mkdirSync(file.substring(0, file.lastIndexOf('/')), { 250 | recursive: true 251 | }) 252 | 253 | // Write document to file. 254 | const ws = fs.createWriteStream(file) 255 | ws.write(await document.save()) 256 | ws.end() 257 | console.log(`${chalk.green('✔')} Generated PDF ${i+1}:${routes.length} at file '${file} (${document.getTitle()})`); 258 | if (buildArgs.generated && !route.keep) { 259 | await fs.unlinkSync(`./dist${route.route}/index.html`) 260 | console.log(`${chalk.green('✔')} Removed route index file used for PDF at ${route.route}`); 261 | await fs.rmdirSync(`./dist${route.route}`) 262 | console.log(`${chalk.green('✔')} Removed route directory used for PDF at ${route.route}`); 263 | } 264 | await page.close(); 265 | await browser.close() 266 | 267 | } catch (e) { 268 | console.log(`${chalk.red('𐄂')} Failed to generated PDF ${i+1}:${routes.length} at route ${route.route} error: ${e.message}`); 269 | } 270 | } 271 | 272 | 273 | if (nuxt) { 274 | await listener.close() 275 | } 276 | } 277 | 278 | if (process.env.NODE_ENV !== "production") { 279 | this.nuxt.hook('build:compiled', async ({ 280 | name 281 | }) => { 282 | if (name !== 'server') return 283 | await build({ 284 | generated: false 285 | }) 286 | }) 287 | } else { 288 | this.nuxt.hook('generate:done', async ({ 289 | builder 290 | }) => { 291 | await build({ 292 | generated: true 293 | }) 294 | }) 295 | } 296 | 297 | } 298 | 299 | module.exports.meta = require('../package.json') 300 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-pdf", 3 | "version": "2.0.1", 4 | "description": "NuxtJS module to generate PDF files and manage PDF in your Nuxt application", 5 | "repository": "https://github.com/ch99q/nuxt-pdf", 6 | "homepage": "https://github.com/ch99q/nuxt-pdf", 7 | "author": { 8 | "name": "Christian Hansen" 9 | }, 10 | "keywords": [ 11 | "vue", 12 | "nuxt", 13 | "nuxt.js" 14 | ], 15 | "main": "lib/module.js", 16 | "files": [ 17 | "lib" 18 | ], 19 | "license": "MIT", 20 | "dependencies": { 21 | "chalk": "^4.1.0", 22 | "pdf-lib": "^1.10.0" 23 | }, 24 | "peerDependencies": { 25 | "nuxt": "^2.0.0", 26 | "puppeteer": "^5.0.0" 27 | }, 28 | "devDependencies": { 29 | "nuxt": "^2.14.1", 30 | "puppeteer": "^5.2.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /playground/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint', 9 | }, 10 | extends: [ 11 | '@nuxtjs', 12 | 'prettier', 13 | 'prettier/vue', 14 | 'plugin:prettier/recommended', 15 | 'plugin:nuxt/recommended', 16 | ], 17 | plugins: ['prettier'], 18 | // add your custom rules here 19 | rules: {}, 20 | } 21 | -------------------------------------------------------------------------------- /playground/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | *.pdf 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # Nuxt generate 74 | dist 75 | 76 | # vuepress build output 77 | .vuepress/dist 78 | 79 | # Serverless directories 80 | .serverless 81 | 82 | # IDE / Editor 83 | .idea 84 | 85 | # Service worker 86 | sw.* 87 | 88 | # macOS 89 | .DS_Store 90 | 91 | # Vim swap files 92 | *.swp 93 | -------------------------------------------------------------------------------- /playground/assets/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss/base'; 2 | @import 'tailwindcss/components'; 3 | @import 'tailwindcss/utilities'; 4 | -------------------------------------------------------------------------------- /playground/content/article.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting started 3 | description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.' 4 | --- 5 | 6 | Empower your NuxtJS application with `@nuxtjs/content` module: write in a `content/` directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a **Git-based Headless CMS**. 7 | 8 | ## Writing content 9 | 10 | Learn how to write your `content/`, supporting Markdown, YAML, CSV and JSON: https://content.nuxtjs.org/writing. 11 | 12 | ## Fetching content 13 | 14 | Learn how to fetch your content with `$content`: https://content.nuxtjs.org/fetching. 15 | 16 | ## Displaying content 17 | 18 | Learn how to display your Markdown content with the `<nuxt-content>` component directly in your template: https://content.nuxtjs.org/displaying. 19 | -------------------------------------------------------------------------------- /playground/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "~/*": ["./*"], 6 | "@/*": ["./*"], 7 | "~~/*": ["./*"], 8 | "@@/*": ["./*"] 9 | } 10 | }, 11 | "exclude": ["node_modules", ".nuxt", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /playground/layout/default.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <nuxt /> 3 | </template> 4 | 5 | <script> 6 | export default { 7 | head() { 8 | return this.$nuxtI18nSeo() 9 | }, 10 | } 11 | </script> 12 | -------------------------------------------------------------------------------- /playground/nuxt.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /* 3 | ** Nuxt rendering mode 4 | ** See https://nuxtjs.org/api/configuration-mode 5 | */ 6 | mode: 'universal', 7 | /* 8 | ** Nuxt target 9 | ** See https://nuxtjs.org/api/configuration-target 10 | */ 11 | // target: 'static', 12 | /* 13 | ** Headers of the page 14 | ** See https://nuxtjs.org/api/configuration-head 15 | */ 16 | head: { 17 | title: process.env.npm_package_name || '', 18 | meta: [ 19 | { charset: 'utf-8' }, 20 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 21 | { 22 | hid: 'description', 23 | name: 'description', 24 | content: process.env.npm_package_description || '', 25 | }, 26 | ], 27 | link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }], 28 | }, 29 | /* 30 | ** Global CSS 31 | */ 32 | css: [], 33 | /* 34 | ** Plugins to load before mounting the App 35 | ** https://nuxtjs.org/guide/plugins 36 | */ 37 | plugins: [], 38 | /* 39 | ** Auto import components 40 | ** See https://nuxtjs.org/api/configuration-components 41 | */ 42 | components: true, 43 | /* 44 | ** Nuxt.js dev-modules 45 | */ 46 | buildModules: [ 47 | // Doc: https://github.com/nuxt-community/eslint-module 48 | '@nuxtjs/eslint-module', 49 | // Doc: https://github.com/nuxt-community/nuxt-tailwindcss 50 | '@nuxtjs/tailwindcss', 51 | 'nuxt-pdf', 52 | ], 53 | /* 54 | ** Nuxt.js modules 55 | */ 56 | modules: [ 57 | // Doc: https://github.com/nuxt/content 58 | '@nuxt/content', 59 | // https://i18n.nuxtjs.org 60 | 'nuxt-i18n', 61 | ], 62 | /* 63 | ** PDF module configuration 64 | */ 65 | pdf: { 66 | i18n: true, 67 | 68 | routes: [ 69 | { 70 | file: 'documentation.pdf', 71 | 72 | route: '/docs', 73 | 74 | meta: { 75 | title: 'Documentation', 76 | }, 77 | }, 78 | { 79 | file: 'dokumentation.pdf', 80 | 81 | route: '/docs?test=true', 82 | 83 | locale: 'da', 84 | }, 85 | { 86 | file: 'article.pdf', 87 | 88 | route: '/article', 89 | }, 90 | ], 91 | }, 92 | /* 93 | ** I18n module configuration 94 | */ 95 | i18n: { 96 | locales: [ 97 | { 98 | name: 'English', 99 | code: 'en', 100 | iso: 'en-US', 101 | file: 'en-US', 102 | domain: process.env.DOMAIN_EN || 'localhost:3000', 103 | }, 104 | { 105 | name: 'Dansk', 106 | code: 'da', 107 | iso: 'da-DK', 108 | file: 'da-DK', 109 | domain: process.env.DOMAIN_DA || 'localhost.dk:3000', 110 | }, 111 | ], 112 | 113 | // differentDomains: true, 114 | 115 | defaultLocale: 'en', 116 | 117 | strategy: 'prefix_and_default', 118 | 119 | seo: true, 120 | 121 | vueI18n: { 122 | fallbackLocale: 'en', 123 | messages: { 124 | en: { 125 | welcome: 'Welcome', 126 | }, 127 | da: { 128 | welcome: 'Velkommen', 129 | }, 130 | }, 131 | }, 132 | }, 133 | /* 134 | ** Content module configuration 135 | ** See https://content.nuxtjs.org/configuration 136 | */ 137 | content: {}, 138 | /* 139 | ** Build configuration 140 | ** See https://nuxtjs.org/api/configuration-build/ 141 | */ 142 | build: {}, 143 | } 144 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "nuxt", 7 | "build": "nuxt build", 8 | "start": "nuxt start", 9 | "generate": "nuxt generate", 10 | "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .", 11 | "lint": "yarn lint:js" 12 | }, 13 | "lint-staged": { 14 | "*.{js,vue}": "eslint" 15 | }, 16 | "husky": { 17 | "hooks": { 18 | "pre-commit": "lint-staged" 19 | } 20 | }, 21 | "dependencies": { 22 | "@nuxt/content": "^1.6.0", 23 | "nuxt": "^2.14.1", 24 | "nuxt-i18n": "^6.15.4" 25 | }, 26 | "devDependencies": { 27 | "@nuxtjs/eslint-config": "^3.1.0", 28 | "@nuxtjs/eslint-module": "^2.0.0", 29 | "@nuxtjs/tailwindcss": "^3.0.0", 30 | "babel-eslint": "^10.1.0", 31 | "eslint": "^7.6.0", 32 | "eslint-config-prettier": "^6.11.0", 33 | "eslint-plugin-nuxt": "^1.0.0", 34 | "eslint-plugin-prettier": "^3.1.4", 35 | "husky": "^4.2.5", 36 | "lint-staged": "^10.2.11", 37 | "nuxt-pdf": "link:../", 38 | "prettier": "^2.0.5" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /playground/pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /playground/pages/article.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <article class="page a4"> 3 | <title>{{ page.title }} 4 |

{{ page.title }}

5 | 6 | 7 | 8 | 9 | 20 | -------------------------------------------------------------------------------- /playground/pages/docs.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 34 | -------------------------------------------------------------------------------- /playground/pages/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /playground/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** TailwindCSS Configuration File 3 | ** 4 | ** Docs: https://tailwindcss.com/docs/configuration 5 | ** Default: https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js 6 | */ 7 | module.exports = { 8 | theme: {}, 9 | variants: {}, 10 | plugins: [], 11 | purge: { 12 | // Learn more on https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css 13 | enabled: process.env.NODE_ENV === 'production', 14 | content: [ 15 | 'components/**/*.vue', 16 | 'layouts/**/*.vue', 17 | 'pages/**/*.vue', 18 | 'plugins/**/*.js', 19 | 'nuxt.config.js' 20 | ] 21 | } 22 | } 23 | --------------------------------------------------------------------------------