这是indexloc提供的服务,不要输入任何密码
Skip to content

Source maps not being generated when using outExtension { js: ".jsx" } #1302

@ChristophP

Description

@ChristophP

What's the problem?

When using outExtension: () => ({ js: ".jsx" }) Tsup doesn not generate source maps. The build works but the missing source maps lead to a very bad debugging experience for us. Would be great to have these for jsx outputs as well.

Longer Story

We're using SolidJS and need to preserve the JSX in library code of a big monorepo. The libs are built with tsup while the application is built with vite. The application uses vite-plugin-solid which expects all library code expose the unbuilt jsx and then builds the JSX in files with .jsx extensions in one batch. The build works but the missing source maps lead to a very bad debugging experience for us.

Reproduction

// tsup.config.ts
import { defineConfig } from "tsup";

export default defineConfig({
  format: "esm",
  entry: ["src/index.tsx"],
  outDir: "out",
  sourcemap: true,
  esbuildOptions(es_options) {
    es_options.jsx = "preserve";
  },
  outExtension: () => ({ js: ".jsx" }),
});
// src/index.tsx
export function Component() {
  return <div style="color: red; font-weight: bold;">Hello</div>;
}
// package.json
{
  "name": "lib",
  "type": "module",
  "exports": "./out/index.jsx",
  "scripts": {
    "build": "tsup"
  },
  "dependencies": {
    "solid-js": "~1.9.4"
  },
  "devDependencies": {
    "typescript": "~5.2.2",
    "tsup": "~8.3.6"
  }
}

Now run tsup

Expected behavior

The out folder should contain index.jsx and index.jsx.map.

Actual behavior

The out folder only contains index.jsx and no index.jsx.map.

Narrowing it down

When commenting out the outExtension option in the tsup.config.ts the maps start appearing.

Additional info

It does not seem to be an upstream esbuild problem. I ran esbuild with the same config that tsup calls it with and the source maps were there. They aren't when run through tsup though. I think the issue might lie in the this code that's responsible for writing to disc

tsup/src/plugin.ts

Lines 131 to 225 in 0328fd6

const files: Array<ChunkInfo | AssetInfo> = outputFiles
.filter((file) => !file.path.endsWith('.map'))
.map((file): ChunkInfo | AssetInfo => {
if (isJS(file.path) || isCSS(file.path)) {
const relativePath = slash(path.relative(process.cwd(), file.path))
const meta = metafile?.outputs[relativePath]
return {
type: 'chunk',
path: file.path,
code: file.text,
map: outputFiles.find((f) => f.path === `${file.path}.map`)?.text,
entryPoint: meta?.entryPoint,
exports: meta?.exports,
imports: meta?.imports,
}
} else {
return { type: 'asset', path: file.path, contents: file.contents }
}
})
const writtenFiles: WrittenFile[] = []
await Promise.all(
files.map(async (info) => {
for (const plugin of this.plugins) {
if (info.type === 'chunk' && plugin.renderChunk) {
const result = await plugin.renderChunk.call(
this.getContext(),
info.code,
info,
)
if (result) {
info.code = result.code
if (result.map) {
const originalConsumer = await new SourceMapConsumer(
parseSourceMap(info.map),
)
const newConsumer = await new SourceMapConsumer(
parseSourceMap(result.map),
)
const generator = SourceMapGenerator.fromSourceMap(newConsumer)
generator.applySourceMap(originalConsumer, info.path)
info.map = generator.toJSON()
originalConsumer.destroy()
newConsumer.destroy()
}
}
}
}
const inlineSourceMap = this.context!.options.sourcemap === 'inline'
const contents =
info.type === 'chunk'
? info.code +
getSourcemapComment(
inlineSourceMap,
info.map,
info.path,
isCSS(info.path),
)
: info.contents
await outputFile(info.path, contents, {
mode: info.type === 'chunk' ? info.mode : undefined,
})
writtenFiles.push({
get name() {
return path.relative(process.cwd(), info.path)
},
get size() {
return contents.length
},
})
if (info.type === 'chunk' && info.map && !inlineSourceMap) {
const map =
typeof info.map === 'string' ? JSON.parse(info.map) : info.map
const outPath = `${info.path}.map`
const contents = JSON.stringify(map)
await outputFile(outPath, contents)
writtenFiles.push({
get name() {
return path.relative(process.cwd(), outPath)
},
get size() {
return contents.length
},
})
}
}),
)
for (const plugin of this.plugins) {
if (plugin.buildEnd) {
await plugin.buildEnd.call(this.getContext(), { writtenFiles })
}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions