Skip to content

Commit 076414d

Browse files
jerivasjamesnwjgerigmeyer
authored
[Shared Resources] dart-sass implementation (#2134)
Co-authored-by: James Stuckey Weber <[email protected]> Co-authored-by: Jonny Gerig Meyer <[email protected]>
1 parent 0d91c92 commit 076414d

File tree

8 files changed

+149
-4
lines changed

8 files changed

+149
-4
lines changed

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
## 1.69.8
1+
## 1.70.0
2+
3+
### JavaScript API
4+
5+
* Add a `sass.initCompiler()` function that returns a `sass.Compiler` object
6+
which supports `compile()` and `compileString()` methods with the same API as
7+
the global Sass object. On the Node.js embedded host, each `sass.Compiler`
8+
object uses a single long-lived subprocess, making compiling multiple
9+
stylesheets much more efficient.
10+
11+
* Add a `sass.initAsyncCompiler()` function that returns a `sass.AsyncCompiler`
12+
object which supports `compileAsync()` and `compileStringAsync()` methods with
13+
the same API as the global Sass object. On the Node.js embedded host, each
14+
`sass.AsynCompiler` object uses a single long-lived subprocess, making
15+
compiling multiple stylesheets much more efficient.
216

317
### Embedded Sass
418

lib/src/js.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'js/exception.dart';
66
import 'js/exports.dart';
77
import 'js/compile.dart';
8+
import 'js/compiler.dart';
89
import 'js/legacy.dart';
910
import 'js/legacy/types.dart';
1011
import 'js/legacy/value.dart';
@@ -24,6 +25,11 @@ void main() {
2425
exports.compileAsync = allowInteropNamed('sass.compileAsync', compileAsync);
2526
exports.compileStringAsync =
2627
allowInteropNamed('sass.compileStringAsync', compileStringAsync);
28+
exports.initCompiler = allowInteropNamed('sass.initCompiler', initCompiler);
29+
exports.initAsyncCompiler =
30+
allowInteropNamed('sass.initAsyncCompiler', initAsyncCompiler);
31+
exports.Compiler = compilerClass;
32+
exports.AsyncCompiler = asyncCompilerClass;
2733
exports.Value = valueClass;
2834
exports.SassBoolean = booleanClass;
2935
exports.SassArgumentList = argumentListClass;

lib/src/js/compiler.dart

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2024 Google LLC. Use of this source code is governed by an
2+
// MIT-style license that can be found in the LICENSE file or at
3+
// https://opensource.org/licenses/MIT.
4+
5+
import 'dart:js_util';
6+
7+
import 'package:async/async.dart';
8+
import 'package:node_interop/js.dart';
9+
10+
import 'compile.dart';
11+
import 'compile_options.dart';
12+
import 'reflection.dart';
13+
import 'utils.dart';
14+
15+
/// The Dart Compiler class.
16+
class Compiler {
17+
/// A flag signifying whether the instance has been disposed.
18+
bool _disposed = false;
19+
20+
/// Checks if `dispose()` has been called on this instance, and throws an
21+
/// error if it has. Used to verify that compilation methods are not called
22+
/// after disposal.
23+
void _throwIfDisposed() {
24+
if (_disposed) {
25+
jsThrow(JsError('Compiler has already been disposed.'));
26+
}
27+
}
28+
}
29+
30+
/// The Dart Async Compiler class.
31+
class AsyncCompiler extends Compiler {
32+
/// A set of all compilations, tracked to ensure all compilations complete
33+
/// before async disposal resolves.
34+
final FutureGroup<void> compilations = FutureGroup();
35+
36+
/// Adds a compilation to the FutureGroup.
37+
void addCompilation(Promise compilation) {
38+
Future<dynamic> comp = promiseToFuture(compilation);
39+
var wrappedComp = comp.catchError((err) {
40+
/// Ignore errors so FutureGroup doesn't close when a compilation fails.
41+
});
42+
compilations.add(wrappedComp);
43+
}
44+
}
45+
46+
/// The JavaScript `Compiler` class.
47+
final JSClass compilerClass = () {
48+
var jsClass = createJSClass(
49+
'sass.Compiler',
50+
(Object self) => {
51+
jsThrow(JsError(("Compiler can not be directly constructed. "
52+
"Please use `sass.initCompiler()` instead.")))
53+
});
54+
55+
jsClass.defineMethods({
56+
'compile': (Compiler self, String path, [CompileOptions? options]) {
57+
self._throwIfDisposed();
58+
return compile(path, options);
59+
},
60+
'compileString': (Compiler self, String source,
61+
[CompileStringOptions? options]) {
62+
self._throwIfDisposed();
63+
return compileString(source, options);
64+
},
65+
'dispose': (Compiler self) {
66+
self._disposed = true;
67+
},
68+
});
69+
70+
getJSClass(Compiler()).injectSuperclass(jsClass);
71+
return jsClass;
72+
}();
73+
74+
Compiler initCompiler() => Compiler();
75+
76+
/// The JavaScript `AsyncCompiler` class.
77+
final JSClass asyncCompilerClass = () {
78+
var jsClass = createJSClass(
79+
'sass.AsyncCompiler',
80+
(Object self) => {
81+
jsThrow(JsError(("AsyncCompiler can not be directly constructed. "
82+
"Please use `sass.initAsyncCompiler()` instead.")))
83+
});
84+
85+
jsClass.defineMethods({
86+
'compileAsync': (AsyncCompiler self, String path,
87+
[CompileOptions? options]) {
88+
self._throwIfDisposed();
89+
var compilation = compileAsync(path, options);
90+
self.addCompilation(compilation);
91+
return compilation;
92+
},
93+
'compileStringAsync': (AsyncCompiler self, String source,
94+
[CompileStringOptions? options]) {
95+
self._throwIfDisposed();
96+
var compilation = compileStringAsync(source, options);
97+
self.addCompilation(compilation);
98+
return compilation;
99+
},
100+
'dispose': (AsyncCompiler self) {
101+
self._disposed = true;
102+
return futureToPromise((() async {
103+
self.compilations.close();
104+
await self.compilations.future;
105+
})());
106+
}
107+
});
108+
109+
getJSClass(AsyncCompiler()).injectSuperclass(jsClass);
110+
return jsClass;
111+
}();
112+
113+
Promise initAsyncCompiler() => futureToPromise((() async => AsyncCompiler())());

lib/src/js/exports.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ class Exports {
1919
external set compileStringAsync(Function function);
2020
external set compile(Function function);
2121
external set compileAsync(Function function);
22+
external set initCompiler(Function function);
23+
external set initAsyncCompiler(Function function);
24+
external set Compiler(JSClass function);
25+
external set AsyncCompiler(JSClass function);
2226
external set info(String info);
2327
external set Exception(JSClass function);
2428
external set Logger(LoggerNamespace namespace);

pkg/sass_api/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 9.3.0
2+
3+
* No user-visible changes.
4+
15
## 9.2.7
26

37
* No user-visible changes.

pkg/sass_api/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ name: sass_api
22
# Note: Every time we add a new Sass AST node, we need to bump the *major*
33
# version because it's a breaking change for anyone who's implementing the
44
# visitor interface(s).
5-
version: 9.2.7
5+
version: 9.3.0
66
description: Additional APIs for Dart Sass.
77
homepage: https://github.com/sass/dart-sass
88

99
environment:
1010
sdk: ">=3.0.0 <4.0.0"
1111

1212
dependencies:
13-
sass: 1.69.7
13+
sass: 1.70.0
1414

1515
dev_dependencies:
1616
dartdoc: ^6.0.0

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: sass
2-
version: 1.69.8-dev
2+
version: 1.70.0
33
description: A Sass implementation in Dart.
44
homepage: https://github.com/sass/dart-sass
55

tool/grind.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ void main(List<String> args) {
5151
'compileAsync',
5252
'compileString',
5353
'compileStringAsync',
54+
'initCompiler',
55+
'initAsyncCompiler',
56+
'Compiler',
57+
'AsyncCompiler',
5458
'Logger',
5559
'SassArgumentList',
5660
'SassBoolean',

0 commit comments

Comments
 (0)