Skip to content

Commit

Permalink
Merge pull request #3 from mml-io/deej/asset-imports
Browse files Browse the repository at this point in the history
treat any imports with extensions (excluding html and [tj]sx?) as asset imports
  • Loading branch information
TheCodeTherapy authored Sep 6, 2024
2 parents 390d705 + aebaaf9 commit 472217c
Show file tree
Hide file tree
Showing 12 changed files with 390 additions and 191 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/index.cjs",
"require": "./dist/index.js"
}
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./package.json": "./package.json"
},
"keywords": [
"esbuild",
Expand Down
86 changes: 83 additions & 3 deletions src/documents.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import esbuild from "esbuild";
import path from "node:path";
import path, { basename } from "node:path";
import util from "node:util";
import fsp from "node:fs/promises";

export interface DocumentPluginOptions {
importStubs: Record<string, string>;
verbose?: boolean;
assetDir: string;
onEnd?: (
result: esbuild.BuildResult,
importStubs: Record<string, string>,
Expand All @@ -23,6 +24,7 @@ export interface DocumentContextOptions {
documents: string[];
build?: esbuild.PluginBuild["esbuild"];
worldDocuments?: Set<string>;
assetDir: string;
verbose?: boolean;
onEnd?: (
result: esbuild.BuildResult,
Expand All @@ -36,6 +38,7 @@ export async function documentContext({
options,
worldDocuments = new Set<string>(),
verbose,
assetDir,
onEnd,
build = esbuild,
}: DocumentContextOptions): Promise<DocumentContext> {
Expand All @@ -46,6 +49,7 @@ export async function documentContext({
format: "iife",
plugins: [
documentPlugin({
assetDir,
importStubs,
verbose,
onEnd,
Expand All @@ -70,6 +74,7 @@ export async function documentContext({
format: "iife",
plugins: [
documentPlugin({
assetDir,
importStubs,
verbose,
onEnd,
Expand Down Expand Up @@ -99,8 +104,17 @@ export async function documentContext({
};
}

const nonAssetExtensions = new Set([
".html",
".css",
".js",
".ts",
".jsx",
".tsx",
]);

export function documentPlugin(args: DocumentPluginOptions): esbuild.Plugin {
const { verbose, importStubs, onEnd } = args;
const { verbose, importStubs, assetDir, onEnd } = args;
const log = verbose
? (...args: unknown[]) => {
console.log("[mml-world]:", ...args);
Expand All @@ -113,8 +127,16 @@ export function documentPlugin(args: DocumentPluginOptions): esbuild.Plugin {
build.initialOptions.metafile ??= true;
build.initialOptions.bundle ??= true;
(build.initialOptions.loader ??= {})[".html"] = "copy";
const outdir = (build.initialOptions.outdir ??= "build");

const discoveredDocuments = new Set<string>();
const assets: { output: string; entrypoint: string }[] = [];

build.onStart(() => {
log("onStart");
discoveredDocuments.clear();
assets.length = 0;
});

build.onResolve({ filter: /mml:/ }, async (args) => {
log("onResolve(/mml:/)", args);
Expand All @@ -140,6 +162,51 @@ export function documentPlugin(args: DocumentPluginOptions): esbuild.Plugin {
};
});

build.onResolve({ filter: /\.[^./]+$/ }, (args) => {
if (nonAssetExtensions.has(path.extname(args.path))) return;

log("onResolve: asset", args);

const resolved = path.resolve(args.resolveDir, args.path);
const relpath = path.relative(process.cwd(), resolved);
importStubs[relpath] = `asset:${relpath}`;

return {
path: resolved,
namespace: "asset",
watchFiles: [args.path],
};
});

build.onLoad(
{ filter: /.*/, namespace: "asset" },
async (args: esbuild.OnLoadArgs) => {
log("onLoad: asset", {
...args,
importStubs,
cwd: process.cwd(),
});

const output = path.relative(
process.cwd(),
path.resolve(outdir, assetDir, basename(args.path)),
);
const entrypoint = path.relative(process.cwd(), args.path);

assets.push({ output, entrypoint });

await fsp.mkdir(path.dirname(output), { recursive: true });
await fsp.copyFile(args.path, output);

const contents = importStubs[entrypoint];

return {
contents,
loader: "text",
};
},
);

build.onLoad(
{ filter: /.*/, namespace: "mml" },
(args: esbuild.OnLoadArgs) => {
Expand Down Expand Up @@ -171,7 +238,9 @@ export function documentPlugin(args: DocumentPluginOptions): esbuild.Plugin {
...((build.initialOptions.entryPoints ?? []) as string[]),
...discoveredDocuments,
],
plugins: [documentPlugin({ importStubs, verbose, onEnd })],
plugins: [
documentPlugin({ importStubs, verbose, assetDir, onEnd }),
],
});

return;
Expand All @@ -180,6 +249,17 @@ export function documentPlugin(args: DocumentPluginOptions): esbuild.Plugin {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const outputs = result.metafile!.outputs;

for (const asset of assets) {
const stats = await fsp.stat(asset.output);
outputs[asset.output] = {
entryPoint: asset.entrypoint,
bytes: stats.size,
inputs: {},
imports: [],
exports: [],
};
}

for (const [jsPath, meta] of Object.entries(outputs)) {
if (!meta.entryPoint || !jsPath.endsWith(".js")) continue;

Expand Down
44 changes: 32 additions & 12 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,35 @@ export type MaybePromise<T> = Promise<T> | T;
export interface MMLPluginOptions {
verbose?: boolean;
outputProcessor?: OutputProcessorProvider;
documentPrefix?: string;
assetPrefix?: string;
assetDir?: string;
importPrefix?: string;
}

export function mml(args: MMLPluginOptions = {}): esbuild.Plugin {
const {
verbose,
outputProcessor: outputProcessorProvider,
importPrefix = "ws:///",
importPrefix,
assetPrefix = "/",
assetDir = "assets",
} = args;
let { documentPrefix = "ws:///" } = args;

const log = verbose
? (...args: unknown[]) => {
console.log("[mml]:", ...args);
}
: noop;

if (importPrefix) {
log("importPrefix is deprecated, use documentPrefix instead");
if (!documentPrefix) {
documentPrefix = importPrefix;
}
}

return {
name: "mml",
async setup(build) {
Expand Down Expand Up @@ -55,19 +69,22 @@ export function mml(args: MMLPluginOptions = {}): esbuild.Plugin {

initialOptions.entryPoints = [];

const onResult = makeResultProcessor(
const processor = makeResultProcessor({
outdir,
importPrefix,
documentPrefix,
assetPrefix,
log,
outputProcessorProvider?.(log),
);
outputProcessor: outputProcessorProvider?.(log),
});

const documentCtx = await documentContext({
build: build.esbuild,
documents,
assetDir,
options: initialOptions,
onEnd: async (result, importStubs) => {
await onResult("document", result, importStubs);
processor.pushResult("document", result, importStubs);
await processor.process();
},
verbose,
});
Expand All @@ -76,23 +93,26 @@ export function mml(args: MMLPluginOptions = {}): esbuild.Plugin {
build: build.esbuild,
worlds,
onEnd: async (result, discoveredDocuments, importStubs) => {
processor.pushResult("world", result, importStubs);
if (discoveredDocuments.size === 0) {
return;
}
await documentCtx.rebuildWithDocuments(
discoveredDocuments,
importStubs,
);
await onResult("world", result, importStubs);
},
options: initialOptions,
verbose,
});

build.onStart(async () => {
log("onStart");
await worldCtx.rebuild();
});

build.onEnd(async (result) => {
await onResult("root", result, {});
if (worlds.length > 0) {
await worldCtx.rebuild();
} else {
await documentCtx.rebuild();
}
});

build.onDispose(() => {
Expand Down
Loading

0 comments on commit 472217c

Please sign in to comment.