Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src-json/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,13 @@
"targets": ["TClass"],
"links": ["https://haxe.org/manual/target-javascript-require.html"]
},
{
"name": "JsImport",
"metadata": ":js.import",
"doc": "Generate ES module import statement for given extern. Produces import lines at the top of the generated JS output.",
"platforms": ["js"],
"targets": ["TClass"]
},
{
"name": "LuaRequire",
"metadata": ":luaRequire",
Expand Down
37 changes: 37 additions & 0 deletions src/generators/genjs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,43 @@ let generate js_gen com =

let ctx = alloc_ctx com es_version in
Gctx.map_source_header com.defines (fun s -> print ctx "// %s\n" s);

let import_statements = ref [] in
let () =
List.iter (fun mt -> match mt with
| TClassDecl c when (has_class_flag c CExtern) && Meta.has Meta.JsImport c.cl_meta && is_directly_used ctx.com c.cl_meta ->
let _, args, mp = Meta.get Meta.JsImport c.cl_meta in
let id = s_path ctx (get_generated_class_path c) in
(match args with
(* @:js.import(@star "module") - namespace import *)
| [EMeta ((Meta.Custom "star",[],_),(EConst(String(module_name,_)),_)),_] ->
import_statements := (Printf.sprintf "import * as %s from \"%s\";" id module_name) :: !import_statements
(* @:js.import(@default "module") - default import *)
| [EMeta ((Meta.Custom "default",[],_),(EConst(String(module_name,_)),_)),_] ->
import_statements := (Printf.sprintf "import %s from \"%s\";" id module_name) :: !import_statements
(* @:js.import("module") - named import using class name *)
| [(EConst(String(module_name,_)),_)] ->
import_statements := (Printf.sprintf "import { %s } from \"%s\";" id module_name) :: !import_statements
(* @:js.import("module", "exportName") - named import with alias *)
| [(EConst(String(module_name,_)),_); (EConst(String(export_name,_)),_)] ->
if export_name = id then
import_statements := (Printf.sprintf "import { %s } from \"%s\";" id module_name) :: !import_statements
else
import_statements := (Printf.sprintf "import { %s as %s } from \"%s\";" export_name id module_name) :: !import_statements
| exprs ->
abort "Unsupported @:js.import format. Use: @:js.import('module'), @:js.import('module', 'name'), @:js.import(@default 'module'), or @:js.import(@star 'module')" mp
)
| _ -> ()
) com.types;
in
(match !import_statements with
| [] -> ()
| lines ->
List.iter (fun line ->
print ctx "%s\n" line
) (List.rev lines)
);

if has_feature ctx "Class" || has_feature ctx "Type.getClassName" then add_feature ctx "js.Boot.isClass";
if has_feature ctx "Enum" || has_feature ctx "Type.getEnumName" then add_feature ctx "js.Boot.isEnum";

Expand Down
56 changes: 56 additions & 0 deletions tests/misc/projects/Issue10615/Main.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
@:js.import(@star '../lib.js')
extern class Lib {
@:native("default") static function default_():Bool;
static var str:String;
static function increment(i:Int):Int;
}

@:js.import(@star '../lib_default_fun.js')
extern class DefaultFun {
@:native("default") static function default_():Bool;
}

@:js.import(@default '../lib_default_class.js')
extern class DefaultClass {
static function def():Bool;
}

@:js.import('../multilib.js')
extern class Foo {
static function name():String;
}
@:js.import('../multilib.js', 'Foo')
extern class Foo2 {
static function name():String;
}
@:js.import('../multilib.js', 'Bar')
extern class Bar2 {
static function name():String;
}

@:js.import(@default '../multilib.js')
extern class DefaultClass2 {
function new();
function name():Bool;
}

class Main {
static function main() {
eq("str", Lib.str);
eq(2, Lib.increment(1));
eq("default function", Lib.default_());

eq("foo", Foo.name());
eq("foo", Foo2.name());
eq("bar", Bar2.name());
final def = new DefaultClass2();
eq("default class", def.name());

eq("default function", DefaultFun.default_());
eq("default static", DefaultClass.def());
}

static function eq(a:Any, b:Any):Void {
if (a != b) throw '$a is not $b';
}
}
5 changes: 5 additions & 0 deletions tests/misc/projects/Issue10615/compile.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
--main Main
-dce full
-D analyzer-optimize
--js bin/main.js
--cmd node bin/main
6 changes: 6 additions & 0 deletions tests/misc/projects/Issue10615/compile2.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
--main Main
-dce full
-D analyzer-optimize
--js bin/main.js
--cmd node bin/main
-D js-es=6
8 changes: 8 additions & 0 deletions tests/misc/projects/Issue10615/lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const str = "str";
export function increment(x) {
return x + 1;
}

export default function def() {
return "default function";
}
6 changes: 6 additions & 0 deletions tests/misc/projects/Issue10615/lib_default_class.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default class Lib {
static def() {
return "default static";
}
}

3 changes: 3 additions & 0 deletions tests/misc/projects/Issue10615/lib_default_fun.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function def() {
return "default function";
}
17 changes: 17 additions & 0 deletions tests/misc/projects/Issue10615/multilib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export class Foo {
static name() {
return "foo";
}
}

export class Bar {
static name() {
return "bar";
}
}

export default class DefClass {
name() {
return "default class";
}
}
Loading