UNPKG

7.66 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
6
7var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
8
9var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
10
11const Asset = require('../Asset');
12
13const postcss = require('postcss');
14
15const valueParser = require('postcss-value-parser');
16
17const postcssTransform = require('../transforms/postcss');
18
19const CssSyntaxError = require('postcss/lib/css-syntax-error');
20
21const SourceMap = require('../SourceMap');
22
23const loadSourceMap = require('../utils/loadSourceMap');
24
25const path = require('path');
26
27const urlJoin = require('../utils/urlJoin');
28
29const isURL = require('../utils/is-url');
30
31const URL_RE = /url\s*\("?(?![a-z]+:)/;
32const IMPORT_RE = /@import/;
33const COMPOSES_RE = /composes:.+from\s*("|').*("|')\s*;?/;
34const FROM_IMPORT_RE = /.+from\s*(?:"|')(.*)(?:"|')\s*;?/;
35const PROTOCOL_RE = /^[a-z]+:/;
36
37class CSSAsset extends Asset {
38 constructor(name, options) {
39 super(name, options);
40 this.type = 'css';
41 this.previousSourceMap = this.options.rendition ? this.options.rendition.map : null;
42 }
43
44 mightHaveDependencies() {
45 return !/\.css$/.test(this.name) || IMPORT_RE.test(this.contents) || COMPOSES_RE.test(this.contents) || URL_RE.test(this.contents);
46 }
47
48 parse(code) {
49 let root = postcss.parse(code, {
50 from: this.name
51 });
52 return new CSSAst(code, root);
53 }
54
55 collectDependencies() {
56 this.ast.root.walkAtRules('import', rule => {
57 let params = valueParser(rule.params);
58
59 let _params$nodes = (0, _toArray2.default)(params.nodes),
60 name = _params$nodes[0],
61 media = _params$nodes.slice(1);
62
63 let dep;
64
65 if (name.type === 'function' && name.value === 'url' && name.nodes.length) {
66 name = name.nodes[0];
67 }
68
69 dep = name.value;
70
71 if (!dep) {
72 throw new Error('Could not find import name for ' + rule);
73 }
74
75 if (PROTOCOL_RE.test(dep)) {
76 return;
77 } // If this came from an inline <style> tag, don't inline the imported file. Replace with the correct URL instead.
78 // TODO: run CSSPackager on inline style tags.
79
80
81 let inlineHTML = this.options.rendition && this.options.rendition.inlineHTML;
82
83 if (inlineHTML) {
84 name.value = this.addURLDependency(dep, {
85 loc: rule.source.start
86 });
87 rule.params = params.toString();
88 } else {
89 media = valueParser.stringify(media).trim();
90 this.addDependency(dep, {
91 media,
92 loc: rule.source.start
93 });
94 rule.remove();
95 }
96
97 this.ast.dirty = true;
98 });
99 this.ast.root.walkDecls(decl => {
100 if (URL_RE.test(decl.value)) {
101 let parsed = valueParser(decl.value);
102 let dirty = false;
103 parsed.walk(node => {
104 if (node.type === 'function' && node.value === 'url' && node.nodes.length) {
105 let url = this.addURLDependency(node.nodes[0].value, {
106 loc: decl.source.start
107 });
108
109 if (!isURL(url)) {
110 url = urlJoin(this.options.publicURL, url);
111 }
112
113 dirty = node.nodes[0].value !== url;
114 node.nodes[0].value = url;
115 }
116 });
117
118 if (dirty) {
119 decl.value = parsed.toString();
120 this.ast.dirty = true;
121 }
122 }
123
124 if (decl.prop === 'composes' && FROM_IMPORT_RE.test(decl.value)) {
125 let parsed = valueParser(decl.value);
126 parsed.walk(node => {
127 if (node.type === 'string') {
128 const _FROM_IMPORT_RE$exec = FROM_IMPORT_RE.exec(decl.value),
129 _FROM_IMPORT_RE$exec2 = (0, _slicedToArray2.default)(_FROM_IMPORT_RE$exec, 2),
130 importPath = _FROM_IMPORT_RE$exec2[1];
131
132 this.addURLDependency(importPath, {
133 dynamic: false,
134 loc: decl.source.start
135 });
136 }
137 });
138 }
139 });
140 }
141
142 pretransform() {
143 var _this = this;
144
145 return (0, _asyncToGenerator2.default)(function* () {
146 if (_this.options.sourceMaps && !_this.previousSourceMap) {
147 _this.previousSourceMap = yield loadSourceMap(_this);
148 }
149 })();
150 }
151
152 transform() {
153 var _this2 = this;
154
155 return (0, _asyncToGenerator2.default)(function* () {
156 yield postcssTransform(_this2);
157 })();
158 }
159
160 getCSSAst() {
161 // Converts the ast to a CSS ast if needed, so we can apply postcss transforms.
162 if (!(this.ast instanceof CSSAst)) {
163 this.ast = CSSAsset.prototype.parse.call(this, this.ast.render(this.name));
164 }
165
166 return this.ast.root;
167 }
168
169 generate() {
170 var _this3 = this;
171
172 return (0, _asyncToGenerator2.default)(function* () {
173 let css;
174
175 if (_this3.ast) {
176 let result = _this3.ast.render(_this3.name);
177
178 css = result.css;
179 if (result.map) _this3.sourceMap = result.map;
180 } else {
181 css = _this3.contents;
182 }
183
184 let js = '';
185
186 if (_this3.options.hmr) {
187 _this3.addDependency('_css_loader');
188
189 js = `
190 var reloadCSS = require('_css_loader');
191 module.hot.dispose(reloadCSS);
192 module.hot.accept(reloadCSS);
193 `;
194 }
195
196 if (_this3.cssModules) {
197 js += 'module.exports = ' + JSON.stringify(_this3.cssModules, null, 2) + ';';
198 }
199
200 if (_this3.options.sourceMaps) {
201 if (_this3.sourceMap) {
202 _this3.sourceMap = yield new SourceMap().addMap(_this3.sourceMap);
203 }
204
205 if (_this3.previousSourceMap) {
206 _this3.previousSourceMap.sources = _this3.previousSourceMap.sources.map(v => path.join(path.dirname(_this3.relativeName), _this3.previousSourceMap.sourceRoot || '', v));
207
208 if (_this3.sourceMap) {
209 _this3.sourceMap = yield new SourceMap().extendSourceMap(_this3.previousSourceMap, _this3.sourceMap);
210 } else {
211 _this3.sourceMap = yield new SourceMap().addMap(_this3.previousSourceMap);
212 }
213 } else if (!_this3.sourceMap) {
214 _this3.sourceMap = new SourceMap().generateEmptyMap(_this3.relativeName, css);
215 }
216 }
217
218 return [{
219 type: 'css',
220 value: css,
221 cssModules: _this3.cssModules,
222 map: _this3.sourceMap
223 }, {
224 type: 'js',
225 value: js,
226 hasDependencies: false
227 }];
228 })();
229 }
230
231 generateErrorMessage(err) {
232 // Wrap the error in a CssSyntaxError if needed so we can generate a code frame
233 if (err.loc && !err.showSourceCode) {
234 err = new CssSyntaxError(err.message, err.loc.line, err.loc.column, this.contents);
235 }
236
237 err.message = err.reason || err.message;
238 err.loc = {
239 line: err.line,
240 column: err.column
241 };
242
243 if (err.showSourceCode) {
244 err.codeFrame = err.showSourceCode();
245 err.highlightedCodeFrame = err.showSourceCode(true);
246 }
247
248 return err;
249 }
250
251}
252
253class CSSAst {
254 constructor(css, root) {
255 this.css = css;
256 this.root = root;
257 this.dirty = false;
258 }
259
260 render(name) {
261 if (this.dirty) {
262 let _this$root$toResult = this.root.toResult({
263 to: name,
264 map: {
265 inline: false,
266 annotation: false,
267 sourcesContent: true
268 }
269 }),
270 css = _this$root$toResult.css,
271 map = _this$root$toResult.map;
272
273 this.css = css;
274 return {
275 css: this.css,
276 map: map ? map.toJSON() : null
277 };
278 }
279
280 return {
281 css: this.css
282 };
283 }
284
285}
286
287module.exports = CSSAsset;
\No newline at end of file