1 | 'use strict';
|
2 |
|
3 | const path = require('path');
|
4 | const fs = require('fs-extra');
|
5 | const {assign, defaults, isFunction, isObject, intersection, keys} = require('lodash');
|
6 |
|
7 | const chalk = require('chalk');
|
8 | const sourceInliner = require('inline-critical');
|
9 | const Bluebird = require('bluebird');
|
10 | const through2 = require('through2');
|
11 | const PluginError = require('plugin-error');
|
12 | const replaceExtension = require('replace-ext');
|
13 |
|
14 | const {cleanup} = require('./lib/gc');
|
15 | const core = require('./lib/core');
|
16 | const file = require('./lib/file-helper');
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | function prepareOptions(opts) {
|
24 | if (!opts) {
|
25 | opts = {};
|
26 | }
|
27 |
|
28 | const options = defaults(opts, {
|
29 | base: file.guessBasePath(opts),
|
30 | dimensions: [{
|
31 | height: opts.height || 900,
|
32 | width: opts.width || 1300
|
33 | }]
|
34 | });
|
35 |
|
36 |
|
37 | if (options.dest && !path.isAbsolute(options.dest)) {
|
38 | options.dest = path.join(options.base, options.dest);
|
39 | }
|
40 |
|
41 |
|
42 | if (options.destFolder && !path.isAbsolute(options.destFolder)) {
|
43 | options.destFolder = path.join(options.base, options.destFolder);
|
44 | }
|
45 |
|
46 | if (!options.inline && options.extract) {
|
47 | console.error(chalk.red('The extract option requires inline:true'));
|
48 | }
|
49 |
|
50 |
|
51 | options.inline = Boolean(options.inline) && assign({
|
52 | minify: opts.minify || false,
|
53 | extract: opts.extract || false,
|
54 | basePath: opts.base || process.cwd()
|
55 | }, (isObject(options.inline) && options.inline) || {});
|
56 |
|
57 |
|
58 | options.penthouse = assign({}, {
|
59 | forceInclude: opts.include || [],
|
60 | timeout: opts.timeout || 30000,
|
61 | maxEmbeddedBase64Length: opts.maxImageFileSize || 10240
|
62 | }, options.penthouse || {});
|
63 |
|
64 |
|
65 | const checkOpts = intersection(keys(options.penthouse), ['url', 'css', 'width', 'height']);
|
66 | if (checkOpts.length > 0) {
|
67 | console.warn(chalk.yellow('Detected presence of penthouse options:'), checkOpts.join(', '));
|
68 | console.warn(chalk.yellow('These options will be overwritten by critical during the process.'));
|
69 | }
|
70 |
|
71 |
|
72 | options.dimensions = options.dimensions.slice().sort((a, b) => (a.width || 0) - (b.width || 0));
|
73 |
|
74 | return options;
|
75 | }
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 | exports.generate = (opts, cb) => {
|
85 | opts = prepareOptions(opts);
|
86 |
|
87 |
|
88 | let corePromise = core.generate(opts);
|
89 |
|
90 |
|
91 | if (opts.styleTarget) {
|
92 | corePromise = corePromise.then(output => fs.outputFile(path.resolve(opts.styleTarget), output).then(() => output));
|
93 | }
|
94 |
|
95 |
|
96 | if (opts.inline) {
|
97 | corePromise = Promise.all([file.getVinylPromise(opts), corePromise])
|
98 | .then(([file, css]) => {
|
99 | if (css) {
|
100 | return sourceInliner(file.contents.toString(), css, opts.inline);
|
101 | }
|
102 |
|
103 | return file.contents.toString();
|
104 | });
|
105 | }
|
106 |
|
107 |
|
108 | if (opts.dest) {
|
109 | corePromise = corePromise.then(output => fs.outputFile(path.resolve(opts.dest), output).then(() => output));
|
110 | }
|
111 |
|
112 |
|
113 | if (isFunction(cb)) {
|
114 | corePromise.catch(error => {
|
115 | cleanup();
|
116 | cb(error);
|
117 | throw new Bluebird.CancellationError();
|
118 | }).then(output => {
|
119 | cleanup();
|
120 | cb(null, output.toString());
|
121 | }).catch(Bluebird.CancellationError, () => {
|
122 | console.log('Canceled due to an error');
|
123 |
|
124 | });
|
125 | } else {
|
126 | return corePromise.then(output => {
|
127 | cleanup();
|
128 | return output;
|
129 | });
|
130 | }
|
131 | };
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | exports.generateInline = () => {
|
137 | throw new Error('"generateInline" has been removed. Use "generate" with the inline option instead. https://goo.gl/7VbE4b');
|
138 | };
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | exports.inline = () => {
|
144 | throw new Error('"inline" has been removed. Consider using "inline-critical" instead. https://goo.gl/MmTrUZ');
|
145 | };
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 | exports.stream = opts => {
|
154 |
|
155 | return through2.obj(function (file, enc, cb) {
|
156 | if (file.isNull()) {
|
157 | return cb(null, file);
|
158 | }
|
159 |
|
160 | if (file.isStream()) {
|
161 | return this.emit('error', new PluginError('critical', 'Streaming not supported'));
|
162 | }
|
163 |
|
164 | const options = assign(opts || {}, {
|
165 | src: file
|
166 | });
|
167 |
|
168 | exports.generate(options, (err, data) => {
|
169 | if (err) {
|
170 | return cb(new PluginError('critical', err.message));
|
171 | }
|
172 |
|
173 |
|
174 | if (!opts.inline) {
|
175 | file.path = replaceExtension(file.path, '.css');
|
176 | }
|
177 |
|
178 | file.contents = Buffer.from(data);
|
179 | cb(err, file);
|
180 | });
|
181 | });
|
182 | };
|