Skip to content

Commit cca9464

Browse files
jerivasjgerigmeyernex3
authored
Add support for running in the browser (#1895)
Closes #25 Co-authored-by: Jonny Gerig Meyer <[email protected]> Co-authored-by: Natalie Weizenbaum <[email protected]>
1 parent eb18526 commit cca9464

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+529
-106
lines changed

.github/util/initialize/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ runs:
1010
steps:
1111
- uses: dart-lang/setup-dart@v1
1212
with:
13-
sdk: "${{ inputs.sdk }}"
13+
sdk: "${{ inputs.dart-sdk }}"
1414
architecture: "${{ inputs.architecture }}"
1515

1616
- uses: actions/setup-node@v3

.github/workflows/ci.yml

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,43 @@ jobs:
187187
run: npm run js-api-spec -- --sassPackage ../embedded-host-node --sassSassRepo ../build/language
188188
working-directory: sass-spec
189189

190+
sass_spec_js_browser:
191+
name: "JS API Tests | Browser | Dart ${{ matrix.dart_channel }}"
192+
193+
strategy:
194+
matrix:
195+
dart_channel: [stable]
196+
fail-fast: false
197+
198+
runs-on: ubuntu-latest
199+
steps:
200+
- uses: actions/checkout@v3
201+
- uses: browser-actions/setup-chrome@v1
202+
- uses: ./.github/util/initialize
203+
with:
204+
dart-sdk: ${{ matrix.dart_channel }}
205+
github-token: ${{ github.token }}
206+
- uses: ./.github/util/sass-spec
207+
208+
- name: Build JS
209+
run: dart run grinder pkg-npm-dev
210+
211+
- name: Install built dependencies
212+
run: npm install
213+
working-directory: build/npm
214+
215+
- name: Check out Sass specification
216+
uses: sass/clone-linked-repo@v1
217+
with:
218+
repo: sass/sass
219+
path: language
220+
221+
- name: Run tests
222+
run: npm run js-api-spec -- --sassSassRepo ../language --sassPackage ../build/npm --browser
223+
working-directory: sass-spec
224+
env:
225+
CHROME_EXECUTABLE: chrome
226+
190227
dart_tests:
191228
name: "Dart tests | Dart ${{ matrix.dart_channel }} | ${{ matrix.os }}"
192229
runs-on: "${{ matrix.os }}"
@@ -207,7 +244,7 @@ jobs:
207244

208245
- run: dart run grinder pkg-standalone-dev
209246
- name: Run tests
210-
run: dart run test -p vm -x node
247+
run: dart run test -x node
211248

212249
# Unit tests that use Node.js, defined in test/.
213250
#
@@ -246,7 +283,30 @@ jobs:
246283

247284
- run: dart run grinder before-test
248285
- name: Run tests
249-
run: dart run test -j 2 -t node
286+
run: dart run test -t node -j 2
287+
288+
browser-test:
289+
name: "Browser Tests | Dart ${{ matrix.dart_channel }}"
290+
291+
strategy:
292+
matrix:
293+
dart_channel: [stable]
294+
fail-fast: false
295+
296+
runs-on: ubuntu-latest
297+
steps:
298+
- uses: actions/checkout@v3
299+
- uses: browser-actions/setup-chrome@v1
300+
- uses: ./.github/util/initialize
301+
with:
302+
dart-sdk: ${{ matrix.dart_channel }}
303+
github-token: ${{ github.token }}
304+
305+
- run: dart run grinder before-test
306+
- name: Run tests
307+
run: dart run test -p chrome -j 2
308+
env:
309+
CHROME_EXECUTABLE: chrome
250310

251311
double_check:
252312
name: Double-check

bin/sass.dart

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:sass/src/executable/repl.dart';
1616
import 'package:sass/src/executable/watch.dart';
1717
import 'package:sass/src/import_cache.dart';
1818
import 'package:sass/src/io.dart';
19+
import 'package:sass/src/io.dart' as io;
1920
import 'package:sass/src/logger/deprecation_handling.dart';
2021
import 'package:sass/src/stylesheet_graph.dart';
2122
import 'package:sass/src/utils.dart';
@@ -32,14 +33,18 @@ Future<void> main(List<String> args) async {
3233
//
3334
// If [trace] is passed, its terse representation is printed after the error.
3435
void printError(String error, StackTrace? stackTrace) {
35-
if (printedError) stderr.writeln();
36+
var buffer = StringBuffer();
37+
if (printedError) buffer.writeln();
3638
printedError = true;
37-
stderr.writeln(error);
39+
buffer.write(error);
3840

3941
if (stackTrace != null) {
40-
stderr.writeln();
41-
stderr.writeln(Trace.from(stackTrace).terse.toString().trimRight());
42+
buffer.writeln();
43+
buffer.writeln();
44+
buffer.write(Trace.from(stackTrace).terse.toString().trimRight());
4245
}
46+
47+
io.printError(buffer);
4348
}
4449

4550
if (args.firstOrNull == '--embedded') {

lib/src/async_compile.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'compile_result.dart';
1313
import 'deprecation.dart';
1414
import 'importer.dart';
1515
import 'importer/legacy_node.dart';
16+
import 'importer/no_op.dart';
1617
import 'io.dart';
1718
import 'logger.dart';
1819
import 'logger/deprecation_handling.dart';
@@ -119,7 +120,7 @@ Future<CompileResult> compileStringAsync(String source,
119120
logger,
120121
importCache,
121122
nodeImporter,
122-
importer ?? FilesystemImporter('.'),
123+
importer ?? (isBrowser ? NoOpImporter() : FilesystemImporter('.')),
123124
functions,
124125
style,
125126
useSpaces,

lib/src/async_import_cache.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:tuple/tuple.dart';
1111
import 'ast/sass.dart';
1212
import 'deprecation.dart';
1313
import 'importer.dart';
14+
import 'importer/no_op.dart';
1415
import 'importer/utils.dart';
1516
import 'io.dart';
1617
import 'logger.dart';
@@ -95,6 +96,7 @@ class AsyncImportCache {
9596
static List<AsyncImporter> _toImporters(Iterable<AsyncImporter>? importers,
9697
Iterable<String>? loadPaths, PackageConfig? packageConfig) {
9798
var sassPath = getEnvironmentVariable('SASS_PATH');
99+
if (isBrowser) return [...?importers];
98100
return [
99101
...?importers,
100102
if (loadPaths != null)
@@ -122,6 +124,12 @@ class AsyncImportCache {
122124
{AsyncImporter? baseImporter,
123125
Uri? baseUrl,
124126
bool forImport = false}) async {
127+
if (isBrowser &&
128+
(baseImporter == null || baseImporter is NoOpImporter) &&
129+
_importers.isEmpty) {
130+
throw "Custom importers are required to load stylesheets when compiling in the browser.";
131+
}
132+
125133
if (baseImporter != null) {
126134
var relativeResult = await putIfAbsentAsync(_relativeCanonicalizeCache,
127135
Tuple4(url, forImport, baseImporter, baseUrl), () async {

lib/src/compile.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// DO NOT EDIT. This file was generated from async_compile.dart.
66
// See tool/grind/synchronize.dart for details.
77
//
8-
// Checksum: 628fbfe8a6717cca332dd646eeda2260dd3e30c6
8+
// Checksum: bac7360553f772bbf8243cca78f4f63e4bdf2755
99
//
1010
// ignore_for_file: unused_import
1111

@@ -22,6 +22,7 @@ import 'compile_result.dart';
2222
import 'deprecation.dart';
2323
import 'importer.dart';
2424
import 'importer/legacy_node.dart';
25+
import 'importer/no_op.dart';
2526
import 'io.dart';
2627
import 'logger.dart';
2728
import 'logger/deprecation_handling.dart';
@@ -128,7 +129,7 @@ CompileResult compileString(String source,
128129
logger,
129130
importCache,
130131
nodeImporter,
131-
importer ?? FilesystemImporter('.'),
132+
importer ?? (isBrowser ? NoOpImporter() : FilesystemImporter('.')),
132133
functions,
133134
style,
134135
useSpaces,

lib/src/embedded/unavailable.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
import '../io.dart';
66

77
void main(List<String> args) async {
8-
stderr.writeln('sass --embedded is unavailable in pure JS mode.');
8+
printError('sass --embedded is unavailable in pure JS mode.');
99
exitCode = 1;
1010
}

lib/src/executable/watch.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,16 @@ class _Watcher {
112112
/// Prints [message] to standard error, with [stackTrace] if [_options.trace]
113113
/// is set.
114114
void _printError(String message, StackTrace stackTrace) {
115-
stderr.writeln(message);
115+
var buffer = StringBuffer(message);
116116

117117
if (_options.trace) {
118-
stderr.writeln();
119-
stderr.writeln(Trace.from(stackTrace).terse.toString().trimRight());
118+
buffer.writeln();
119+
buffer.writeln();
120+
buffer.write(Trace.from(stackTrace).terse.toString().trimRight());
120121
}
121122

122-
if (!_options.stopOnError) stderr.writeln();
123+
if (!_options.stopOnError) buffer.writeln();
124+
printError(buffer);
123125
}
124126

125127
/// Listens to `watcher.events` and updates the filesystem accordingly.

lib/src/import_cache.dart

Lines changed: 9 additions & 1 deletion
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/grind/synchronize.dart for details.
77
//
8-
// Checksum: 92d6816f673ecbabd993aea7b79e27553f896ff4
8+
// Checksum: 96e085628560f348a79b8f99b96f7352f450868c
99
//
1010
// ignore_for_file: unused_import
1111

@@ -18,6 +18,7 @@ import 'package:tuple/tuple.dart';
1818
import 'ast/sass.dart';
1919
import 'deprecation.dart';
2020
import 'importer.dart';
21+
import 'importer/no_op.dart';
2122
import 'importer/utils.dart';
2223
import 'io.dart';
2324
import 'logger.dart';
@@ -101,6 +102,7 @@ class ImportCache {
101102
static List<Importer> _toImporters(Iterable<Importer>? importers,
102103
Iterable<String>? loadPaths, PackageConfig? packageConfig) {
103104
var sassPath = getEnvironmentVariable('SASS_PATH');
105+
if (isBrowser) return [...?importers];
104106
return [
105107
...?importers,
106108
if (loadPaths != null)
@@ -126,6 +128,12 @@ class ImportCache {
126128
/// applicable). Otherwise, returns `null`.
127129
Tuple3<Importer, Uri, Uri>? canonicalize(Uri url,
128130
{Importer? baseImporter, Uri? baseUrl, bool forImport = false}) {
131+
if (isBrowser &&
132+
(baseImporter == null || baseImporter is NoOpImporter) &&
133+
_importers.isEmpty) {
134+
throw "Custom importers are required to load stylesheets when compiling in the browser.";
135+
}
136+
129137
if (baseImporter != null) {
130138
var relativeResult = _relativeCanonicalizeCache
131139
.putIfAbsent(Tuple4(url, forImport, baseImporter, baseUrl), () {

lib/src/io/interface.dart

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,12 @@
44

55
import 'package:watcher/watcher.dart';
66

7-
/// An output sink that writes to this process's standard error.
8-
class Stderr {
9-
/// Writes the string representation of [object] to standard error.
10-
void write(Object object) {}
11-
12-
/// Writes the string representation of [object] to standard error, followed
13-
/// by a newline.
14-
///
15-
/// If [object] is `null`, just writes a newline.
16-
void writeln([Object? object]) {}
17-
18-
/// Flushes any buffered text.
19-
void flush() {}
20-
}
21-
227
/// An error thrown by [readFile].
238
class FileSystemException {
249
String get message => throw '';
2510
String? get path => throw '';
2611
}
2712

28-
/// The standard error for the current process.
29-
Stderr get stderr => throw '';
30-
3113
/// Whether the current process is running on Windows.
3214
bool get isWindows => throw '';
3315

@@ -37,15 +19,22 @@ bool get isMacOS => throw '';
3719
/// Returns whether or not stdout is connected to an interactive terminal.
3820
bool get hasTerminal => throw '';
3921

40-
/// Whether we're running as Node.JS.
22+
/// Whether we're running as JS (browser or Node.js).
23+
bool get isJS => throw '';
24+
25+
/// Whether we're running as Node.js (not browser or Dart VM).
4126
bool get isNode => throw '';
4227

28+
/// Whether we're running as browser (not Node.js or Dart VM).
29+
bool get isBrowser => throw '';
30+
4331
/// Whether this process is connected to a terminal that supports ANSI escape
4432
/// sequences.
4533
bool get supportsAnsiEscapes => throw '';
4634

47-
/// The current working directory.
48-
String get currentPath => throw '';
35+
/// Prints [message] (followed by a newline) to standard error or the
36+
/// equivalent.
37+
void printError(Object? message) => throw '';
4938

5039
/// Reads the file at [path] as a UTF-8 encoded string.
5140
///

0 commit comments

Comments
 (0)