Skip to content

Commit

Permalink
feat: expose getCombinedSourcemap in webpack, rspack and farm
Browse files Browse the repository at this point in the history
  • Loading branch information
KID-joker committed Oct 16, 2024
1 parent 2c27d2a commit 15686b8
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 10 deletions.
6 changes: 4 additions & 2 deletions src/farm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type {
} from '../types'
import type { WatchChangeEvents } from './utils'
import path from 'path'
import { toArray } from '../utils'
import { getCombinedSourcemap, toArray } from '../utils'
import { createFarmContext, unpluginContext } from './context'

import {
Expand Down Expand Up @@ -187,7 +187,9 @@ export function toFarmPlugin(plugin: UnpluginOptions, options?: Record<string, a
&& plugin.transformInclude(params.resolvedPath)
const farmContext = createFarmContext(context, params.resolvedPath)
const resource: TransformResult = await _transform.call(
Object.assign(unpluginContext(context), farmContext),
Object.assign({
getCombinedSourcemap: () => getCombinedSourcemap(params.sourceMapChain, params.resolvedPath, params.content),
}, unpluginContext(context), farmContext),
params.content,
params.resolvedPath,
)
Expand Down
4 changes: 3 additions & 1 deletion src/rspack/loaders/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export default async function transform(
const context = createContext(this)
const res = await plugin.transform.call(
Object.assign(
{},
{
getCombinedSourcemap: () => map,
},
this._compilation && createBuildContext(this._compiler, this._compilation, this),
context,
),
Expand Down
149 changes: 149 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { DecodedSourceMap, RawSourceMap } from '@ampproject/remapping'
import type { SourceMap } from 'rollup'
import type { ResolvedUnpluginOptions } from './types'
import { isAbsolute, normalize } from 'path'
import remapping from '@ampproject/remapping'

/**
* Normalizes a given path when it's absolute. Normalizing means returning a new path by converting
Expand Down Expand Up @@ -72,3 +75,149 @@ export function resolveQuery(query: string | { unpluginName: string }) {
return query.unpluginName
}
}

const postfixRE = /[?#].*$/
export function cleanUrl(url: string): string {
return url.replace(postfixRE, '')
}

/*
The following functions are copied from vite
https://github.com/vitejs/vite/blob/0fe95d4a71930cf55acd628efef59e6eae0f77f7/packages/vite/src/node/utils.ts#L781-L868
MIT License
Copyright (c) 2019-present, VoidZero Inc. and Vite contributors
https://github.com/vitejs/vite/blob/main/LICENSE
*/
const windowsDriveRE = /^[A-Z]:/
const replaceWindowsDriveRE = /^([A-Z]):\//
const linuxAbsolutePathRE = /^\/[^/]/
function escapeToLinuxLikePath(path: string) {
if (windowsDriveRE.test(path)) {
return path.replace(replaceWindowsDriveRE, '/windows/$1/')
}
if (linuxAbsolutePathRE.test(path)) {
return `/linux${path}`
}
return path
}

const revertWindowsDriveRE = /^\/windows\/([A-Z])\//
function unescapeToLinuxLikePath(path: string) {
if (path.startsWith('/linux/')) {
return path.slice('/linux'.length)
}
if (path.startsWith('/windows/')) {
return path.replace(revertWindowsDriveRE, '$1:/')
}
return path
}

const nullSourceMap: RawSourceMap = {
names: [],
sources: [],
mappings: '',
version: 3,
}
function combineSourcemaps(
filename: string,
sourcemapList: Array<DecodedSourceMap | RawSourceMap>,
): RawSourceMap {
if (
sourcemapList.length === 0
|| sourcemapList.every(m => m.sources.length === 0)
) {
return { ...nullSourceMap }
}

// hack for parse broken with normalized absolute paths on windows (C:/path/to/something).
// escape them to linux like paths
// also avoid mutation here to prevent breaking plugin's using cache to generate sourcemaps like vue (see #7442)
sourcemapList = sourcemapList.map((sourcemap) => {
const newSourcemaps = { ...sourcemap }
newSourcemaps.sources = sourcemap.sources.map(source =>
source ? escapeToLinuxLikePath(source) : null,
)
if (sourcemap.sourceRoot) {
newSourcemaps.sourceRoot = escapeToLinuxLikePath(sourcemap.sourceRoot)
}
return newSourcemaps
})

// We don't declare type here so we can convert/fake/map as RawSourceMap
let map // : SourceMap
let mapIndex = 1
const useArrayInterface
= sourcemapList.slice(0, -1).find(m => m.sources.length !== 1) === undefined
if (useArrayInterface) {
map = remapping(sourcemapList, () => null)
}
else {
map = remapping(sourcemapList[0], (sourcefile) => {
const mapForSources = sourcemapList
.slice(mapIndex)
.find(s => s.sources.includes(sourcefile))

if (mapForSources) {
mapIndex++
return mapForSources
}
return null
})
}
if (!map.file) {
delete map.file
}

// unescape the previous hack
map.sources = map.sources.map(source =>
source ? unescapeToLinuxLikePath(source) : source,
)
map.file = filename

return map as RawSourceMap
}

export function getCombinedSourcemap(sourcemapChain: Nullable<Arrayable<SourceMap | string>>, filename: string, originalCode: string): SourceMap | null {
sourcemapChain = toArray(sourcemapChain)
let combinedMap = null

for (let m of sourcemapChain) {
if (typeof m === 'string')
m = JSON.parse(m)
if (!('version' in (m as SourceMap))) {
// { mappings: '' }
if ((m as SourceMap).mappings === '') {
combinedMap = { mappings: '' }
break
}
// empty, nullified source map
combinedMap = null
break
}
if (!combinedMap) {
const sm = m as SourceMap
// sourcemap should not include `sources: [null]` (because `sources` should be string) nor
// `sources: ['']` (because `''` means the path of sourcemap)
// but MagicString generates this when `filename` option is not set.
// Rollup supports these and therefore we support this as well
if (sm.sources.length === 1 && !sm.sources[0]) {
combinedMap = {
...sm,
sources: [filename],
sourcesContent: [originalCode],
}
}
else {
combinedMap = sm
}
}
else {
combinedMap = combineSourcemaps(cleanUrl(filename), [
m as RawSourceMap,
combinedMap as RawSourceMap,
]) as SourceMap
}
}
return combinedMap as SourceMap
}
20 changes: 13 additions & 7 deletions src/webpack/loaders/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@ export default async function transform(this: LoaderContext<{ unpluginName: stri

const context = createContext(this)
const res = await plugin.transform.call(
Object.assign({}, createBuildContext({
addWatchFile: (file) => {
this.addDependency(file)
Object.assign(
{
getCombinedSourcemap: () => map,
},
getWatchFiles: () => {
return this.getDependencies()
},
}, this._compiler!, this._compilation, this), context),
createBuildContext({
addWatchFile: (file) => {
this.addDependency(file)
},
getWatchFiles: () => {
return this.getDependencies()
},
}, this._compiler!, this._compilation, this),
context,
),
source,
this.resource,
)
Expand Down

0 comments on commit 15686b8

Please sign in to comment.