Skip to content

Commit 0f7f9e6

Browse files
authored
Add support for plain CSS imports (#436)
Closes #424
1 parent bad88c8 commit 0f7f9e6

File tree

21 files changed

+566
-206
lines changed

21 files changed

+566
-206
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
## 1.11.0
22

3+
* Add support for importing plain CSS files. They can only be imported *without*
4+
an extension—for example, `@import "style"` will import `style.css`. Plain CSS
5+
files imported this way only support standard CSS features, not Sass
6+
extensions.
7+
8+
See [the proposal][css-import] for details.
9+
310
* Add support for CSS's `min()` and `max()` [math functions][]. A `min()` and
411
`max()` call will continue to be parsed as a Sass function if it involves any
512
Sass-specific features like variables or function calls, but if it's valid
@@ -15,6 +22,7 @@
1522
[the proposal][identifier-escapes] for details.
1623

1724
[math functions]: https://drafts.csswg.org/css-values/#math-function
25+
[css-import]: https://github.com/sass/language/blob/master/accepted/css-imports.md
1826
[css-min-max]: https://github.com/sass/language/blob/master/accepted/min-max.md
1927
[media-ranges]: https://github.com/sass/language/blob/master/accepted/media-ranges.md
2028
[identifier-escapes]: https://github.com/sass/language/blob/master/accepted/identifier-escapes.md
@@ -24,6 +32,23 @@
2432
* The `--watch` command now continues to recompile a file after a syntax error
2533
has been detected.
2634

35+
### Dart API
36+
37+
* Added a `Syntax` enum to indicate syntaxes for Sass source files.
38+
39+
* The `compile()` and `compileAsync()` functions now parse files with the `.css`
40+
extension as plain CSS.
41+
42+
* Added a `syntax` parameter to `compileString()` and `compileStringAsync()`.
43+
44+
* Deprecated the `indented` parameter to `compileString()` and `compileStringAsync()`.
45+
46+
* Added a `syntax` parameter to `new ImporterResult()` and a
47+
`ImporterResult.syntax` getter to set the syntax of the source file.
48+
49+
* Deprecated the `indented` parameter to `new ImporterResult()` and the
50+
`ImporterResult.indented` getter in favor of `syntax`.
51+
2752
## 1.10.4
2853

2954
### Command-Line Interface

lib/sass.dart

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import 'src/exception.dart';
1515
import 'src/importer.dart';
1616
import 'src/logger.dart';
1717
import 'src/sync_package_resolver.dart';
18+
import 'src/syntax.dart';
1819
import 'src/visitor/serialize.dart';
1920

2021
export 'src/callable.dart' show Callable, AsyncCallable;
2122
export 'src/importer.dart';
2223
export 'src/logger.dart';
24+
export 'src/syntax.dart';
2325
export 'src/value.dart' show ListSeparator;
2426
export 'src/value/external/value.dart';
2527
export 'src/visitor/serialize.dart' show OutputStyle;
@@ -94,8 +96,7 @@ String compile(String path,
9496

9597
/// Compiles [source] to CSS and returns the result.
9698
///
97-
/// If [indented] is `true`, this parses [source] using indented syntax;
98-
/// otherwise (and by default) it uses SCSS.
99+
/// This parses the stylesheet as [syntax], which defaults to [Syntax.scss].
99100
///
100101
/// If [color] is `true`, this will use terminal colors in warnings. It's
101102
/// ignored if [logger] is passed.
@@ -147,7 +148,7 @@ String compile(String path,
147148
///
148149
/// Throws a [SassException] if conversion fails.
149150
String compileString(String source,
150-
{bool indented: false,
151+
{Syntax syntax,
151152
bool color: false,
152153
Logger logger,
153154
Iterable<Importer> importers,
@@ -157,9 +158,10 @@ String compileString(String source,
157158
OutputStyle style,
158159
Importer importer,
159160
url,
160-
void sourceMap(SingleMapping map)}) {
161+
void sourceMap(SingleMapping map),
162+
@Deprecated("Use syntax instead.") bool indented: false}) {
161163
var result = c.compileString(source,
162-
indented: indented,
164+
syntax: syntax ?? (indented ? Syntax.sass : Syntax.scss),
163165
logger: logger ?? new Logger.stderr(color: color),
164166
importers: importers,
165167
packageResolver: packageResolver,
@@ -205,7 +207,7 @@ Future<String> compileAsync(String path,
205207
/// synchronous [Importer]s. However, running asynchronously is also somewhat
206208
/// slower, so [compileString] should be preferred if possible.
207209
Future<String> compileStringAsync(String source,
208-
{bool indented: false,
210+
{Syntax syntax,
209211
bool color: false,
210212
Logger logger,
211213
Iterable<AsyncImporter> importers,
@@ -215,9 +217,10 @@ Future<String> compileStringAsync(String source,
215217
OutputStyle style,
216218
AsyncImporter importer,
217219
url,
218-
void sourceMap(SingleMapping map)}) async {
220+
void sourceMap(SingleMapping map),
221+
@Deprecated("Use syntax instead.") bool indented: false}) async {
219222
var result = await c.compileStringAsync(source,
220-
indented: indented,
223+
syntax: syntax ?? (indented ? Syntax.sass : Syntax.scss),
221224
logger: logger ?? new Logger.stderr(color: color),
222225
importers: importers,
223226
packageResolver: packageResolver,

lib/src/ast/sass/statement/stylesheet.dart

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import 'package:source_span/source_span.dart';
66

77
import '../../../visitor/interface/statement.dart';
88
import '../../../logger.dart';
9+
import '../../../parse/css.dart';
910
import '../../../parse/sass.dart';
1011
import '../../../parse/scss.dart';
12+
import '../../../syntax.dart';
1113
import '../statement.dart';
1214
import 'parent.dart';
1315

@@ -17,9 +19,31 @@ import 'parent.dart';
1719
class Stylesheet extends ParentStatement {
1820
final FileSpan span;
1921

20-
Stylesheet(Iterable<Statement> children, this.span)
22+
/// Whether this was parsed from a plain CSS stylesheet.
23+
final bool plainCss;
24+
25+
Stylesheet(Iterable<Statement> children, this.span, {this.plainCss: false})
2126
: super(new List.unmodifiable(children));
2227

28+
/// Parses a stylesheet from [contents] according to [syntax].
29+
///
30+
/// If passed, [url] is the name of the file from which [contents] comes.
31+
///
32+
/// Throws a [SassFormatException] if parsing fails.
33+
factory Stylesheet.parse(String contents, Syntax syntax,
34+
{url, Logger logger}) {
35+
switch (syntax) {
36+
case Syntax.sass:
37+
return new Stylesheet.parseSass(contents, url: url, logger: logger);
38+
case Syntax.scss:
39+
return new Stylesheet.parseScss(contents, url: url, logger: logger);
40+
case Syntax.css:
41+
return new Stylesheet.parseCss(contents, url: url, logger: logger);
42+
default:
43+
throw new ArgumentError("Unknown syntax $syntax.");
44+
}
45+
}
46+
2347
/// Parses an indented-syntax stylesheet from [contents].
2448
///
2549
/// If passed, [url] is the name of the file from which [contents] comes.
@@ -36,6 +60,14 @@ class Stylesheet extends ParentStatement {
3660
factory Stylesheet.parseScss(String contents, {url, Logger logger}) =>
3761
new ScssParser(contents, url: url, logger: logger).parse();
3862

63+
/// Parses a plain CSS stylesheet from [contents].
64+
///
65+
/// If passed, [url] is the name of the file from which [contents] comes.
66+
///
67+
/// Throws a [SassFormatException] if parsing fails.
68+
factory Stylesheet.parseCss(String contents, {url, Logger logger}) =>
69+
new CssParser(contents, url: url, logger: logger).parse();
70+
3971
T accept<T>(StatementVisitor<T> visitor) => visitor.visitStylesheet(this);
4072

4173
String toString() => children.join(" ");

lib/src/ast/selector/list.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,20 @@ class SelectorList extends Selector {
4949
/// Parses a selector list from [contents].
5050
///
5151
/// If passed, [url] is the name of the file from which [contents] comes.
52-
/// [allowParent] controls whether a [ParentSelector] is allowed in this
53-
/// selector.
52+
/// [allowParent] and [allowPlaceholder] control whether [ParentSelector]s or
53+
/// [PlaceholderSelector]s are allowed in this selector, respectively.
5454
///
5555
/// Throws a [SassFormatException] if parsing fails.
5656
factory SelectorList.parse(String contents,
57-
{url, Logger logger, bool allowParent: true}) =>
57+
{url,
58+
Logger logger,
59+
bool allowParent: true,
60+
bool allowPlaceholder: true}) =>
5861
new SelectorParser(contents,
59-
url: url, logger: logger, allowParent: allowParent)
62+
url: url,
63+
logger: logger,
64+
allowParent: allowParent,
65+
allowPlaceholder: allowPlaceholder)
6066
.parse();
6167

6268
T accept<T>(SelectorVisitor<T> visitor) => visitor.visitSelectorList(this);

lib/src/async_import_cache.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,8 @@ class AsyncImportCache {
152152
var displayUrl = originalUrl == null
153153
? canonicalUrl
154154
: originalUrl.resolve(p.url.basename(canonicalUrl.path));
155-
return result.isIndented
156-
? new Stylesheet.parseSass(result.contents,
157-
url: displayUrl, logger: _logger)
158-
: new Stylesheet.parseScss(result.contents,
159-
url: displayUrl, logger: _logger);
155+
return new Stylesheet.parse(result.contents, result.syntax,
156+
url: displayUrl, logger: _logger);
160157
});
161158
}
162159

lib/src/compile.dart

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ import 'importer/node.dart';
1717
import 'io.dart';
1818
import 'logger.dart';
1919
import 'sync_package_resolver.dart';
20+
import 'syntax.dart';
2021
import 'visitor/async_evaluate.dart';
2122
import 'visitor/evaluate.dart';
2223
import 'visitor/serialize.dart';
2324

2425
/// Like [compile] in `lib/sass.dart`, but provides more options to support the
2526
/// node-sass compatible API.
2627
CompileResult compile(String path,
27-
{bool indented,
28+
{Syntax syntax,
2829
Logger logger,
2930
Iterable<Importer> importers,
3031
NodeImporter nodeImporter,
@@ -37,7 +38,7 @@ CompileResult compile(String path,
3738
LineFeed lineFeed,
3839
bool sourceMap: false}) =>
3940
compileString(readFile(path),
40-
indented: indented ?? p.extension(path) == '.sass',
41+
syntax: syntax ?? Syntax.forPath(path),
4142
logger: logger,
4243
functions: functions,
4344
importers: importers,
@@ -55,7 +56,7 @@ CompileResult compile(String path,
5556
/// Like [compileString] in `lib/sass.dart`, but provides more options to support
5657
/// the node-sass compatible API.
5758
CompileResult compileString(String source,
58-
{bool indented: false,
59+
{Syntax syntax,
5960
Logger logger,
6061
Iterable<Importer> importers,
6162
NodeImporter nodeImporter,
@@ -69,11 +70,10 @@ CompileResult compileString(String source,
6970
LineFeed lineFeed,
7071
url,
7172
bool sourceMap: false}) {
72-
var sassTree = indented
73-
? new Stylesheet.parseSass(source, url: url, logger: logger)
74-
: new Stylesheet.parseScss(source, url: url, logger: logger);
73+
var stylesheet = new Stylesheet.parse(source, syntax ?? Syntax.scss,
74+
url: url, logger: logger);
7575

76-
var evaluateResult = evaluate(sassTree,
76+
var evaluateResult = evaluate(stylesheet,
7777
importCache: new ImportCache(importers,
7878
loadPaths: loadPaths,
7979
packageResolver: packageResolver,
@@ -97,7 +97,7 @@ CompileResult compileString(String source,
9797
/// Like [compileAsync] in `lib/sass.dart`, but provides more options to support
9898
/// the node-sass compatible API.
9999
Future<CompileResult> compileAsync(String path,
100-
{bool indented,
100+
{Syntax syntax,
101101
Logger logger,
102102
Iterable<AsyncImporter> importers,
103103
NodeImporter nodeImporter,
@@ -110,7 +110,7 @@ Future<CompileResult> compileAsync(String path,
110110
LineFeed lineFeed,
111111
bool sourceMap: false}) =>
112112
compileStringAsync(readFile(path),
113-
indented: indented ?? p.extension(path) == '.sass',
113+
syntax: syntax ?? Syntax.forPath(path),
114114
logger: logger,
115115
importers: importers,
116116
nodeImporter: nodeImporter,
@@ -128,7 +128,7 @@ Future<CompileResult> compileAsync(String path,
128128
/// Like [compileStringAsync] in `lib/sass.dart`, but provides more options to
129129
/// support the node-sass compatible API.
130130
Future<CompileResult> compileStringAsync(String source,
131-
{bool indented: false,
131+
{Syntax syntax,
132132
Logger logger,
133133
Iterable<AsyncImporter> importers,
134134
NodeImporter nodeImporter,
@@ -142,11 +142,10 @@ Future<CompileResult> compileStringAsync(String source,
142142
LineFeed lineFeed,
143143
url,
144144
bool sourceMap: false}) async {
145-
var sassTree = indented
146-
? new Stylesheet.parseSass(source, url: url, logger: logger)
147-
: new Stylesheet.parseScss(source, url: url, logger: logger);
145+
var stylesheet = new Stylesheet.parse(source, syntax ?? Syntax.scss,
146+
url: url, logger: logger);
148147

149-
var evaluateResult = await evaluateAsync(sassTree,
148+
var evaluateResult = await evaluateAsync(stylesheet,
150149
importCache: new AsyncImportCache(importers,
151150
loadPaths: loadPaths,
152151
packageResolver: packageResolver,

lib/src/executable/compile_stylesheet.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import '../async_import_cache.dart';
1414
import '../import_cache.dart';
1515
import '../io.dart';
1616
import '../stylesheet_graph.dart';
17+
import '../syntax.dart';
1718
import '../visitor/async_evaluate.dart';
1819
import '../visitor/evaluate.dart';
1920
import '../visitor/serialize.dart';
@@ -98,9 +99,17 @@ Future<Stylesheet> _parseStylesheet(
9899

99100
var text = source == null ? await readStdin() : readFile(source);
100101
var url = source == null ? null : p.toUri(source);
101-
return options.indented ?? (source != null && p.extension(source) == '.sass')
102-
? new Stylesheet.parseSass(text, url: url, logger: options.logger)
103-
: new Stylesheet.parseScss(text, url: url, logger: options.logger);
102+
103+
Syntax syntax;
104+
if (options.indented == true) {
105+
syntax = Syntax.sass;
106+
} else if (source != null) {
107+
syntax = Syntax.forPath(source);
108+
} else {
109+
syntax = Syntax.scss;
110+
}
111+
112+
return new Stylesheet.parse(text, syntax, url: url, logger: options.logger);
104113
}
105114

106115
/// Writes the source map given by [mapping] to disk (if necessary) according to

lib/src/import_cache.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// DO NOT EDIT. This file was generated from async_import_cache.dart.
66
// See tool/synchronize.dart for details.
77
//
8-
// Checksum: b76ccc1b4de05a239f04d77fe0034795c68498f7
8+
// Checksum: 172219d313175ec88ce1ced2c37ca4b60348a4d2
99

1010
import 'package:path/path.dart' as p;
1111
import 'package:tuple/tuple.dart';
@@ -154,11 +154,8 @@ class ImportCache {
154154
var displayUrl = originalUrl == null
155155
? canonicalUrl
156156
: originalUrl.resolve(p.url.basename(canonicalUrl.path));
157-
return result.isIndented
158-
? new Stylesheet.parseSass(result.contents,
159-
url: displayUrl, logger: _logger)
160-
: new Stylesheet.parseScss(result.contents,
161-
url: displayUrl, logger: _logger);
157+
return new Stylesheet.parse(result.contents, result.syntax,
158+
url: displayUrl, logger: _logger);
162159
});
163160
}
164161

lib/src/importer/filesystem.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:path/path.dart' as p;
66

77
import '../importer.dart';
88
import '../io.dart' as io;
9+
import '../syntax.dart';
910
import 'result.dart';
1011
import 'utils.dart';
1112

@@ -26,7 +27,7 @@ class FilesystemImporter extends Importer {
2627
ImporterResult load(Uri url) {
2728
var path = p.fromUri(url);
2829
return new ImporterResult(io.readFile(path),
29-
sourceMapUrl: url, indented: p.extension(path) == '.sass');
30+
sourceMapUrl: url, syntax: Syntax.forPath(path));
3031
}
3132

3233
DateTime modificationTime(Uri url) => io.modificationTime(p.fromUri(url));

0 commit comments

Comments
 (0)