Skip to content

Commit 5fd8fff

Browse files
authored
feat: use external loaders for fallback (#26)
1 parent 77e65c1 commit 5fd8fff

File tree

5 files changed

+114
-49
lines changed

5 files changed

+114
-49
lines changed

README.md

+18-4
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,24 @@ module.exports = {
256256
{
257257
test: /\.ts$/,
258258
include: path.resolve(__dirname, "src/assembly"),
259-
loader: "as-loader",
260-
options: {
261-
fallback: true
262-
}
259+
use: [
260+
// fallback loader (must be before as-loader)
261+
{
262+
loader: "ts-loader",
263+
options: {
264+
transpileOnly: true
265+
}
266+
},
267+
// as-loader, apart from building .wasm file,
268+
// will forward assembly script files to the fallback loader above
269+
// to build a .js file
270+
{
271+
loader: "as-loader",
272+
options: {
273+
fallback: true
274+
}
275+
}
276+
]
263277
},
264278
{
265279
test: /\.ts$/,

src/loader/index.ts

+40-29
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import { createCompilerHost } from "./compiler-host";
88
import { mapAscOptionsToArgs, Options } from "./options";
99
import { AssemblyScriptError } from "./error";
1010
import * as schema from "./schema.json";
11-
import { addErrorToModule, addWarningToModule } from "./webpack";
11+
import {
12+
addErrorToModule,
13+
addWarningToModule,
14+
isModuleCompiledToWasm,
15+
markModuleAsCompiledToWasm,
16+
} from "./webpack";
1217

1318
const SUPPORTED_EXTENSIONS = [".wasm", ".js"];
1419

@@ -21,7 +26,7 @@ interface LoaderOptions {
2126
type CompilerOptions = OptionObject;
2227

2328
// eslint-disable-next-line @typescript-eslint/no-explicit-any
24-
function loader(this: any, buffer: Buffer) {
29+
function loader(this: any, content: string, map?: any, meta?: any) {
2530
const options = getOptions(this);
2631
validate(schema as Schema, options, {
2732
name: "AssemblyScript Loader",
@@ -43,6 +48,35 @@ function loader(this: any, buffer: Buffer) {
4348
...userAscOptions
4449
} = options as LoaderOptions & CompilerOptions;
4550

51+
if (isModuleCompiledToWasm(module)) {
52+
// skip asc compilation - forward request to a fallback loader
53+
return callback(null, content, map, meta);
54+
}
55+
56+
if (!SUPPORTED_EXTENSIONS.some((extension) => name.endsWith(extension))) {
57+
throw new Error(
58+
`Unsupported extension in name: "${name}" option in as-loader. ` +
59+
`Supported extensions are ${SUPPORTED_EXTENSIONS.join(", ")}`
60+
);
61+
}
62+
63+
if (fallback) {
64+
if (module.type?.startsWith("webassembly")) {
65+
throw new Error(
66+
`Cannot use fallback option together with module type "${module.type}". ` +
67+
`Use standard module type or disable fallback option.`
68+
);
69+
} else if (raw) {
70+
throw new Error(`Cannot use fallback option together with raw option.`);
71+
}
72+
}
73+
74+
if (name.endsWith(".js")) {
75+
throw new Error(
76+
`Cannot use .js extension directly. Please use fallback option instead.`
77+
);
78+
}
79+
4680
const ascOptions: Options = {
4781
// default options
4882
// when user imports wasm with webassembly type, it's not possible to pass env
@@ -58,44 +92,20 @@ function loader(this: any, buffer: Buffer) {
5892
...userAscOptions,
5993
};
6094

61-
if (!SUPPORTED_EXTENSIONS.some((extension) => name.endsWith(extension))) {
62-
throw new Error(
63-
`Unsupported extension in name: "${name}" option in as-loader. ` +
64-
`Supported extensions are ${SUPPORTED_EXTENSIONS.join(", ")}`
65-
);
66-
}
67-
68-
if (bind && name.endsWith(".wasm")) {
95+
if (bind) {
6996
// overwrite options for bind
7097
ascOptions.exportRuntime = true;
7198
ascOptions.transform = "as-bind";
7299
}
73100

74-
if (name.endsWith(".js")) {
75-
// overwrite options for js
76-
ascOptions.runtime = "stub";
77-
ascOptions.exportRuntime = false;
78-
}
79-
80101
const shouldGenerateSourceMap = this.sourceMap;
81102
const baseDir = path.dirname(this.resourcePath);
82103
const outFileName = interpolateName(this, name, {
83104
context,
84-
content: buffer.toString(),
105+
content,
85106
});
86107
const sourceMapFileName = outFileName + ".map";
87108

88-
if (fallback) {
89-
if (module.type?.startsWith("webassembly")) {
90-
throw new Error(
91-
`Cannot use fallback option together with module type "${module.type}". ` +
92-
`Use standard module type or disable fallback option.`
93-
);
94-
} else if (raw) {
95-
throw new Error(`Cannot use fallback option together with raw option.`);
96-
}
97-
}
98-
99109
const host = createCompilerHost(this);
100110

101111
if (shouldGenerateSourceMap) {
@@ -176,6 +186,8 @@ function loader(this: any, buffer: Buffer) {
176186
}
177187

178188
if (outFileName.endsWith(".wasm")) {
189+
markModuleAsCompiledToWasm(module);
190+
179191
if (module.type?.startsWith("webassembly") || raw) {
180192
// uses module type: "webassembly/sync" or "webasssembly/async" or raw: true -
181193
// return binary instead of emitting files
@@ -266,6 +278,5 @@ function loader(this: any, buffer: Buffer) {
266278
callback(error);
267279
});
268280
}
269-
loader.raw = true;
270281

271282
module.exports = loader;

src/loader/webpack.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as webpack from "webpack";
2+
13
interface CompatibleWebpackModule {
24
addWarning?(warning: Error): void;
35
addError?(error: Error): void;
@@ -29,4 +31,20 @@ function addErrorToModule(module: CompatibleWebpackModule, error: Error) {
2931
}
3032
}
3133

32-
export { addWarningToModule, addErrorToModule };
34+
function markModuleAsCompiledToWasm(module: webpack.Module) {
35+
module.buildMeta.asLoaderCompiledToWasm = true;
36+
}
37+
38+
function isModuleCompiledToWasm(module: webpack.Module): boolean {
39+
return Boolean(
40+
module.buildMeta.asLoaderCompiledToWasm ||
41+
(module.issuer && isModuleCompiledToWasm(module.issuer))
42+
);
43+
}
44+
45+
export {
46+
addWarningToModule,
47+
addErrorToModule,
48+
markModuleAsCompiledToWasm,
49+
isModuleCompiledToWasm,
50+
};
+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import * as assembly from "./assembly/correct/simple";
1+
import * as assembly from "./assembly/correct/complex";
22

33
async function loadAndRun() {
44
// eslint-disable-next-line @typescript-eslint/no-explicit-any
55
const module = await (assembly as any).fallback() as typeof assembly;
66

7-
console.log(module.run());
7+
const colors = module.getPalette(10);
8+
console.log(colors.map(color => color.toString()).join(','))
89
}
910

1011
loadAndRun();

test/e2e/main.spec.ts

+34-13
Original file line numberDiff line numberDiff line change
@@ -168,31 +168,52 @@ describe("as-loader", () => {
168168
);
169169
await sandbox.patch(
170170
"webpack.config.js",
171-
' name: "[name].wasm",',
172-
[' name: "[name].wasm",', " fallback: true,"].join(
173-
"\n"
174-
)
171+
[
172+
' loader: "as-loader",',
173+
" options: {",
174+
' name: "[name].wasm",',
175+
" },",
176+
].join("\n"),
177+
[
178+
" use: [",
179+
" {",
180+
' loader: "ts-loader",',
181+
" options: {",
182+
" transpileOnly: true,",
183+
" }",
184+
" },",
185+
" {",
186+
' loader: "as-loader",',
187+
" options: {",
188+
' name: "[name].wasm",',
189+
" fallback: true,",
190+
" },",
191+
" },",
192+
" ],",
193+
].join("\n")
175194
);
176195

177196
const webpackResults = await sandbox.exec("yarn webpack");
178197

179-
expect(webpackResults).toContain("simple.js");
180-
expect(webpackResults).toContain("simple.wasm");
181-
expect(webpackResults).toContain("simple.wasm.map");
198+
expect(webpackResults).toContain("complex.js");
199+
expect(webpackResults).toContain("complex.wasm");
200+
expect(webpackResults).toContain("complex.wasm.map");
182201
expect(webpackResults).toContain("main.js");
183202

184-
expect(await sandbox.exists("dist/simple.js")).toEqual(true);
185-
expect(await sandbox.exists("dist/simple.wasm")).toEqual(true);
186-
expect(await sandbox.exists("dist/simple.js.map")).toEqual(true);
187-
expect(await sandbox.exists("dist/simple.wasm.map")).toEqual(true);
203+
expect(await sandbox.exists("dist/complex.js")).toEqual(true);
204+
expect(await sandbox.exists("dist/complex.wasm")).toEqual(true);
205+
expect(await sandbox.exists("dist/complex.js.map")).toEqual(true);
206+
expect(await sandbox.exists("dist/complex.wasm.map")).toEqual(true);
188207

189-
const simpleJsMap = await sandbox.read("dist/simple.js.map", "utf8");
208+
const simpleJsMap = await sandbox.read("dist/complex.js.map", "utf8");
190209
expect(Object.keys(JSON.parse(simpleJsMap))).toEqual(
191210
expect.arrayContaining(["version", "sources", "names", "mappings"])
192211
);
193212

194213
const mainResults = await sandbox.exec("node ./dist/main.js");
195-
expect(mainResults).toEqual("15\n");
214+
expect(mainResults).toEqual(
215+
"rgb(100, 50, 20),rgb(105, 51, 19),rgb(110, 52, 18),rgb(115, 53, 17),rgb(120, 54, 16),rgb(125, 55, 15),rgb(130, 56, 14),rgb(135, 57, 13),rgb(140, 58, 12),rgb(145, 59, 11)\n"
216+
);
196217
}
197218
);
198219

0 commit comments

Comments
 (0)