1 |
|
2 |
|
3 |
|
4 |
|
5 | 'use strict';
|
6 |
|
7 | var path = require('path'),
|
8 | fs = require('fs'),
|
9 | loaderUtils = require('loader-utils'),
|
10 | camelcase = require('camelcase'),
|
11 | SourceMapConsumer = require('source-map').SourceMapConsumer;
|
12 |
|
13 | var adjustSourceMap = require('adjust-sourcemap-loader/lib/process');
|
14 |
|
15 | var valueProcessor = require('./lib/value-processor');
|
16 | var joinFn = require('./lib/join-function');
|
17 | var logToTestHarness = require('./lib/log-to-test-harness');
|
18 |
|
19 | var PACKAGE_NAME = require('./package.json').name;
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | function resolveUrlLoader(content, sourceMap) {
|
29 |
|
30 |
|
31 |
|
32 | var loader = this;
|
33 |
|
34 |
|
35 | if (/^\./.test(loader.context)) {
|
36 | return handleAsError(
|
37 | 'webpack misconfiguration',
|
38 | 'loader.context is relative, expected absolute'
|
39 | );
|
40 | }
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | var options = Object.assign(
|
47 | {
|
48 | sourceMap: loader.sourceMap,
|
49 | engine : 'postcss',
|
50 | silent : false,
|
51 | absolute : false,
|
52 | keepQuery: false,
|
53 | removeCR : false,
|
54 | root : false,
|
55 | debug : false,
|
56 | join : joinFn.defaultJoin
|
57 | },
|
58 | !!loader.options && loader.options[camelcase(PACKAGE_NAME)],
|
59 | loaderUtils.getOptions(loader)
|
60 | );
|
61 |
|
62 |
|
63 | logToTestHarness(options);
|
64 |
|
65 |
|
66 | if ('attempts' in options) {
|
67 | handleAsWarning(
|
68 | 'loader misconfiguration',
|
69 | '"attempts" option is defunct (consider "join" option if search is needed)'
|
70 | );
|
71 | }
|
72 | if ('includeRoot' in options) {
|
73 | handleAsWarning(
|
74 | 'loader misconfiguration',
|
75 | '"includeRoot" option is defunct (consider "join" option if search is needed)'
|
76 | );
|
77 | }
|
78 | if ('fail' in options) {
|
79 | handleAsWarning(
|
80 | 'loader misconfiguration',
|
81 | '"fail" option is defunct'
|
82 | );
|
83 | }
|
84 |
|
85 |
|
86 | if (typeof options.join !== 'function') {
|
87 | return handleAsError(
|
88 | 'loader misconfiguration',
|
89 | '"join" option must be a Function'
|
90 | );
|
91 | } else if (options.join.length !== 2) {
|
92 | return handleAsError(
|
93 | 'loader misconfiguration',
|
94 | '"join" Function must take exactly 2 arguments (filename and options hash)'
|
95 | );
|
96 | }
|
97 |
|
98 |
|
99 | if (typeof options.root === 'string') {
|
100 | var isValid = (options.root === '') ||
|
101 | (path.isAbsolute(options.root) && fs.existsSync(options.root) && fs.statSync(options.root).isDirectory());
|
102 |
|
103 | if (!isValid) {
|
104 | return handleAsError(
|
105 | 'loader misconfiguration',
|
106 | '"root" option must be an empty string or an absolute path to an existing directory'
|
107 | );
|
108 | }
|
109 | } else if (options.root !== false) {
|
110 | handleAsWarning(
|
111 | 'loader misconfiguration',
|
112 | '"root" option must be string where used or false where unused'
|
113 | );
|
114 | }
|
115 |
|
116 |
|
117 | loader.cacheable();
|
118 |
|
119 |
|
120 | var sourceMapConsumer, absSourceMap;
|
121 | if (sourceMap) {
|
122 |
|
123 |
|
124 | if (typeof sourceMap === 'string') {
|
125 | try {
|
126 | sourceMap = JSON.parse(sourceMap);
|
127 | }
|
128 | catch (exception) {
|
129 | return handleAsError(
|
130 | 'source-map error',
|
131 | 'cannot parse source-map string (from less-loader?)'
|
132 | );
|
133 | }
|
134 | }
|
135 |
|
136 |
|
137 |
|
138 | try {
|
139 | absSourceMap = adjustSourceMap(loader, {format: 'absolute'}, sourceMap);
|
140 | }
|
141 | catch (exception) {
|
142 | return handleAsError(
|
143 | 'source-map error',
|
144 | exception.message
|
145 | );
|
146 | }
|
147 |
|
148 |
|
149 | sourceMapConsumer = new SourceMapConsumer(absSourceMap);
|
150 | }
|
151 |
|
152 |
|
153 | var enginePath = /^\w+$/.test(options.engine) && path.join(__dirname, 'lib', 'engine', options.engine + '.js');
|
154 | var isValidEngine = fs.existsSync(enginePath);
|
155 | if (!isValidEngine) {
|
156 | return handleAsError(
|
157 | 'loader misconfiguration',
|
158 | '"engine" option is not valid'
|
159 | );
|
160 | }
|
161 |
|
162 |
|
163 | var callback = loader.async();
|
164 | Promise
|
165 | .resolve(require(enginePath)(loader.resourcePath, content, {
|
166 | outputSourceMap : !!options.sourceMap,
|
167 | transformDeclaration: valueProcessor(loader.resourcePath, options),
|
168 | absSourceMap : absSourceMap,
|
169 | sourceMapConsumer : sourceMapConsumer,
|
170 | removeCR : options.removeCR
|
171 | }))
|
172 | .catch(onFailure)
|
173 | .then(onSuccess);
|
174 |
|
175 | function onFailure(error) {
|
176 | callback(encodeError('CSS error', error));
|
177 | }
|
178 |
|
179 | function onSuccess(reworked) {
|
180 | if (reworked) {
|
181 |
|
182 |
|
183 | if (options.sourceMap) {
|
184 | var finalMap = adjustSourceMap(loader, {format: 'sourceRelative'}, reworked.map);
|
185 | callback(null, reworked.content, finalMap);
|
186 | }
|
187 |
|
188 | else {
|
189 | callback(null, reworked.content);
|
190 | }
|
191 | }
|
192 | }
|
193 |
|
194 | |
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 | function handleAsWarning(label, exception) {
|
201 | if (!options.silent) {
|
202 | loader.emitWarning(encodeError(label, exception));
|
203 | }
|
204 | return content;
|
205 | }
|
206 |
|
207 | |
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 | function handleAsError(label, exception) {
|
214 | loader.emitError(encodeError(label, exception));
|
215 | return content;
|
216 | }
|
217 |
|
218 | function encodeError(label, exception) {
|
219 | return new Error(
|
220 | [
|
221 | PACKAGE_NAME,
|
222 | ': ',
|
223 | [label]
|
224 | .concat(
|
225 | (typeof exception === 'string') && exception ||
|
226 | (exception instanceof Error) && [exception.message, exception.stack.split('\n')[1].trim()] ||
|
227 | []
|
228 | )
|
229 | .filter(Boolean)
|
230 | .join('\n ')
|
231 | ].join('')
|
232 | );
|
233 | }
|
234 | }
|
235 |
|
236 | module.exports = Object.assign(resolveUrlLoader, joinFn);
|