/
SASSAsset.js
129 lines (114 loc) Β· 3.4 KB
/
SASSAsset.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const Asset = require('../Asset');
const localRequire = require('../utils/localRequire');
const promisify = require('../utils/promisify');
const path = require('path');
const os = require('os');
const Resolver = require('../Resolver');
const parseCSSImport = require('../utils/parseCSSImport');
class SASSAsset extends Asset {
constructor(name, options) {
super(name, options);
this.type = 'css';
}
async parse(code) {
// node-sass or dart-sass should be installed locally in the module that's being required
let sass = await getSassRuntime(this.name);
let render = promisify(sass.render.bind(sass));
const resolver = new Resolver({
extensions: ['.scss', '.sass'],
rootDir: this.options.rootDir
});
let opts =
(await this.getConfig(['.sassrc', '.sassrc.js'], {packageKey: 'sass'})) ||
{};
opts.includePaths = (opts.includePaths
? opts.includePaths.map(includePath => path.resolve(includePath))
: []
).concat(path.dirname(this.name));
opts.data = opts.data ? opts.data + os.EOL + code : code;
let type = this.options.rendition
? this.options.rendition.type
: path
.extname(this.name)
.toLowerCase()
.replace('.', '');
opts.indentedSyntax =
typeof opts.indentedSyntax === 'boolean'
? opts.indentedSyntax
: type === 'sass';
opts.functions = Object.assign({}, opts.functions, {
'url($url)': url => {
let filename = this.addURLDependency(url.getValue());
return new sass.types.String(`url(${JSON.stringify(filename)})`);
}
});
opts.importer = opts.importer || [];
opts.importer = Array.isArray(opts.importer)
? opts.importer
: [opts.importer];
opts.importer.push((url, prev, done) => {
url = parseCSSImport(url);
resolver
.resolve(url, prev === 'stdin' ? this.name : prev)
.then(resolved => resolved.path)
.catch(() => url)
.then(file => done({file}))
.catch(err => done(normalizeError(err)));
});
try {
return await render(opts);
} catch (err) {
// Format the error so it can be handled by parcel's prettyError
if (err.formatted) {
throw sassToCodeFrame(err);
}
// Throw original error if there is no codeFrame
throw err;
}
}
collectDependencies() {
for (let dep of this.ast.stats.includedFiles) {
this.addDependency(dep, {includedInParent: true});
}
}
generate() {
return [
{
type: 'css',
value: this.ast ? this.ast.css.toString() : '',
hasDependencies: false
}
];
}
}
module.exports = SASSAsset;
async function getSassRuntime(searchPath) {
try {
return await localRequire('node-sass', searchPath, true);
} catch (e) {
// If node-sass is not used locally, install dart-sass, as this causes no freezing issues
return await localRequire('sass', searchPath);
}
}
function sassToCodeFrame(err) {
let error = new Error(err.message);
error.codeFrame = err.formatted;
error.stack = err.stack;
error.fileName = err.file;
error.loc = {
line: err.line,
column: err.column
};
return error;
}
// Ensures an error inherits from Error
function normalizeError(err) {
let message = 'Unknown error';
if (err) {
if (err instanceof Error) {
return err;
}
message = err.stack || err.message || err;
}
return new Error(message);
}