source: imaps-frontend/node_modules/clean-css/lib/reader/read-sources.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 13.2 KB
RevLine 
[79a0317]1var fs = require('fs');
2var path = require('path');
3
4var applySourceMaps = require('./apply-source-maps');
5var extractImportUrlAndMedia = require('./extract-import-url-and-media');
6var isAllowedResource = require('./is-allowed-resource');
7var loadOriginalSources = require('./load-original-sources');
8var normalizePath = require('./normalize-path');
9var rebase = require('./rebase');
10var rebaseLocalMap = require('./rebase-local-map');
11var rebaseRemoteMap = require('./rebase-remote-map');
12var restoreImport = require('./restore-import');
13
14var tokenize = require('../tokenizer/tokenize');
15var Token = require('../tokenizer/token');
16var Marker = require('../tokenizer/marker');
17var hasProtocol = require('../utils/has-protocol');
18var isImport = require('../utils/is-import');
19var isRemoteResource = require('../utils/is-remote-resource');
20
21var UNKNOWN_URI = 'uri:unknown';
22var FILE_RESOURCE_PROTOCOL = 'file://';
23
24function readSources(input, context, callback) {
25 return doReadSources(input, context, function(tokens) {
26 return applySourceMaps(tokens, context, function() {
27 return loadOriginalSources(context, function() { return callback(tokens); });
28 });
29 });
30}
31
32function doReadSources(input, context, callback) {
33 if (typeof input == 'string') {
34 return fromString(input, context, callback);
35 } if (Buffer.isBuffer(input)) {
36 return fromString(input.toString(), context, callback);
37 } if (Array.isArray(input)) {
38 return fromArray(input, context, callback);
39 } if (typeof input == 'object') {
40 return fromHash(input, context, callback);
41 }
42}
43
44function fromString(input, context, callback) {
45 context.source = undefined;
46 context.sourcesContent[undefined] = input;
47 context.stats.originalSize += input.length;
48
49 return fromStyles(input, context, { inline: context.options.inline }, callback);
50}
51
52function fromArray(input, context, callback) {
53 var inputAsImports = input.reduce(function(accumulator, uriOrHash) {
54 if (typeof uriOrHash === 'string') {
55 return addStringSource(uriOrHash, accumulator);
56 }
57 return addHashSource(uriOrHash, context, accumulator);
58 }, []);
59
60 return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback);
61}
62
63function fromHash(input, context, callback) {
64 var inputAsImports = addHashSource(input, context, []);
65 return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback);
66}
67
68function addStringSource(input, imports) {
69 imports.push(restoreAsImport(normalizeUri(input)));
70 return imports;
71}
72
73function addHashSource(input, context, imports) {
74 var uri;
75 var normalizedUri;
76 var source;
77
78 for (uri in input) {
79 source = input[uri];
80 normalizedUri = normalizeUri(uri);
81
82 imports.push(restoreAsImport(normalizedUri));
83
84 context.sourcesContent[normalizedUri] = source.styles;
85
86 if (source.sourceMap) {
87 trackSourceMap(source.sourceMap, normalizedUri, context);
88 }
89 }
90
91 return imports;
92}
93
94function normalizeUri(uri) {
95 var currentPath = path.resolve('');
96 var absoluteUri;
97 var relativeToCurrentPath;
98 var normalizedUri;
99
100 if (isRemoteResource(uri)) {
101 return uri;
102 }
103
104 absoluteUri = path.isAbsolute(uri)
105 ? uri
106 : path.resolve(uri);
107 relativeToCurrentPath = path.relative(currentPath, absoluteUri);
108 normalizedUri = normalizePath(relativeToCurrentPath);
109
110 return normalizedUri;
111}
112
113function trackSourceMap(sourceMap, uri, context) {
114 var parsedMap = typeof sourceMap == 'string'
115 ? JSON.parse(sourceMap)
116 : sourceMap;
117 var rebasedMap = isRemoteResource(uri)
118 ? rebaseRemoteMap(parsedMap, uri)
119 : rebaseLocalMap(parsedMap, uri || UNKNOWN_URI, context.options.rebaseTo);
120
121 context.inputSourceMapTracker.track(uri, rebasedMap);
122}
123
124function restoreAsImport(uri) {
125 return restoreImport('url(' + uri + ')', '') + Marker.SEMICOLON;
126}
127
128function fromStyles(styles, context, parentInlinerContext, callback) {
129 var tokens;
130 var rebaseConfig = {};
131
132 if (!context.source) {
133 rebaseConfig.fromBase = path.resolve('');
134 rebaseConfig.toBase = context.options.rebaseTo;
135 } else if (isRemoteResource(context.source)) {
136 rebaseConfig.fromBase = context.source;
137 rebaseConfig.toBase = context.source;
138 } else if (path.isAbsolute(context.source)) {
139 rebaseConfig.fromBase = path.dirname(context.source);
140 rebaseConfig.toBase = context.options.rebaseTo;
141 } else {
142 rebaseConfig.fromBase = path.dirname(path.resolve(context.source));
143 rebaseConfig.toBase = context.options.rebaseTo;
144 }
145
146 tokens = tokenize(styles, context);
147 tokens = rebase(tokens, context.options.rebase, context.validator, rebaseConfig);
148
149 return allowsAnyImports(parentInlinerContext.inline)
150 ? inline(tokens, context, parentInlinerContext, callback)
151 : callback(tokens);
152}
153
154function allowsAnyImports(inline) {
155 return !(inline.length == 1 && inline[0] == 'none');
156}
157
158function inline(tokens, externalContext, parentInlinerContext, callback) {
159 var inlinerContext = {
160 afterContent: false,
161 callback: callback,
162 errors: externalContext.errors,
163 externalContext: externalContext,
164 fetch: externalContext.options.fetch,
165 inlinedStylesheets: parentInlinerContext.inlinedStylesheets || externalContext.inlinedStylesheets,
166 inline: parentInlinerContext.inline,
167 inlineRequest: externalContext.options.inlineRequest,
168 inlineTimeout: externalContext.options.inlineTimeout,
169 isRemote: parentInlinerContext.isRemote || false,
170 localOnly: externalContext.localOnly,
171 outputTokens: [],
172 rebaseTo: externalContext.options.rebaseTo,
173 sourceTokens: tokens,
174 warnings: externalContext.warnings
175 };
176
177 return doInlineImports(inlinerContext);
178}
179
180function doInlineImports(inlinerContext) {
181 var token;
182 var i, l;
183
184 for (i = 0, l = inlinerContext.sourceTokens.length; i < l; i++) {
185 token = inlinerContext.sourceTokens[i];
186
187 if (token[0] == Token.AT_RULE && isImport(token[1])) {
188 inlinerContext.sourceTokens.splice(0, i);
189 return inlineStylesheet(token, inlinerContext);
190 } if (token[0] == Token.AT_RULE || token[0] == Token.COMMENT) {
191 inlinerContext.outputTokens.push(token);
192 } else {
193 inlinerContext.outputTokens.push(token);
194 inlinerContext.afterContent = true;
195 }
196 }
197
198 inlinerContext.sourceTokens = [];
199 return inlinerContext.callback(inlinerContext.outputTokens);
200}
201
202function inlineStylesheet(token, inlinerContext) {
203 var uriAndMediaQuery = extractImportUrlAndMedia(token[1]);
204 var uri = uriAndMediaQuery[0];
205 var mediaQuery = uriAndMediaQuery[1];
206 var metadata = token[2];
207
208 return isRemoteResource(uri)
209 ? inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext)
210 : inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext);
211}
212
213function inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext) {
214 var isAllowed = isAllowedResource(uri, true, inlinerContext.inline);
215 var originalUri = uri;
216 var isLoaded = uri in inlinerContext.externalContext.sourcesContent;
217 var isRuntimeResource = !hasProtocol(uri);
218
219 if (inlinerContext.inlinedStylesheets.indexOf(uri) > -1) {
220 inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as it has already been imported.');
221 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
222 return doInlineImports(inlinerContext);
223 } if (inlinerContext.localOnly && inlinerContext.afterContent) {
224 inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as no callback given and after other content.');
225 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
226 return doInlineImports(inlinerContext);
227 } if (isRuntimeResource) {
228 inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as no protocol given.');
229 inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
230 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
231 return doInlineImports(inlinerContext);
232 } if (inlinerContext.localOnly && !isLoaded) {
233 inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as no callback given.');
234 inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
235 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
236 return doInlineImports(inlinerContext);
237 } if (!isAllowed && inlinerContext.afterContent) {
238 inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as resource is not allowed and after other content.');
239 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
240 return doInlineImports(inlinerContext);
241 } if (!isAllowed) {
242 inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as resource is not allowed.');
243 inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
244 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
245 return doInlineImports(inlinerContext);
246 }
247
248 inlinerContext.inlinedStylesheets.push(uri);
249
250 function whenLoaded(error, importedStyles) {
251 if (error) {
252 inlinerContext.errors.push('Broken @import declaration of "' + uri + '" - ' + error);
253
254 return process.nextTick(function() {
255 inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
256 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
257 doInlineImports(inlinerContext);
258 });
259 }
260
261 inlinerContext.inline = inlinerContext.externalContext.options.inline;
262 inlinerContext.isRemote = true;
263
264 inlinerContext.externalContext.source = originalUri;
265 inlinerContext.externalContext.sourcesContent[uri] = importedStyles;
266 inlinerContext.externalContext.stats.originalSize += importedStyles.length;
267
268 return fromStyles(importedStyles, inlinerContext.externalContext, inlinerContext, function(importedTokens) {
269 importedTokens = wrapInMedia(importedTokens, mediaQuery, metadata);
270
271 inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens);
272 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
273
274 return doInlineImports(inlinerContext);
275 });
276 }
277
278 return isLoaded
279 ? whenLoaded(null, inlinerContext.externalContext.sourcesContent[uri])
280 : inlinerContext.fetch(uri, inlinerContext.inlineRequest, inlinerContext.inlineTimeout, whenLoaded);
281}
282
283function inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext) {
284 var protocolLessUri = uri.replace(FILE_RESOURCE_PROTOCOL, '');
285 var currentPath = path.resolve('');
286 var absoluteUri = path.isAbsolute(protocolLessUri)
287 ? path.resolve(currentPath, protocolLessUri[0] == '/' ? protocolLessUri.substring(1) : protocolLessUri)
288 : path.resolve(inlinerContext.rebaseTo, protocolLessUri);
289 var relativeToCurrentPath = path.relative(currentPath, absoluteUri);
290 var importedStyles;
291 var isAllowed = isAllowedResource(protocolLessUri, false, inlinerContext.inline);
292 var normalizedPath = normalizePath(relativeToCurrentPath);
293 var isLoaded = normalizedPath in inlinerContext.externalContext.sourcesContent;
294
295 if (inlinerContext.inlinedStylesheets.indexOf(absoluteUri) > -1) {
296 inlinerContext.warnings.push('Ignoring local @import of "' + protocolLessUri + '" as it has already been imported.');
297 } else if (isAllowed && !isLoaded && (!fs.existsSync(absoluteUri) || !fs.statSync(absoluteUri).isFile())) {
298 inlinerContext.errors.push('Ignoring local @import of "' + protocolLessUri + '" as resource is missing.');
299 } else if (!isAllowed && inlinerContext.afterContent) {
300 inlinerContext.warnings.push('Ignoring local @import of "' + protocolLessUri + '" as resource is not allowed and after other content.');
301 } else if (inlinerContext.afterContent) {
302 inlinerContext.warnings.push('Ignoring local @import of "' + protocolLessUri + '" as after other content.');
303 } else if (!isAllowed) {
304 inlinerContext.warnings.push('Skipping local @import of "' + protocolLessUri + '" as resource is not allowed.');
305 inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
306 } else {
307 importedStyles = isLoaded
308 ? inlinerContext.externalContext.sourcesContent[normalizedPath]
309 : fs.readFileSync(absoluteUri, 'utf-8');
310
311 if (importedStyles.charCodeAt(0) === 65279) {
312 importedStyles = importedStyles.substring(1);
313 }
314
315 inlinerContext.inlinedStylesheets.push(absoluteUri);
316 inlinerContext.inline = inlinerContext.externalContext.options.inline;
317
318 inlinerContext.externalContext.source = normalizedPath;
319 inlinerContext.externalContext.sourcesContent[normalizedPath] = importedStyles;
320 inlinerContext.externalContext.stats.originalSize += importedStyles.length;
321
322 return fromStyles(importedStyles, inlinerContext.externalContext, inlinerContext, function(importedTokens) {
323 importedTokens = wrapInMedia(importedTokens, mediaQuery, metadata);
324
325 inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens);
326 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
327
328 return doInlineImports(inlinerContext);
329 });
330 }
331
332 inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
333
334 return doInlineImports(inlinerContext);
335}
336
337function wrapInMedia(tokens, mediaQuery, metadata) {
338 if (mediaQuery) {
339 return [[Token.NESTED_BLOCK, [[Token.NESTED_BLOCK_SCOPE, '@media ' + mediaQuery, metadata]], tokens]];
340 }
341 return tokens;
342}
343
344module.exports = readSources;
Note: See TracBrowser for help on using the repository browser.