Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix: bring back inline sourcemap support, closes #495
  • Loading branch information
egoist committed Dec 15, 2021
1 parent d428ce7 commit 0826b35
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 44 deletions.
4 changes: 4 additions & 0 deletions docs/README.md
Expand Up @@ -163,6 +163,10 @@ If you want to inline sourcemap, you can try:
tsup index.ts --sourcemap inline
```

> Warn: Note that inline sourcemap is solely used for development, e.g. when developing a browser extension and the access to `.map` file is not allowed, and it's not recommended for production.
> Warn: Source map is not supported in `--dts` build.
### Bundle formats

Supported format: `esm`, `cjs`, (default) and `iife`.
Expand Down
4 changes: 2 additions & 2 deletions src/cli-main.ts
Expand Up @@ -39,8 +39,8 @@ export async function main(options: Options = {}) {
.option('--dts-resolve', 'Resolve externals types used for d.ts files')
.option('--dts-only', 'Emit declaration files only')
.option(
'--sourcemap [option]',
'Generate sourcemap, "external", "inline", "both"'
'--sourcemap [inline]',
'Generate external sourcemap, or inline source: --sourcemap inline'
)
.option(
'--watch [path]',
Expand Down
2 changes: 1 addition & 1 deletion src/esbuild/index.ts
Expand Up @@ -237,7 +237,7 @@ export async function runEsbuild(
logger.success(format, `⚡️ Build success in ${Math.floor(timeInMs)}ms`)

await pluginContainer.buildFinished({
files: result.outputFiles,
outputFiles: result.outputFiles,
metafile: result.metafile,
})
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/report-size.ts
Expand Up @@ -2,6 +2,7 @@ import * as colors from 'colorette'
import { Logger } from '../log'

const prettyBytes = (bytes: number) => {
if (bytes === 0) return '0 B'
const unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const exp = Math.floor(Math.log(bytes) / Math.log(1024))
return `${(bytes / Math.pow(1024, exp)).toFixed(2)} ${unit[exp]}`
Expand Down
2 changes: 1 addition & 1 deletion src/options.ts
Expand Up @@ -58,7 +58,7 @@ export type Options = {
[k: string]: string
}
dts?: boolean | string | DtsConfig
sourcemap?: boolean
sourcemap?: boolean | 'inline'
/** Always bundle modules matching given patterns */
noExternal?: (string | RegExp)[]
/** Don't bundle these modules */
Expand Down
102 changes: 70 additions & 32 deletions src/plugin.ts
Expand Up @@ -41,7 +41,7 @@ export type RenderChunk = (
export type BuildStart = (this: PluginContext) => MaybePromise<void>
export type BuildEnd = (
this: PluginContext,
ctx: { metafile?: Metafile }
ctx: { writtenFiles: WrittenFile[] }
) => MaybePromise<void>

export type ModifyEsbuildOptions = (
Expand All @@ -68,6 +68,8 @@ export type PluginContext = {
logger: Logger
}

export type WrittenFile = { readonly name: string; readonly size: number }

const parseSourceMap = (map?: string | object | null) => {
return typeof map === 'string' ? JSON.parse(map) : map
}
Expand Down Expand Up @@ -109,27 +111,31 @@ export class PluginContainer {
}

async buildFinished({
files,
outputFiles,
metafile,
}: {
files: OutputFile[]
outputFiles: OutputFile[]
metafile?: Metafile
}) {
const files: Array<ChunkInfo | AssetInfo> = outputFiles
.filter((file) => !file.path.endsWith('.map'))
.map((file) => {
if (isJS(file.path) || isCSS(file.path)) {
return {
type: 'chunk',
path: file.path,
code: file.text,
map: outputFiles.find((f) => f.path === `${file.path}.map`)?.text,
}
} else {
return { type: 'asset', path: file.path, contents: file.contents }
}
})

const writtenFiles: WrittenFile[] = []

await Promise.all(
files.map(async (file) => {
const info: AssetInfo | ChunkInfo =
isJS(file.path) || isCSS(file.path)
? {
type: 'chunk',
path: file.path,
code: file.text,
map: files.find((f) => f.path === `${file.path}.map`)?.text,
}
: {
type: 'asset',
path: file.path,
contents: file.contents,
}
files.map(async (info) => {
for (const plugin of this.plugins) {
if (info.type === 'chunk' && plugin.renderChunk) {
const result = await plugin.renderChunk.call(
Expand Down Expand Up @@ -157,35 +163,67 @@ export class PluginContainer {
}
}

await outputFile(
info.path,
const inlineSourceMap = this.context!.options.sourcemap === 'inline'
const contents =
info.type === 'chunk'
? info.code + getSourcemapComment(!!info.map, info.path, isCSS(file.path))
: info.contents,
{ mode: info.type === 'chunk' ? info.mode : undefined }
)
if (info.type === 'chunk' && info.map) {
? 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
// map.sources = map.sources?.map((name: string) =>
// path.relative(path.dirname(info.path), name)
// )
await outputFile(`${info.path}.map`, JSON.stringify(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(), { metafile })
await plugin.buildEnd.call(this.getContext(), { writtenFiles })
}
}
}
}

const getSourcemapComment = (hasMap: boolean, filepath: string, isCssFile: boolean) => {
if (!hasMap) return ''
const getSourcemapComment = (
inline: boolean,
map: RawSourceMap | string | null | undefined,
filepath: string,
isCssFile: boolean
) => {
if (!map) return ''
const prefix = isCssFile ? '/*' : '//'
const suffix = isCssFile ? ' */' : ''
return `${prefix}# sourceMappingURL=${path.basename(filepath)}.map${suffix}`
const url = inline
? `data:application/json;base64,${Buffer.from(
typeof map === 'string' ? map : JSON.stringify(map)
).toString('base64')}`
: `${path.basename(filepath)}.map`
return `${prefix}# sourceMappingURL=${url}${suffix}`
}
7 changes: 3 additions & 4 deletions src/plugins/size-reporter.ts
Expand Up @@ -5,15 +5,14 @@ export const sizeReporter = (): Plugin => {
return {
name: 'size-reporter',

buildEnd({ metafile }) {
if (!metafile) return
buildEnd({ writtenFiles }) {
reportSize(
this.logger,
this.format,
Object.keys(metafile.outputs).reduce((res, name) => {
writtenFiles.reduce((res, file) => {
return {
...res,
[name]: metafile!.outputs[name].bytes,
[file.name]: file.size,
}
}, {})
)
Expand Down
9 changes: 5 additions & 4 deletions test/index.test.ts
Expand Up @@ -162,7 +162,7 @@ test('enable --dts-resolve for specific module', async (t) => {
})

test('bundle graphql-tools with --sourcemap flag', async (t) => {
await run(
const { outFiles } = await run(
t.title,
{
'input.ts': `export { makeExecutableSchema } from 'graphql-tools'`,
Expand All @@ -171,11 +171,11 @@ test('bundle graphql-tools with --sourcemap flag', async (t) => {
flags: ['--sourcemap'],
}
)
t.pass()
t.deepEqual(outFiles, ['input.js', 'input.js.map'])
})

test('bundle graphql-tools with --sourcemap inline flag', async (t) => {
const { output } = await run(
const { output, outFiles } = await run(
t.title,
{
'input.ts': `export { makeExecutableSchema } from 'graphql-tools'`,
Expand All @@ -185,7 +185,8 @@ test('bundle graphql-tools with --sourcemap inline flag', async (t) => {
}
)

t.assert(output.includes('//# sourceMappingURL='))
t.assert(output.includes('//# sourceMappingURL=data:application/json;base64'))
t.deepEqual(outFiles, ['input.js'])
})

test('multiple formats', async (t) => {
Expand Down

0 comments on commit 0826b35

Please sign in to comment.