Skip to content

Commit

Permalink
Sourcemap: source mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Mar 19, 2024
1 parent 713efc6 commit 76c3d7e
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const colorWarning = (message: string): string => {
}

export const prettySourceMessage = (message: string, span: Span, source: Source, notes: string[] = []): string => {
const start = indexToLocation(span.start, source)!
const start = indexToLocation(span.start, source.code)!
const locationStr = `${source.filepath}:${locationToString(start)}`
const locationMsg = ` at ${locationStr}`
const notesStr = notes.length > 0 ? notes.map(n => ` note: ${n}`).join('\n') : ''
Expand Down
22 changes: 19 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Context, pathToVid } from './scope'
import { buildInstanceRelations } from './scope/trait'
import { checkModule, checkTopLevelDefinition, prepareModule } from './semantic'
import { Source } from './source'
import { foldEmitTree } from './sourcemap'
import { createSourceMap, foldEmitTree } from './sourcemap'
import { assert } from './util/todo'

const dir = dirname(fileURLToPath(import.meta.url))
Expand Down Expand Up @@ -202,9 +202,25 @@ if (config.emit) {
const mainFnName = mainFn?.kind === 'fn-def' ? mainFn.name.value : undefined
const emitNode = emitModule(m, ctx, mainFnName)
const { emit, map } = foldEmitTree(emitNode)
const js = [emit, native].filter(m => m.length > 0).join('\n\n')

const sourceMapLink = `//# sourceMappingURL=${moduleOutPath.name}.js.map`
const js = [emit, native, sourceMapLink].filter(m => m.length > 0).join('\n\n')
const jsPath = join(moduleOutPath.dir, moduleOutPath.name) + '.js'
writeFile(jsPath, js).then(() => console.info(`emit: js ${jsPath} [${js.length}B]`))

const sourceMap = JSON.stringify(
createSourceMap(
basename(jsPath),
relative(moduleOutPath.dir, m.source.filepath),
m.source.code,
js,
map
)
)
const sourceMapPath = join(moduleOutPath.dir, moduleOutPath.name) + '.js.map'
writeFile(sourceMapPath, sourceMap).then(() =>
console.info(`emit: source map ${sourceMapPath} [${sourceMap.length}B]`)
)
})
} else {
const m = pkg.modules[0]
Expand All @@ -215,7 +231,7 @@ if (config.emit) {
: undefined
const mainFnName = mainFn?.kind === 'fn-def' ? mainFn.name.value : undefined
const emitNode = emitModule(m, ctx, mainFnName)
const { emit, map } = foldEmitTree(emitNode)
const { emit } = foldEmitTree(emitNode)
const jsPath = join(config.outPath, parse(config.pkgPath).name) + '.js'
writeFile(jsPath, emit).then(() => console.info(`emit: js ${jsPath} [${emit.length}B]`))
}
Expand Down
8 changes: 4 additions & 4 deletions src/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ export interface Location {
column: number
}

export const indexToLocation = (index: number, source: Source): Location | undefined => {
export const indexToLocation = (index: number, code: string): Location | undefined => {
let line = 0
let column = 0
for (let i = 0; i < index; i++) {
if (isNewline(source.code[i])) {
if (isNewline(code[i])) {
line++
column = 0
} else {
Expand All @@ -32,8 +32,8 @@ export const indexToLocation = (index: number, source: Source): Location | undef
}

export const prettyLineAt = (span: Span, source: Source): string => {
const start = indexToLocation(span.start, source)
const end = indexToLocation(span.end, source)
const start = indexToLocation(span.start, source.code)
const end = indexToLocation(span.end, source.code)
if (!start || !end) return '<outside of a file>'
const lineNumSize = start.line.toString().length
const padSize = Math.max(6, 3 + lineNumSize)
Expand Down
79 changes: 77 additions & 2 deletions src/sourcemap/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { EmitNode } from '../codegen/js/node'
import { Span } from '../location'
import { Span, indexToLocation } from '../location'
import { getSpan } from '../parser'
import { encode } from './base64vlq'

export interface SourceMap {
version: number
file: string
sourceRoot: string
sources: string[]
names: string[]
mappings: string
}

export const foldEmitTree = (node: EmitNode, index: number = 0): { emit: string; map: [Span, Span][] } => {
const map: [Span, Span][] = []
Expand All @@ -25,5 +35,70 @@ export const foldEmitTree = (node: EmitNode, index: number = 0): { emit: string;
const span = getSpan(node.parseNode)
map.push([span, { start, end: index + 1 }])
}
return { emit, map }
return { emit, map: map.toSorted(([, ad], [, bd]) => ad.start - bd.start) }
}

export const createSourceMap = (
outFilename: string,
srcPath: string,
src: string,
out: string,
map: [Span, Span][]
): SourceMap => {
const lines = []
let line = []
let sLine: number | undefined
let sCol: number | undefined
let dLine: number | undefined
let dCol: number | undefined

for (const [sSpan, dSpan] of map) {
const ms = []
const sLoc = indexToLocation(sSpan.start, src)!
const dLoc = indexToLocation(dSpan.start, out)!

if (dLine !== undefined && dLine !== dLoc.line) {
lines.push(line)
line = []
dCol = undefined
}

if (dCol !== undefined) {
ms.push(dLoc.column - dCol)
} else {
ms.push(dLoc.column)
}

ms.push(0)

if (sLine !== undefined) {
ms.push(sLoc.line - sLine)
} else {
ms.push(sLoc.line)
}

if (sCol !== undefined) {
ms.push(sLoc.column - sCol)
} else {
ms.push(sLoc.column)
}

dLine = dLoc.line
dCol = dLoc.column
sLine = sLoc.line
sCol = sLoc.column

line.push(ms.map(encode).join(''))
}
if (line.length > 0) {
lines.push(line)
}
return {
version: 3,
file: outFilename,
sourceRoot: '',
sources: [srcPath],
names: [],
mappings: lines.join(';')
}
}

0 comments on commit 76c3d7e

Please sign in to comment.