Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80668 views
1
/**
2
* Copyright 2013 Facebook, Inc.
3
*
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
*
8
* http://www.apache.org/licenses/LICENSE-2.0
9
*
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*/
16
var inherits = require('util').inherits;
17
var zlib = require('zlib');
18
19
var docblock = require('../parse/docblock');
20
var ResourceLoader = require('./ResourceLoader');
21
var extractFBSprites = require('../parse/css').extractFBSprites;
22
var CSS = require('../resource/CSS');
23
var MessageList = require('../MessageList');
24
25
26
/**
27
* @class Loads and parses CSS files
28
* Extracts options from the docblock, calculates gziped network size. Network
29
* size calculation is off by default due to it's perf cost. Use options
30
* parameter to switch them on.
31
*
32
* @extends {ResourceLoader}
33
* @param {Object|null} options Object with the following options:
34
* - extractNetworkSize
35
* - extractFBSprites
36
*/
37
function CSSLoader(options) {
38
ResourceLoader.call(this, options);
39
var extractNetworkSize = !!this.options.networkSize;
40
41
if (extractNetworkSize) {
42
this.extractExtra = this.extractNetworkSize;
43
} else {
44
this.extractExtra = function(css, sourceCode, messages, callback) {
45
// make async to break long stack traces
46
process.nextTick(function() {
47
callback(messages, css);
48
});
49
};
50
}
51
}
52
inherits(CSSLoader, ResourceLoader);
53
CSSLoader.prototype.path = __filename;
54
55
CSSLoader.prototype.getResourceTypes = function() {
56
return [CSS];
57
};
58
59
CSSLoader.prototype.getExtensions = function() {
60
return ['.css'];
61
};
62
63
/**
64
* Extracts aproximate network size by gziping the source
65
* @todo (voloko) why not minify?
66
* Off by default due to perf cost
67
*
68
* @protected
69
* @param {CSS} css
70
* @param {String} sourceCode
71
* @param {Function} callback
72
*/
73
CSSLoader.prototype.extractNetworkSize =
74
function(css, sourceCode, messages, callback) {
75
zlib.deflate(sourceCode, function(err, buffer) {
76
css.networkSize = buffer.length;
77
callback(messages, css);
78
});
79
};
80
81
/**
82
* Initialize a resource with the source code and configuration
83
* Loader can parse, gzip, minify the source code to build the resulting
84
* Resource value object
85
*
86
* @protected
87
* @param {String} path resource being built
88
* @param {ProjectConfiguration} configuration configuration for the path
89
* @param {String} sourceCode
90
* @param {Function} callback
91
*/
92
CSSLoader.prototype.loadFromSource =
93
function(path, configuration, sourceCode, messages, callback) {
94
var css = new CSS(path);
95
96
var props = docblock.parse(docblock.extract(sourceCode));
97
98
props.forEach(function(pair) {
99
var name = pair[0];
100
var value = pair[1];
101
102
switch (name) {
103
case 'provides':
104
css.id = value;
105
break;
106
case 'providesModule':
107
css.isModule = true;
108
css.id = 'css:' + value;
109
break;
110
case 'css':
111
value.split(/\s+/).forEach(css.addRequiredCSS, css);
112
break;
113
case 'requires':
114
value.split(/\s+/).forEach(css.addRequiredLegacyComponent, css);
115
break;
116
case 'nonblocking':
117
css.isNonblocking = true;
118
break;
119
case 'nopackage':
120
css.isNopackage = true;
121
break;
122
case 'permanent':
123
css.isPermanent = true;
124
break;
125
case 'option':
126
case 'options':
127
value.split(/\s+/).forEach(function(key) {
128
css.options[key] = true;
129
});
130
break;
131
case 'author':
132
case 'deprecated':
133
// Support these so Diviner can pick them up.
134
break;
135
case 'nolint':
136
case 'generated':
137
case 'preserve-header':
138
// various options
139
break;
140
case 'layer':
141
// This directive is currently used by Connect JS library
142
break;
143
default:
144
messages.addClowntownError(css.path, 'docblock',
145
'Unknown directive ' + name);
146
}
147
});
148
149
if (this.options.extractFBSprites) {
150
css.fbSprites = extractFBSprites(sourceCode);
151
}
152
153
css.finalize();
154
155
this.extractExtra(css, sourceCode, messages, callback);
156
};
157
158
159
/**
160
* Only match *.css files
161
* @param {String} filePath
162
* @return {Boolean}
163
*/
164
CSSLoader.prototype.matchPath = function(filePath) {
165
return filePath.lastIndexOf('.css') === filePath.length - 4;
166
};
167
168
/**
169
* Post process is called after the map is updated but before the update
170
* task is complete.
171
* Used to resolve local required paths and /index.js directory requires
172
*
173
* @param {ResourceMap} map
174
* @param {Array.<Resource>} resources
175
* @param {Function} callback
176
*/
177
CSSLoader.prototype.postProcess = function(map, resources, callback) {
178
var messages = MessageList.create();
179
180
resources.forEach(function(r) {
181
var resource, i, required;
182
183
required = r.requiredCSS;
184
for (i = 0; i < required.length; i++) {
185
resource = map.getResource('CSS', 'css:' + required[i]);
186
if (resource && resource.isModule) {
187
required[i] = 'css:' + required[i];
188
}
189
}
190
});
191
192
process.nextTick(function() {
193
callback(messages);
194
});
195
};
196
197
198
module.exports = CSSLoader;
199
200