Skip to content

Commit 6d66c43

Browse files
authored
Properly handle new NodePackageImporter() with an ESM entrypoint (#2181)
Closes #2178
1 parent 85a932f commit 6d66c43

File tree

6 files changed

+67
-15
lines changed

6 files changed

+67
-15
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88

99
* Export the `NodePackageImporter` class in ESM mode.
1010

11+
* Allow `NodePackageImporter` to locate a default directory even when the
12+
entrypoint is an ESM module.
13+
14+
### Dart API
15+
16+
* Make passing a null argument to `NodePackageImporter()` a static error rather
17+
than just a runtime error.
18+
1119
### Embedded Sass
1220

1321
* In the JS Embedded Host, properly install the musl Linux embedded compiler

lib/src/importer/node_package.dart

+2-6
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,8 @@ class NodePackageImporter extends Importer {
1919
late final String _entryPointDirectory;
2020

2121
/// Creates a Node package importer with the associated entry point.
22-
NodePackageImporter(String? entryPointDirectory) {
23-
if (entryPointDirectory == null) {
24-
throw "The Node package importer cannot determine an entry point "
25-
"because `require.main.filename` is not defined. "
26-
"Please provide an `entryPointDirectory` to the `NodePackageImporter`.";
27-
} else if (isBrowser) {
22+
NodePackageImporter(String entryPointDirectory) {
23+
if (isBrowser) {
2824
throw "The Node package importer cannot be used without a filesystem.";
2925
}
3026
_entryPointDirectory = p.absolute(entryPointDirectory);

lib/src/js/compile.dart

+8-5
Original file line numberDiff line numberDiff line change
@@ -334,10 +334,13 @@ List<AsyncCallable> _parseFunctions(Object? functions, {bool asynch = false}) {
334334
final JSClass nodePackageImporterClass = () {
335335
var jsClass = createJSClass(
336336
'sass.NodePackageImporter',
337-
(Object self, [String? entryPointDirectory]) => NodePackageImporter(
338-
entryPointDirectory ??
339-
(requireMainFilename != null
340-
? p.dirname(requireMainFilename!)
341-
: null)));
337+
(Object self, [String? entrypointDirectory]) => NodePackageImporter(
338+
switch ((entrypointDirectory, entrypointFilename)) {
339+
((var directory?, _)) => directory,
340+
(_, var filename?) => p.dirname(filename),
341+
_ => throw "The Node package importer cannot determine an entry "
342+
"point because `require.main.filename` is not defined. Please "
343+
"provide an `entryPointDirectory` to the `NodePackageImporter`."
344+
}));
342345
return jsClass;
343346
}();

lib/src/js/module.dart

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2024 Google Inc. 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 'package:js/js.dart';
6+
7+
@JS('nodeModule')
8+
external JSModule get module;
9+
10+
/// A Dart API for the [`node:module`] module.
11+
///
12+
/// [`node:module`]: https://nodejs.org/api/module.html#modules-nodemodule-api
13+
@JS()
14+
@anonymous
15+
class JSModule {
16+
/// See https://nodejs.org/api/module.html#modulecreaterequirefilename.
17+
external JSModuleRequire createRequire(String filename);
18+
}
19+
20+
/// A `require` function returned by `module.createRequire()`.
21+
@JS()
22+
@anonymous
23+
class JSModuleRequire {
24+
/// See https://nodejs.org/api/modules.html#requireresolverequest-options.
25+
external String resolve(String filename);
26+
}

lib/src/js/utils.dart

+21-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import 'dart:js_util';
66
import 'dart:typed_data';
77

8-
import 'package:node_interop/js.dart';
8+
import 'package:node_interop/node.dart' hide module;
99
import 'package:js/js.dart';
1010
import 'package:js/js_util.dart';
1111

@@ -14,6 +14,7 @@ import '../utils.dart';
1414
import '../value.dart';
1515
import 'array.dart';
1616
import 'function.dart';
17+
import 'module.dart';
1718
import 'reflection.dart';
1819
import 'url.dart';
1920

@@ -234,6 +235,22 @@ Syntax parseSyntax(String? syntax) => switch (syntax) {
234235
_ => jsThrow(JsError('Unknown syntax "$syntax".'))
235236
};
236237

237-
/// The value of require.main.filename
238-
@JS("require.main.filename")
239-
external String? get requireMainFilename;
238+
/// The path to the Node.js entrypoint, if one can be located.
239+
String? get entrypointFilename {
240+
if (_requireMain?.filename case var filename?) {
241+
return filename;
242+
} else if (process.argv case [_, String path]) {
243+
return module.createRequire(path).resolve(path);
244+
} else {
245+
return null;
246+
}
247+
}
248+
249+
@JS("require.main")
250+
external _RequireMain? get _requireMain;
251+
252+
@JS()
253+
@anonymous
254+
class _RequireMain {
255+
external String? get filename;
256+
}

tool/grind.dart

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ void main(List<String> args) {
3535
pkg.JSRequire("chokidar", target: pkg.JSRequireTarget.cli),
3636
pkg.JSRequire("readline", target: pkg.JSRequireTarget.cli),
3737
pkg.JSRequire("fs", target: pkg.JSRequireTarget.node),
38+
pkg.JSRequire("module",
39+
target: pkg.JSRequireTarget.node, identifier: 'nodeModule'),
3840
pkg.JSRequire("stream", target: pkg.JSRequireTarget.node),
3941
pkg.JSRequire("util", target: pkg.JSRequireTarget.node),
4042
];

0 commit comments

Comments
 (0)