/
compile_stylesheet.dart
181 lines (164 loc) · 6.14 KB
/
compile_stylesheet.dart
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Copyright 2016 Google Inc. Use of this source code is governed by an
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:convert';
import 'package:path/path.dart' as p;
import 'package:source_maps/source_maps.dart';
import '../async_import_cache.dart';
import '../compile.dart';
import '../compile_result.dart';
import '../exception.dart';
import '../importer/filesystem.dart';
import '../io.dart';
import '../stylesheet_graph.dart';
import '../syntax.dart';
import '../utils.dart';
import '../visitor/serialize.dart';
import 'options.dart';
/// Compiles the stylesheet at [source] to [destination].
///
/// Loads files using `graph.importCache` when possible.
///
/// If [source] is `null`, that indicates that the stylesheet should be read
/// from stdin. If [destination] is `null`, that indicates that the stylesheet
/// should be emitted to stdout.
///
/// If [ifModified] is `true`, only recompiles if [source]'s modification time
/// or that of a file it imports is more recent than [destination]'s
/// modification time. Note that these modification times are cached by [graph].
Future<void> compileStylesheet(ExecutableOptions options, StylesheetGraph graph,
String? source, String? destination,
{bool ifModified = false}) async {
var importer = FilesystemImporter('.');
if (ifModified) {
try {
if (source != null &&
destination != null &&
!graph.modifiedSince(
p.toUri(source), modificationTime(destination), importer)) {
return;
}
} on FileSystemException catch (_) {
// Compile as normal if the destination file doesn't exist.
}
}
Syntax syntax;
if (options.indented == true) {
syntax = Syntax.sass;
} else if (source != null) {
syntax = Syntax.forPath(source);
} else {
syntax = Syntax.scss;
}
CompileResult result;
try {
if (options.asynchronous) {
var importCache = AsyncImportCache(
loadPaths: options.loadPaths, logger: options.logger);
result = source == null
? await compileStringAsync(await readStdin(),
syntax: syntax,
logger: options.logger,
importCache: importCache,
importer: FilesystemImporter('.'),
style: options.style,
quietDeps: options.quietDeps,
verbose: options.verbose,
sourceMap: options.emitSourceMap,
charset: options.charset)
: await compileAsync(source,
syntax: syntax,
logger: options.logger,
importCache: importCache,
style: options.style,
quietDeps: options.quietDeps,
verbose: options.verbose,
sourceMap: options.emitSourceMap,
charset: options.charset);
} else {
result = source == null
? compileString(await readStdin(),
syntax: syntax,
logger: options.logger,
importCache: graph.importCache,
importer: FilesystemImporter('.'),
style: options.style,
quietDeps: options.quietDeps,
verbose: options.verbose,
sourceMap: options.emitSourceMap,
charset: options.charset)
: compile(source,
syntax: syntax,
logger: options.logger,
importCache: graph.importCache,
style: options.style,
quietDeps: options.quietDeps,
verbose: options.verbose,
sourceMap: options.emitSourceMap,
charset: options.charset);
}
} on SassException catch (error) {
if (options.emitErrorCss) {
if (destination == null) {
print(error.toCssString());
} else {
ensureDir(p.dirname(destination));
writeFile(destination, error.toCssString() + "\n");
}
}
rethrow;
}
var css = result.css;
css += _writeSourceMap(options, result.sourceMap, destination);
if (destination == null) {
if (css.isNotEmpty) print(css);
} else {
ensureDir(p.dirname(destination));
writeFile(destination, css + "\n");
}
if (options.quiet || (!options.update && !options.watch)) return;
var buffer = StringBuffer();
if (options.color) buffer.write('\u001b[32m');
var sourceName = source == null ? 'stdin' : p.prettyUri(p.toUri(source));
// `destination` is guaranteed to be non-null in update and watch mode.
var destinationName = p.prettyUri(p.toUri(destination!));
buffer.write('Compiled $sourceName to $destinationName.');
if (options.color) buffer.write('\u001b[0m');
print(buffer);
}
/// Writes the source map given by [mapping] to disk (if necessary) according to
/// [options].
///
/// The [destination] is the path where the CSS file associated with this source
/// map will be written. If it's `null`, that indicates that the CSS will be
/// printed to stdout.
///
/// Returns the source map comment to add to the end of the CSS file.
String _writeSourceMap(
ExecutableOptions options, SingleMapping? sourceMap, String? destination) {
if (sourceMap == null) return "";
if (destination != null) {
sourceMap.targetUrl = p.toUri(p.basename(destination)).toString();
}
// TODO(nweiz): Don't explicitly use a type parameter when dart-lang/sdk#25490
// is fixed.
mapInPlace<String>(sourceMap.urls,
(url) => options.sourceMapUrl(Uri.parse(url), destination).toString());
var sourceMapText =
jsonEncode(sourceMap.toJson(includeSourceContents: options.embedSources));
Uri url;
if (options.embedSourceMap) {
url = Uri.dataFromString(sourceMapText,
mimeType: 'application/json', encoding: utf8);
} else {
// [destination] can't be null here because --embed-source-map is
// incompatible with writing to stdout.
var sourceMapPath = destination! + '.map';
ensureDir(p.dirname(sourceMapPath));
writeFile(sourceMapPath, sourceMapText);
url = p.toUri(p.relative(sourceMapPath, from: p.dirname(destination)));
}
var escapedUrl = url.toString().replaceAll("*/", "*\\/");
return (options.style == OutputStyle.compressed ? '' : '\n\n') +
'/*# sourceMappingURL=$escapedUrl */';
}