Skip to content

Commit

Permalink
feat: add support for generating sourcemaps
Browse files Browse the repository at this point in the history
The CLI now accepts a `--sourcemap` option that should match the
behaviour of `esbuild`'s `--sourcemap` option. The programmatic API's
`esbuild.sourcemap` option should now also match with `esbuild`'s
`sourcemap` option for the Build API.

The `sourcemap: 'linked'` option is special cased in the implementation,
since `esbuild` doesn't support the option under the Transform API that
`mkdist` uses [^1].

Refs: https://esbuild.github.io/api/#sourcemap
Refs: https://sourcemaps.info/spec.html

[^1]: https://github.com/evanw/esbuild/blob/745abd9f0c06f73ca40fbe198546a9bc36c23b81/pkg/api/api_impl.go#L1749
  • Loading branch information
bryan-hoang committed Dec 31, 2024
1 parent 818b664 commit d8f7dc5
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ const main = defineCommand({
type: "string",
description: "Target environment (esbuild)",
},
sourcemap: {
type: "string",
description: "Emit sourcemap (esbuild)",
},
},
async run({ args }) {
const { writtenFiles } = await mkdist({
Expand All @@ -103,6 +107,7 @@ const main = defineCommand({
jsxFragment: args.jsxFragment,
minify: args.minify,
target: args.target,
sourcemap: args.sourcemap,
},
} as MkdistOptions);

Expand Down
38 changes: 34 additions & 4 deletions src/loaders/js.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { transform } from "esbuild";
import jiti from "jiti";
import { basename, extname } from "pathe";

import type { Loader, LoaderResult } from "../loader";

Expand All @@ -18,6 +19,7 @@ export const jsLoader: Loader = async (input, { options }) => {
const output: LoaderResult = [];

let contents = await input.getContents();
let sourceMapping = "";

// declaration
if (options.declaration && !input.srcPath?.match(DECLARATION_RE)) {
Expand All @@ -33,16 +35,28 @@ export const jsLoader: Loader = async (input, { options }) => {
}

// typescript => js
const sourcemap =
options.esbuild?.sourcemap === "linked"
? "external"
: options.esbuild?.sourcemap;
if (TS_EXTS.has(input.extension)) {
contents = await transform(contents, {
const result = await transform(contents, {
...options.esbuild,
sourcemap,
sourcefile: input.srcPath,
loader: "ts",
}).then((r) => r.code);
});
contents = result.code;
sourceMapping = result.map;
} else if ([".tsx", ".jsx"].includes(input.extension)) {
contents = await transform(contents, {
const result = await transform(contents, {
loader: input.extension === ".tsx" ? "tsx" : "jsx",
...options.esbuild,
}).then((r) => r.code);
sourcemap,
sourcefile: input.srcPath,
});
contents = result.code;
sourceMapping = result.map;
}

// esm => cjs
Expand All @@ -60,6 +74,22 @@ export const jsLoader: Loader = async (input, { options }) => {
extension = options.ext.startsWith(".") ? options.ext : `.${options.ext}`;
}

// sourcemap
if (options.esbuild?.sourcemap && sourceMapping !== "") {
if (options.esbuild.sourcemap !== "inline") {
output.push({
contents: sourceMapping,
path: input.path,
extension: `${extension}.map`,
});
}

if (options.esbuild.sourcemap === "linked") {
const sourceMappingURL = `${basename(input.path, extname(input.path))}${extension}.map`;
contents += `\n//# sourceMappingURL=${sourceMappingURL}`;
}
}

output.push({
contents,
path: input.path,
Expand Down
65 changes: 65 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,71 @@ describe("mkdist", () => {
`);
}, 50_000);

it("mkdist (emit sourcemaps)", async () => {
const rootDir = resolve(__dirname, "fixture");
const { writtenFiles } = await mkdist({
rootDir,
esbuild: {
sourcemap: "linked",
},
});

expect(writtenFiles.sort()).toEqual(
[
"dist/README.md",
"dist/bar.mjs",
"dist/bar.mjs.map",
"dist/demo.css",
"dist/dir-export.mjs",
"dist/dir-export.mjs.map",
"dist/foo.mjs",
"dist/foo.d.ts",
"dist/index.mjs",
"dist/index.mjs.map",
"dist/types.d.ts",
"dist/star/index.mjs",
"dist/star/index.mjs.map",
"dist/star/other.mjs",
"dist/star/other.mjs.map",
"dist/components/index.mjs",
"dist/components/index.mjs.map",
"dist/components/blank.vue",
"dist/components/js.vue",
"dist/components/script-multi-block.vue",
"dist/components/script-multi-block.vue.mjs.map",
"dist/components/script-setup-ts.vue",
"dist/components/script-setup-ts.vue.mjs.map",
"dist/components/ts.vue",
"dist/components/ts.vue.mjs.map",
"dist/components/jsx.mjs",
"dist/components/jsx.mjs.map",
"dist/components/tsx.mjs",
"dist/components/tsx.mjs.map",
"dist/bar/index.mjs",
"dist/bar/index.mjs.map",
"dist/bar/esm.mjs",
"dist/ts/test1.mjs",
"dist/ts/test1.mjs.map",
"dist/ts/test2.mjs",
"dist/ts/test2.mjs.map",
"dist/nested.css",
"dist/prop-types/index.mjs",
"dist/prop-types/index.mjs.map",
]
.map((f) => resolve(rootDir, f))
.sort(),
);

expect(await readFile(resolve(rootDir, "dist/index.mjs"), "utf8")).toMatch(
/\n\/\/# sourceMappingURL=index.mjs.map$/,
);

expect(
JSON.parse(await readFile(resolve(rootDir, "dist/bar.mjs.map"), "utf8"))
.sourcesContent[0],
).toMatch(await readFile(resolve(rootDir, "src/bar.ts"), "utf8"));
});

describe("mkdist (sass compilation)", () => {
const rootDir = resolve(__dirname, "fixture");
let writtenFiles: string[];
Expand Down

0 comments on commit d8f7dc5

Please sign in to comment.