Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sourcemap types #2985

Merged
merged 17 commits into from Jul 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/Chunk.ts
Expand Up @@ -14,7 +14,13 @@ import ExternalModule from './ExternalModule';
import finalisers from './finalisers/index';
import Graph from './Graph';
import Module from './Module';
import { GlobalsOption, OutputOptions, RawSourceMap, RenderedChunk, RenderedModule } from './rollup/types';
import {
DecodedSourceMapOrMissing,
GlobalsOption,
OutputOptions,
RenderedChunk,
RenderedModule
} from './rollup/types';
import { Addons } from './utils/addons';
import { toBase64 } from './utils/base64';
import collapseSourcemaps from './utils/collapseSourcemaps';
Expand Down Expand Up @@ -676,7 +682,7 @@ export default class Chunk {
timeEnd('render format', 3);

let map: SourceMap = null as any;
const chunkSourcemapChain: RawSourceMap[] = [];
const chunkSourcemapChain: DecodedSourceMapOrMissing[] = [];

return renderChunk({
chunk: this,
Expand Down
9 changes: 5 additions & 4 deletions src/Module.ts
Expand Up @@ -32,9 +32,10 @@ import ExternalModule from './ExternalModule';
import Graph from './Graph';
import {
Asset,
DecodedSourceMapOrMissing,
EmittedChunk,
ExistingDecodedSourceMap,
ModuleJSON,
RawSourceMap,
ResolvedIdMap,
RollupError,
RollupWarning,
Expand Down Expand Up @@ -199,11 +200,11 @@ export default class Module {
manualChunkAlias: string = null as any;
moduleSideEffects: boolean;
originalCode!: string;
originalSourcemap!: RawSourceMap | null;
originalSourcemap!: ExistingDecodedSourceMap | null;
reexports: { [name: string]: ReexportDescription } = Object.create(null);
resolvedIds!: ResolvedIdMap;
scope!: ModuleScope;
sourcemapChain!: RawSourceMap[];
sourcemapChain!: DecodedSourceMapOrMissing[];
sources: string[] = [];
transformAssets?: Asset[];
transformChunks?: EmittedChunk[];
Expand Down Expand Up @@ -540,7 +541,7 @@ export default class Module {
this.code = code;
this.originalCode = originalCode;
this.originalSourcemap = originalSourcemap;
this.sourcemapChain = sourcemapChain as RawSourceMap[];
this.sourcemapChain = sourcemapChain;
if (transformAssets) {
this.transformAssets = transformAssets;
}
Expand Down
39 changes: 31 additions & 8 deletions src/rollup/types.d.ts
Expand Up @@ -38,6 +38,21 @@ export interface RollupLogProps {
url?: string;
}

export type SourceMapSegment =
| [number]
| [number, number, number, number]
| [number, number, number, number, number];

export interface ExistingDecodedSourceMap {
file?: string;
mappings: SourceMapSegment[][];
names: string[];
sourceRoot?: string;
sources: string[];
sourcesContent?: string[];
version: number;
}

export interface ExistingRawSourceMap {
file?: string;
mappings: string;
Expand All @@ -48,7 +63,13 @@ export interface ExistingRawSourceMap {
version: number;
}

export type RawSourceMap = { mappings: '' } | ExistingRawSourceMap;
export type DecodedSourceMapOrMissing =
| {
mappings?: never;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

missing: true;
plugin: string;
}
| ExistingDecodedSourceMap;

export interface SourceMap {
file: string;
Expand All @@ -61,10 +82,12 @@ export interface SourceMap {
toUrl(): string;
}

export type SourceMapInput = ExistingRawSourceMap | string | null | { mappings: '' };

export interface SourceDescription {
ast?: ESTree.Program;
code: string;
map?: string | RawSourceMap;
map?: SourceMapInput;
moduleSideEffects?: boolean | null;
}

Expand All @@ -79,9 +102,9 @@ export interface TransformModuleJSON {
customTransformCache: boolean;
moduleSideEffects: boolean | null;
originalCode: string;
originalSourcemap: RawSourceMap | null;
originalSourcemap: ExistingDecodedSourceMap | null;
resolvedIds?: ResolvedIdMap;
sourcemapChain: (RawSourceMap | { missing: true; plugin: string })[];
sourcemapChain: DecodedSourceMapOrMissing[];
transformDependencies: string[] | null;
}

Expand Down Expand Up @@ -207,8 +230,8 @@ export type TransformChunkHook = (
code: string,
options: OutputOptions
) =>
| Promise<{ code: string; map: RawSourceMap } | null | undefined>
| { code: string; map: RawSourceMap }
| Promise<{ code: string; map?: SourceMapInput } | null | undefined>
| { code: string; map?: SourceMapInput }
| null
| undefined;

Expand All @@ -218,8 +241,8 @@ export type RenderChunkHook = (
chunk: RenderedChunk,
options: OutputOptions
) =>
| Promise<{ code: string; map: RawSourceMap } | null>
| { code: string; map: RawSourceMap }
| Promise<{ code: string; map?: SourceMapInput } | null>
| { code: string; map?: SourceMapInput }
| string
| null;

Expand Down
108 changes: 54 additions & 54 deletions src/utils/collapseSourcemaps.ts
@@ -1,7 +1,11 @@
import { DecodedSourceMap, SourceMap } from 'magic-string';
import Chunk from '../Chunk';
import Module from '../Module';
import { ExistingRawSourceMap, RawSourceMap } from '../rollup/types';
import {
DecodedSourceMapOrMissing,
ExistingDecodedSourceMap,
SourceMapSegment
} from '../rollup/types';
import { error } from './error';
import { basename, dirname, relative, resolve } from './path';

Expand All @@ -21,10 +25,6 @@ class Source {
}
}

type SourceMapSegmentVector =
| [number, number, number, number, number]
| [number, number, number, number];

interface SourceMapSegmentObject {
column: number;
line: number;
Expand All @@ -33,11 +33,14 @@ interface SourceMapSegmentObject {
}

class Link {
mappings: SourceMapSegmentVector[][];
mappings: SourceMapSegment[][];
names: string[];
sources: Source[];
sources: (Source | Link)[];

constructor(map: { mappings: SourceMapSegmentVector[][]; names: string[] }, sources: Source[]) {
constructor(
map: { mappings: SourceMapSegment[][]; names: string[] },
sources: (Source | Link)[]
) {
this.sources = sources;
this.names = map.names;
this.mappings = map.mappings;
Expand All @@ -51,16 +54,17 @@ class Link {
const mappings = [];

for (const line of this.mappings) {
const tracedLine: SourceMapSegmentVector[] = [];
const tracedLine: SourceMapSegment[] = [];

for (const segment of line) {
if (segment.length == 1) continue;
const source = this.sources[segment[1]];
if (!source) continue;

const traced = source.traceSegment(
segment[2],
segment[3],
this.names[segment[4] as number]
segment.length === 5 ? this.names[segment[4]] : ''
);

if (traced) {
Expand All @@ -77,13 +81,11 @@ class Link {
sourcesContent[sourceIndex] !== traced.source.content
) {
error({
message: `Multiple conflicting contents for sourcemap source ${
traced.source.filename
}`
message: `Multiple conflicting contents for sourcemap source ${traced.source.filename}`
});
}

const tracedSegment: SourceMapSegmentVector = [
const tracedSegment: SourceMapSegment = [
segment[0],
sourceIndex,
traced.line,
Expand All @@ -97,7 +99,7 @@ class Link {
names.push(traced.name);
}

(tracedSegment as SourceMapSegmentVector)[4] = nameIndex;
(tracedSegment as SourceMapSegment)[4] = nameIndex;
}

tracedLine.push(tracedSegment);
Expand All @@ -110,7 +112,7 @@ class Link {
return { sources, sourcesContent, names, mappings };
}

traceSegment(line: number, column: number, name: string) {
traceSegment(line: number, column: number, name: string): SourceMapSegmentObject | null {
const segments = this.mappings[line];
if (!segments) return null;

Expand All @@ -122,13 +124,14 @@ class Link {
const m = (i + j) >> 1;
const segment = segments[m];
if (segment[0] === column) {
if (segment.length == 1) return null;
const source = this.sources[segment[1]];
if (!source) return null;

return source.traceSegment(
segment[2],
segment[3],
this.names[segment[4] as number] || name
segment.length === 5 ? this.names[segment[4]] : name
);
}
if (segment[0] > column) {
Expand All @@ -142,72 +145,69 @@ class Link {
}
}

// TODO TypeScript: Fix <any> typecasts
export default function collapseSourcemaps(
bundle: Chunk,
file: string,
map: DecodedSourceMap,
modules: Module[],
bundleSourcemapChain: RawSourceMap[],
bundleSourcemapChain: DecodedSourceMapOrMissing[],
excludeContent: boolean
) {
function linkMap(source: Source, map: any) {
if (map.missing) {
bundle.graph.warn({
code: 'SOURCEMAP_BROKEN',
message: `Sourcemap is likely to be incorrect: a plugin${
map.plugin ? ` ('${map.plugin}')` : ``
} was used to transform files, but didn't generate a sourcemap for the transformation. Consult the plugin documentation for help`,
plugin: map.plugin,
url: `https://rollupjs.org/guide/en/#warning-sourcemap-is-likely-to-be-incorrect`
});

map = {
mappings: '',
names: []
};
function linkMap(source: Source | Link, map: DecodedSourceMapOrMissing) {
if (map.mappings) {
return new Link(map, [source]);
}

return new Link(map, [source]);
bundle.graph.warn({
code: 'SOURCEMAP_BROKEN',
message: `Sourcemap is likely to be incorrect: a plugin${
map.plugin ? ` ('${map.plugin}')` : ``
} was used to transform files, but didn't generate a sourcemap for the transformation. Consult the plugin documentation for help`,
plugin: map.plugin,
url: `https://rollupjs.org/guide/en/#warning-sourcemap-is-likely-to-be-incorrect`
});

return new Link(
{
mappings: [],
names: []
},
[source]
);
}

const moduleSources = modules
.filter(module => !module.excludeFromSourcemap)
.map(module => {
let sourcemapChain = module.sourcemapChain;

let source: Source;
const originalSourcemap = module.originalSourcemap as ExistingRawSourceMap;
let source: Source | Link;
const originalSourcemap = module.originalSourcemap;
if (!originalSourcemap) {
source = new Source(module.id, module.originalCode);
} else {
const sources = originalSourcemap.sources;
const sourcesContent = originalSourcemap.sourcesContent || [];

if (sources == null || (sources.length <= 1 && sources[0] == null)) {
source = new Source(module.id, sourcesContent[0]);
sourcemapChain = [originalSourcemap as RawSourceMap].concat(sourcemapChain);
} else {
// TODO indiscriminately treating IDs and sources as normal paths is probably bad.
const directory = dirname(module.id) || '.';
const sourceRoot = originalSourcemap.sourceRoot || '.';
// TODO indiscriminately treating IDs and sources as normal paths is probably bad.
const directory = dirname(module.id) || '.';
const sourceRoot = originalSourcemap.sourceRoot || '.';

const baseSources = sources.map(
(source, i) => new Source(resolve(directory, sourceRoot, source), sourcesContent[i])
);
const baseSources = sources.map(
(source, i) => new Source(resolve(directory, sourceRoot, source), sourcesContent[i])
);

source = new Link(originalSourcemap as any, baseSources) as any;
}
source = new Link(originalSourcemap, baseSources);
}

source = sourcemapChain.reduce(linkMap as any, source);
source = module.sourcemapChain.reduce(linkMap, source);

return source;
});

let source = new Link(map as any, moduleSources);
// DecodedSourceMap (from magic-string) uses a number[] instead of the more
// correct SourceMapSegment tuples. Cast it here to gain type safety.
let source = new Link(map as ExistingDecodedSourceMap, moduleSources);

source = bundleSourcemapChain.reduce(linkMap as any, source);
source = bundleSourcemapChain.reduce(linkMap, source);

let { sources, sourcesContent, names, mappings } = source.traceMappings();

Expand Down
29 changes: 29 additions & 0 deletions src/utils/decodedSourcemap.ts
@@ -0,0 +1,29 @@
import { decode } from 'sourcemap-codec';
import { ExistingDecodedSourceMap, ExistingRawSourceMap, SourceMapInput } from '../rollup/types';

type Input = SourceMapInput | ExistingDecodedSourceMap | undefined;

export function decodedSourcemap(map: Input): ExistingDecodedSourceMap | null {
if (!map) return null;

if (typeof map === 'string') {
map = JSON.parse(map) as ExistingRawSourceMap;
}
if (map.mappings === '') {
return {
mappings: [],
names: [],
sources: [],
version: 3
};
}

let mappings;
if (typeof map.mappings === 'string') {
mappings = decode(map.mappings);
} else {
mappings = map.mappings;
}

return { ...(map as ExistingRawSourceMap | ExistingDecodedSourceMap), mappings };
}