mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-23 06:12:00 +01:00
Add support for the new importer API (#1522)
This commit is contained in:
parent
4b0f008760
commit
c9e2f96f88
56
lib/src/importer/node_to_dart/async.dart
Normal file
56
lib/src/importer/node_to_dart/async.dart
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2021 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:async';
|
||||
|
||||
import 'package:node_interop/js.dart';
|
||||
import 'package:node_interop/util.dart';
|
||||
|
||||
import '../../node/importer.dart';
|
||||
import '../../node/url.dart';
|
||||
import '../../node/utils.dart';
|
||||
import '../../util/nullable.dart';
|
||||
import '../async.dart';
|
||||
import '../result.dart';
|
||||
|
||||
/// A wrapper for a potentially-asynchronous JS API importer that exposes it as
|
||||
/// a Dart [AsyncImporter].
|
||||
class NodeToDartAsyncImporter extends AsyncImporter {
|
||||
/// The wrapped canonicalize function.
|
||||
final Object? Function(String, CanonicalizeOptions) _canonicalize;
|
||||
|
||||
/// The wrapped load function.
|
||||
final Object? Function(JSUrl) _load;
|
||||
|
||||
NodeToDartAsyncImporter(this._canonicalize, this._load);
|
||||
|
||||
FutureOr<Uri?> canonicalize(Uri url) async {
|
||||
var result = _canonicalize(
|
||||
url.toString(), CanonicalizeOptions(fromImport: fromImport));
|
||||
if (isPromise(result)) result = await promiseToFuture(result as Promise);
|
||||
if (result == null) return null;
|
||||
|
||||
if (isJSUrl(result)) return jsToDartUrl(result as JSUrl);
|
||||
|
||||
jsThrow(JsError("The canonicalize() method must return a URL."));
|
||||
}
|
||||
|
||||
FutureOr<ImporterResult?> load(Uri url) async {
|
||||
var result = _load(dartToJSUrl(url));
|
||||
if (isPromise(result)) result = await promiseToFuture(result as Promise);
|
||||
if (result == null) return null;
|
||||
|
||||
result as NodeImporterResult;
|
||||
var contents = result.contents;
|
||||
var syntax = result.syntax;
|
||||
if (contents == null || syntax == null) {
|
||||
jsThrow(JsError("The load() function must return an object with contents "
|
||||
"and syntax fields."));
|
||||
}
|
||||
|
||||
return ImporterResult(contents,
|
||||
syntax: parseSyntax(syntax),
|
||||
sourceMapUrl: result.sourceMapUrl.andThen(jsToDartUrl));
|
||||
}
|
||||
}
|
80
lib/src/importer/node_to_dart/async_file.dart
Normal file
80
lib/src/importer/node_to_dart/async_file.dart
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2021 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:async';
|
||||
|
||||
import 'package:node_interop/js.dart';
|
||||
import 'package:node_interop/util.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../io.dart' as io;
|
||||
import '../../node/importer.dart';
|
||||
import '../../node/utils.dart';
|
||||
import '../../syntax.dart';
|
||||
import '../async.dart';
|
||||
import '../filesystem.dart';
|
||||
import '../result.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
/// A filesystem importer to use for most implementation details of
|
||||
/// [NodeToDartAsyncFileImporter].
|
||||
///
|
||||
/// This allows us to avoid duplicating logic between the two importers.
|
||||
final _filesystemImporter = FilesystemImporter('.');
|
||||
|
||||
/// A wrapper for a potentially-asynchronous JS API file importer that exposes
|
||||
/// it as a Dart [AsyncImporter].
|
||||
class NodeToDartAsyncFileImporter extends AsyncImporter {
|
||||
/// The wrapped `findFileUrl` function.
|
||||
final Object? Function(String, CanonicalizeOptions) _findFileUrl;
|
||||
|
||||
/// A map from canonical URLs to the `sourceMapUrl`s associated with them.
|
||||
final _sourceMapUrls = <Uri, Uri>{};
|
||||
|
||||
NodeToDartAsyncFileImporter(this._findFileUrl);
|
||||
|
||||
FutureOr<Uri?> canonicalize(Uri url) async {
|
||||
if (url.scheme != 'file' && url.scheme != '') return null;
|
||||
|
||||
var result = _findFileUrl(
|
||||
url.toString(), CanonicalizeOptions(fromImport: fromImport));
|
||||
if (isPromise(result)) result = await promiseToFuture(result as Promise);
|
||||
if (result == null) return null;
|
||||
|
||||
result as NodeFileImporterResult;
|
||||
var dartUrl = result.url;
|
||||
var sourceMapUrl = result.sourceMapUrl;
|
||||
if (dartUrl == null) {
|
||||
jsThrow(JsError(
|
||||
"The findFileUrl() method must return an object a url field."));
|
||||
}
|
||||
|
||||
var resultUrl = jsToDartUrl(dartUrl);
|
||||
if (resultUrl.scheme != 'file') {
|
||||
jsThrow(JsError(
|
||||
'The findFileUrl() must return a URL with scheme file://, was '
|
||||
'"$url".'));
|
||||
}
|
||||
|
||||
var canonical = _filesystemImporter.canonicalize(resultUrl);
|
||||
if (canonical == null) return null;
|
||||
if (sourceMapUrl != null) {
|
||||
_sourceMapUrls[canonical] = jsToDartUrl(sourceMapUrl);
|
||||
}
|
||||
|
||||
return canonical;
|
||||
}
|
||||
|
||||
ImporterResult? load(Uri url) {
|
||||
var path = p.fromUri(url);
|
||||
return ImporterResult(io.readFile(path),
|
||||
sourceMapUrl: _sourceMapUrls[url] ?? url, syntax: Syntax.forPath(path));
|
||||
}
|
||||
|
||||
DateTime modificationTime(Uri url) =>
|
||||
_filesystemImporter.modificationTime(url);
|
||||
|
||||
bool couldCanonicalize(Uri url, Uri canonicalUrl) =>
|
||||
_filesystemImporter.couldCanonicalize(url, canonicalUrl);
|
||||
}
|
82
lib/src/importer/node_to_dart/file.dart
Normal file
82
lib/src/importer/node_to_dart/file.dart
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2021 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 'package:node_interop/js.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../io.dart' as io;
|
||||
import '../../importer.dart';
|
||||
import '../../node/importer.dart';
|
||||
import '../../node/utils.dart';
|
||||
import '../../syntax.dart';
|
||||
import '../filesystem.dart';
|
||||
import '../result.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
/// A filesystem importer to use for most implementation details of
|
||||
/// [NodeToDartAsyncFileImporter].
|
||||
///
|
||||
/// This allows us to avoid duplicating logic between the two importers.
|
||||
final _filesystemImporter = FilesystemImporter('.');
|
||||
|
||||
/// A wrapper for a potentially-asynchronous JS API file importer that exposes
|
||||
/// it as a Dart [AsyncImporter].
|
||||
class NodeToDartFileImporter extends Importer {
|
||||
/// The wrapped `findFileUrl` function.
|
||||
final Object? Function(String, CanonicalizeOptions) _findFileUrl;
|
||||
|
||||
/// A map from canonical URLs to the `sourceMapUrl`s associated with them.
|
||||
final _sourceMapUrls = <Uri, Uri>{};
|
||||
|
||||
NodeToDartFileImporter(this._findFileUrl);
|
||||
|
||||
Uri? canonicalize(Uri url) {
|
||||
if (url.scheme != 'file' && url.scheme != '') return null;
|
||||
|
||||
var result = _findFileUrl(
|
||||
url.toString(), CanonicalizeOptions(fromImport: fromImport));
|
||||
if (result == null) return null;
|
||||
|
||||
if (isPromise(result)) {
|
||||
jsThrow(JsError(
|
||||
"The canonicalize() function can't return a Promise for synchronous "
|
||||
"compile functions."));
|
||||
}
|
||||
|
||||
result as NodeFileImporterResult;
|
||||
var dartUrl = result.url;
|
||||
var sourceMapUrl = result.sourceMapUrl;
|
||||
if (dartUrl == null) {
|
||||
jsThrow(JsError(
|
||||
"The findFileUrl() method must return an object a url field."));
|
||||
}
|
||||
|
||||
var resultUrl = jsToDartUrl(dartUrl);
|
||||
if (resultUrl.scheme != 'file') {
|
||||
jsThrow(JsError(
|
||||
'The findFileUrl() must return a URL with scheme file://, was '
|
||||
'"$url".'));
|
||||
}
|
||||
|
||||
var canonical = _filesystemImporter.canonicalize(resultUrl);
|
||||
if (canonical == null) return null;
|
||||
if (sourceMapUrl != null) {
|
||||
_sourceMapUrls[canonical] = jsToDartUrl(sourceMapUrl);
|
||||
}
|
||||
|
||||
return canonical;
|
||||
}
|
||||
|
||||
ImporterResult? load(Uri url) {
|
||||
var path = p.fromUri(url);
|
||||
return ImporterResult(io.readFile(path),
|
||||
sourceMapUrl: _sourceMapUrls[url] ?? url, syntax: Syntax.forPath(path));
|
||||
}
|
||||
|
||||
DateTime modificationTime(Uri url) =>
|
||||
_filesystemImporter.modificationTime(url);
|
||||
|
||||
bool couldCanonicalize(Uri url, Uri canonicalUrl) =>
|
||||
_filesystemImporter.couldCanonicalize(url, canonicalUrl);
|
||||
}
|
62
lib/src/importer/node_to_dart/sync.dart
Normal file
62
lib/src/importer/node_to_dart/sync.dart
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2021 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 'package:node_interop/js.dart';
|
||||
|
||||
import '../../importer.dart';
|
||||
import '../../node/importer.dart';
|
||||
import '../../node/url.dart';
|
||||
import '../../node/utils.dart';
|
||||
import '../../util/nullable.dart';
|
||||
import '../result.dart';
|
||||
|
||||
/// A wrapper for a synchronous JS API importer that exposes it as a Dart
|
||||
/// [Importer].
|
||||
class NodeToDartImporter extends Importer {
|
||||
/// The wrapped canonicalize function.
|
||||
final Object? Function(String, CanonicalizeOptions) _canonicalize;
|
||||
|
||||
/// The wrapped load function.
|
||||
final Object? Function(JSUrl) _load;
|
||||
|
||||
NodeToDartImporter(this._canonicalize, this._load);
|
||||
|
||||
Uri? canonicalize(Uri url) {
|
||||
var result = _canonicalize(
|
||||
url.toString(), CanonicalizeOptions(fromImport: fromImport));
|
||||
if (result == null) return null;
|
||||
if (isJSUrl(result)) return jsToDartUrl(result as JSUrl);
|
||||
|
||||
if (isPromise(result)) {
|
||||
jsThrow(JsError(
|
||||
"The canonicalize() function can't return a Promise for synchronous "
|
||||
"compile functions."));
|
||||
} else {
|
||||
jsThrow(JsError("The canonicalize() method must return a URL."));
|
||||
}
|
||||
}
|
||||
|
||||
ImporterResult? load(Uri url) {
|
||||
var result = _load(dartToJSUrl(url));
|
||||
if (result == null) return null;
|
||||
|
||||
if (isPromise(result)) {
|
||||
jsThrow(JsError(
|
||||
"The load() function can't return a Promise for synchronous compile "
|
||||
"functions."));
|
||||
}
|
||||
|
||||
result as NodeImporterResult;
|
||||
var contents = result.contents;
|
||||
var syntax = result.syntax;
|
||||
if (contents == null || syntax == null) {
|
||||
jsThrow(JsError("The load() function must return an object with contents "
|
||||
"and syntax fields."));
|
||||
}
|
||||
|
||||
return ImporterResult(contents,
|
||||
syntax: parseSyntax(syntax),
|
||||
sourceMapUrl: result.sourceMapUrl.andThen(jsToDartUrl));
|
||||
}
|
||||
}
|
@ -8,6 +8,10 @@ import 'package:node_interop/util.dart';
|
||||
|
||||
import '../../sass.dart';
|
||||
import '../importer/no_op.dart';
|
||||
import '../importer/node_to_dart/async.dart';
|
||||
import '../importer/node_to_dart/async_file.dart';
|
||||
import '../importer/node_to_dart/file.dart';
|
||||
import '../importer/node_to_dart/sync.dart';
|
||||
import '../io.dart';
|
||||
import '../logger.dart';
|
||||
import '../logger/node_to_dart.dart';
|
||||
@ -15,6 +19,7 @@ import '../util/nullable.dart';
|
||||
import 'compile_options.dart';
|
||||
import 'compile_result.dart';
|
||||
import 'exception.dart';
|
||||
import 'importer.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
/// The JS API `compile` function.
|
||||
@ -31,7 +36,8 @@ NodeCompileResult compile(String path, [CompileOptions? options]) {
|
||||
style: _parseOutputStyle(options?.style),
|
||||
verbose: options?.verbose ?? false,
|
||||
sourceMap: options?.sourceMap ?? false,
|
||||
logger: NodeToDartLogger(options?.logger, Logger.stderr(color: color)));
|
||||
logger: NodeToDartLogger(options?.logger, Logger.stderr(color: color)),
|
||||
importers: options?.importers?.map(_parseImporter));
|
||||
return _convertResult(result);
|
||||
} on SassException catch (error) {
|
||||
throwNodeException(error, color: color);
|
||||
@ -46,16 +52,18 @@ NodeCompileResult compileString(String text, [CompileStringOptions? options]) {
|
||||
var color = options?.alertColor ?? hasTerminal;
|
||||
try {
|
||||
var result = compileStringToResult(text,
|
||||
syntax: _parseSyntax(options?.syntax),
|
||||
syntax: parseSyntax(options?.syntax),
|
||||
url: options?.url.andThen(jsToDartUrl),
|
||||
importer: options?.url == null ? NoOpImporter() : null,
|
||||
color: color,
|
||||
loadPaths: options?.loadPaths,
|
||||
quietDeps: options?.quietDeps ?? false,
|
||||
style: _parseOutputStyle(options?.style),
|
||||
verbose: options?.verbose ?? false,
|
||||
sourceMap: options?.sourceMap ?? false,
|
||||
logger: NodeToDartLogger(options?.logger, Logger.stderr(color: color)));
|
||||
logger: NodeToDartLogger(options?.logger, Logger.stderr(color: color)),
|
||||
importers: options?.importers?.map(_parseImporter),
|
||||
importer: options?.importer.andThen(_parseImporter) ??
|
||||
(options?.url == null ? NoOpImporter() : null));
|
||||
return _convertResult(result);
|
||||
} on SassException catch (error) {
|
||||
throwNodeException(error, color: color);
|
||||
@ -76,7 +84,9 @@ Promise compileAsync(String path, [CompileOptions? options]) {
|
||||
style: _parseOutputStyle(options?.style),
|
||||
verbose: options?.verbose ?? false,
|
||||
sourceMap: options?.sourceMap ?? false,
|
||||
logger: NodeToDartLogger(options?.logger, Logger.stderr(color: color)));
|
||||
logger: NodeToDartLogger(options?.logger, Logger.stderr(color: color)),
|
||||
importers: options?.importers
|
||||
?.map((importer) => _parseAsyncImporter(importer)));
|
||||
return _convertResult(result);
|
||||
}()), color: color);
|
||||
}
|
||||
@ -89,16 +99,20 @@ Promise compileStringAsync(String text, [CompileStringOptions? options]) {
|
||||
var color = options?.alertColor ?? hasTerminal;
|
||||
return _wrapAsyncSassExceptions(futureToPromise(() async {
|
||||
var result = await compileStringToResultAsync(text,
|
||||
syntax: _parseSyntax(options?.syntax),
|
||||
syntax: parseSyntax(options?.syntax),
|
||||
url: options?.url.andThen(jsToDartUrl),
|
||||
importer: options?.url == null ? NoOpImporter() : null,
|
||||
color: color,
|
||||
loadPaths: options?.loadPaths,
|
||||
quietDeps: options?.quietDeps ?? false,
|
||||
style: _parseOutputStyle(options?.style),
|
||||
verbose: options?.verbose ?? false,
|
||||
sourceMap: options?.sourceMap ?? false,
|
||||
logger: NodeToDartLogger(options?.logger, Logger.stderr(color: color)));
|
||||
logger: NodeToDartLogger(options?.logger, Logger.stderr(color: color)),
|
||||
importers: options?.importers
|
||||
?.map((importer) => _parseAsyncImporter(importer)),
|
||||
importer: options?.importer
|
||||
.andThen((importer) => _parseAsyncImporter(importer)) ??
|
||||
(options?.url == null ? NoOpImporter() : null));
|
||||
return _convertResult(result);
|
||||
}()), color: color);
|
||||
}
|
||||
@ -136,10 +150,49 @@ OutputStyle _parseOutputStyle(String? style) {
|
||||
jsThrow(JsError('Unknown output style "$style".'));
|
||||
}
|
||||
|
||||
/// Converts a syntax string to an instance of [Syntax].
|
||||
Syntax _parseSyntax(String? syntax) {
|
||||
if (syntax == null || syntax == 'scss') return Syntax.scss;
|
||||
if (syntax == 'indented') return Syntax.sass;
|
||||
if (syntax == 'css') return Syntax.css;
|
||||
jsThrow(JsError('Unknown syntax "$syntax".'));
|
||||
/// Converts [importer] into an [AsyncImporter] that can be used with
|
||||
/// [compileAsync] or [compileStringAsync].
|
||||
AsyncImporter _parseAsyncImporter(Object? importer) {
|
||||
if (importer == null) jsThrow(JsError("Importers may not be null."));
|
||||
|
||||
importer as NodeImporter;
|
||||
var findFileUrl = importer.findFileUrl;
|
||||
var canonicalize = importer.canonicalize;
|
||||
var load = importer.load;
|
||||
if (findFileUrl == null) {
|
||||
if (canonicalize == null || load == null) {
|
||||
jsThrow(JsError(
|
||||
"An importer must have either canonicalize and load methods, or a "
|
||||
"findFileUrl method."));
|
||||
}
|
||||
return NodeToDartAsyncImporter(canonicalize, load);
|
||||
} else if (canonicalize != null || load != null) {
|
||||
jsThrow(JsError("An importer may not have a findFileUrl method as well as "
|
||||
"canonicalize and load methods."));
|
||||
} else {
|
||||
return NodeToDartAsyncFileImporter(findFileUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts [importer] into a synchronous [Importer].
|
||||
Importer _parseImporter(Object? importer) {
|
||||
if (importer == null) jsThrow(JsError("Importers may not be null."));
|
||||
|
||||
importer as NodeImporter;
|
||||
var findFileUrl = importer.findFileUrl;
|
||||
var canonicalize = importer.canonicalize;
|
||||
var load = importer.load;
|
||||
if (findFileUrl == null) {
|
||||
if (canonicalize == null || load == null) {
|
||||
jsThrow(JsError(
|
||||
"An importer must have either canonicalize and load methods, or a "
|
||||
"findFileUrl method."));
|
||||
}
|
||||
return NodeToDartImporter(canonicalize, load);
|
||||
} else if (canonicalize != null || load != null) {
|
||||
jsThrow(JsError("An importer may not have a findFileUrl method as well as "
|
||||
"canonicalize and load methods."));
|
||||
} else {
|
||||
return NodeToDartFileImporter(findFileUrl);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import 'package:js/js.dart';
|
||||
|
||||
import 'importer.dart';
|
||||
import 'logger.dart';
|
||||
import 'url.dart';
|
||||
|
||||
@ -18,6 +19,7 @@ class CompileOptions {
|
||||
external bool? get verbose;
|
||||
external bool? get sourceMap;
|
||||
external NodeLogger? get logger;
|
||||
external List<Object?>? get importers;
|
||||
}
|
||||
|
||||
@JS()
|
||||
@ -25,4 +27,5 @@ class CompileOptions {
|
||||
class CompileStringOptions extends CompileOptions {
|
||||
external String? get syntax;
|
||||
external JSUrl? get url;
|
||||
external NodeImporter? get importer;
|
||||
}
|
||||
|
39
lib/src/node/importer.dart
Normal file
39
lib/src/node/importer.dart
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2021 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 'package:js/js.dart';
|
||||
|
||||
import 'url.dart';
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class NodeImporter {
|
||||
external Object? Function(String, CanonicalizeOptions)? get canonicalize;
|
||||
external Object? Function(JSUrl)? get load;
|
||||
external NodeFileImporterResult? Function(String, CanonicalizeOptions)?
|
||||
get findFileUrl;
|
||||
}
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class CanonicalizeOptions {
|
||||
external bool get fromImport;
|
||||
|
||||
external factory CanonicalizeOptions({bool fromImport});
|
||||
}
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class NodeImporterResult {
|
||||
external String? get contents;
|
||||
external String? get syntax;
|
||||
external JSUrl? get sourceMapUrl;
|
||||
}
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class NodeFileImporterResult {
|
||||
external JSUrl? get url;
|
||||
external JSUrl? get sourceMapUrl;
|
||||
}
|
@ -6,9 +6,11 @@ import 'dart:js';
|
||||
import 'dart:js_util';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:node_interop/js.dart';
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart';
|
||||
|
||||
import '../syntax.dart';
|
||||
import 'array.dart';
|
||||
import 'function.dart';
|
||||
import 'url.dart';
|
||||
@ -160,6 +162,19 @@ void _addGettersToPrototype(Object prototype, Map<String, Function> getters) {
|
||||
/// Returns whether [value] is truthy according to JavaScript.
|
||||
bool isTruthy(Object? value) => value != false && value != null;
|
||||
|
||||
@JS('Promise')
|
||||
external Function get _promiseClass;
|
||||
|
||||
/// Returns whether [object] is a `Promise`.
|
||||
bool isPromise(Object? object) =>
|
||||
object != null && instanceof(object, _promiseClass);
|
||||
|
||||
@JS('URL')
|
||||
external Function get _urlClass;
|
||||
|
||||
/// Returns whether [object] is a JavaScript `URL`.
|
||||
bool isJSUrl(Object? object) => object != null && instanceof(object, _urlClass);
|
||||
|
||||
@JS('Buffer.from')
|
||||
external Uint8List _buffer(String text, String encoding);
|
||||
|
||||
@ -188,3 +203,11 @@ JSArray toJSArray(Iterable<Object?> iterable) {
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Converts a syntax string to an instance of [Syntax].
|
||||
Syntax parseSyntax(String? syntax) {
|
||||
if (syntax == null || syntax == 'scss') return Syntax.scss;
|
||||
if (syntax == 'indented') return Syntax.sass;
|
||||
if (syntax == 'css') return Syntax.css;
|
||||
jsThrow(JsError('Unknown syntax "$syntax".'));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user