Skip to content

Commit

Permalink
renderChunk hook to replace transformChunk
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford committed Aug 13, 2018
1 parent 4d491a4 commit 68cdff5
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 129 deletions.
63 changes: 37 additions & 26 deletions src/Chunk.ts
Expand Up @@ -12,18 +12,24 @@ import ExternalModule from './ExternalModule';
import finalisers from './finalisers/index';
import Graph from './Graph';
import Module from './Module';
import { GlobalsOption, OutputOptions, RawSourceMap, RenderedModule } from './rollup/types';
import {
GlobalsOption,
OutputOptions,
RawSourceMap,
RenderChunk,
RenderedModule
} from './rollup/types';
import { Addons } from './utils/addons';
import { toBase64 } from './utils/base64';
import collapseSourcemaps from './utils/collapseSourcemaps';
import error from './utils/error';
import getIndentString from './utils/getIndentString';
import { makeLegal } from './utils/identifierHelpers';
import { basename, dirname, normalize, relative, resolve } from './utils/path';
import renderChunk from './utils/renderChunk';
import { RenderOptions } from './utils/renderHelpers';
import { makeUnique, renderNamePattern } from './utils/renderNamePattern';
import { timeEnd, timeStart } from './utils/timers';
import transformChunk from './utils/transformChunk';

export interface ModuleDeclarations {
exports: ChunkExports;
Expand Down Expand Up @@ -1027,7 +1033,7 @@ export default class Chunk {
this.id = outName;
}

render(options: OutputOptions, addons: Addons) {
render(options: OutputOptions, addons: Addons, outputChunk: RenderChunk) {
timeStart('render format', 3);

if (!this.renderedSource)
Expand Down Expand Up @@ -1105,32 +1111,37 @@ export default class Chunk {
let map: SourceMap = null;
const chunkSourcemapChain: RawSourceMap[] = [];

return transformChunk(this.graph, this, prevCode, chunkSourcemapChain, options).then(
(code: string) => {
if (options.sourcemap) {
timeStart('sourcemap', 3);

let file: string;
if (options.file) file = resolve(options.sourcemapFile || options.file);
else if (options.dir) file = resolve(options.dir, this.id);
else file = resolve(this.id);

if (this.graph.pluginDriver.hasLoadersOrTransforms) {
const decodedMap = magicString.generateDecodedMap({});
map = collapseSourcemaps(this, file, decodedMap, this.usedModules, chunkSourcemapChain);
} else {
map = magicString.generateMap({ file, includeContent: true });
}

map.sources = map.sources.map(normalize);

timeEnd('sourcemap', 3);
return renderChunk({
graph: this.graph,
chunk: this,
renderChunk: outputChunk,
code: prevCode,
sourcemapChain: chunkSourcemapChain,
options
}).then((code: string) => {
if (options.sourcemap) {
timeStart('sourcemap', 3);

let file: string;
if (options.file) file = resolve(options.sourcemapFile || options.file);
else if (options.dir) file = resolve(options.dir, this.id);
else file = resolve(this.id);

if (this.graph.pluginDriver.hasLoadersOrTransforms) {
const decodedMap = magicString.generateDecodedMap({});
map = collapseSourcemaps(this, file, decodedMap, this.usedModules, chunkSourcemapChain);
} else {
map = magicString.generateMap({ file, includeContent: true });
}

if (options.compact !== true && code[code.length - 1] !== '\n') code += '\n';
map.sources = map.sources.map(normalize);

return { code, map };
timeEnd('sourcemap', 3);
}
);

if (options.compact !== true && code[code.length - 1] !== '\n') code += '\n';

return { code, map };
});
}
}
19 changes: 6 additions & 13 deletions src/rollup/index.ts
Expand Up @@ -274,7 +274,7 @@ export default function rollup(
optimized = true;
}

// then name all chunks
// name all chunks
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const imports = chunk.getImportIds();
Expand All @@ -288,14 +288,6 @@ export default function rollup(
? inputOptions.input[0]
: <string>inputOptions.input)
);
const outputChunk: OutputChunk = {
imports,
exports,
modules,
code: undefined,
map: undefined
};
outputBundle[singleChunk.id] = outputChunk;
} else if (inputOptions.experimentalPreserveModules) {
chunk.generateIdPreserveModules(inputBase);
} else {
Expand All @@ -309,7 +301,10 @@ export default function rollup(
}
chunk.generateId(pattern, patternName, addons, outputOptions, outputBundle);
}

outputBundle[chunk.id] = {
fileName: chunk.id,
isEntry: chunk.entryModule !== undefined,
imports,
exports,
modules,
Expand All @@ -318,12 +313,10 @@ export default function rollup(
};
}

// render chunk import statements and finalizer wrappers given known names
return Promise.all(
chunks.map(chunk => {
const chunkId = chunk.id;
return chunk.render(outputOptions, addons).then(rendered => {
const outputChunk = <OutputChunk>outputBundle[chunkId];
const outputChunk = <OutputChunk>outputBundle[chunk.id];
return chunk.render(outputOptions, addons, outputChunk).then(rendered => {
outputChunk.code = rendered.code;
outputChunk.map = rendered.map;

Expand Down
21 changes: 8 additions & 13 deletions src/rollup/types.d.ts
Expand Up @@ -129,25 +129,15 @@ export type TransformHook = (
| void;

export type TransformChunkHook = (
this: PluginContext,
code: string,
options: OutputOptions,
chunk: OutputChunk
options: OutputOptions
) =>
| Promise<{ code: string; map: RawSourceMap } | void>
| { code: string; map: RawSourceMap }
| void
| null;

export type TransformChunkHookBound = (
this: PluginContext,
code: string,
options: OutputOptions,
chunk: OutputChunk
) =>
| Promise<{ code: string; map: RawSourceMap } | void>
| { code: string; map: RawSourceMap }
| void;

export type ResolveDynamicImportHook = (
this: PluginContext,
specifier: string | ESTree.Node,
Expand Down Expand Up @@ -354,12 +344,17 @@ export interface RenderedModule {
originalLength: number;
}

export interface OutputChunk {
export interface RenderChunk {
fileName: string;
isEntry: boolean;
imports: string[];
exports: string[];
modules: {
[id: string]: RenderedModule;
};
}

export interface OutputChunk extends RenderChunk {
code: string;
map?: SourceMap;
}
Expand Down
69 changes: 69 additions & 0 deletions src/utils/renderChunk.ts
@@ -0,0 +1,69 @@
import { decode } from 'sourcemap-codec';
import Chunk from '../Chunk';
import Graph from '../Graph';
import { OutputOptions, Plugin, RawSourceMap, RenderChunk } from '../rollup/types';
import error from './error';

export default function renderChunk({
graph,
chunk,
renderChunk,
code,
sourcemapChain,
options
}: {
graph: Graph;
chunk: Chunk;
renderChunk: RenderChunk;
code: string;
sourcemapChain: RawSourceMap[];
options: OutputOptions;
}) {
const renderChunkReducer = (code: string, result: any, plugin: Plugin): string => {
if (result == null) return code;

if (typeof result === 'string')
result = {
code: result,
map: undefined
};

const map = typeof result.map === 'string' ? JSON.parse(result.map) : result.map;
if (map && typeof map.mappings === 'string') map.mappings = decode(map.mappings);

// strict null check allows 'null' maps to not be pushed to the chain, while 'undefined' gets the missing map warning
if (map !== null) sourcemapChain.push(map || { missing: true, plugin: plugin.name });

return result.code;
};

let inTransformBundle = false;
let inRenderChunk = true;
return graph.pluginDriver
.hookReduceArg0('renderChunk', [code, renderChunk, options], renderChunkReducer)
.then(code => {
inRenderChunk = false;
return graph.pluginDriver.hookReduceArg0(
'transformChunk',
[code, options, chunk],
renderChunkReducer
);
})
.then(code => {
inTransformBundle = true;
return graph.pluginDriver.hookReduceArg0(
'transformBundle',
[code, options, chunk],
renderChunkReducer
);
})
.catch(err => {
if (inRenderChunk) throw err;
error(err, {
code: inTransformBundle ? 'BAD_BUNDLE_TRANSFORMER' : 'BAD_CHUNK_TRANSFORMER',
message: `Error transforming ${(inTransformBundle ? 'bundle' : 'chunk') +
(err.plugin ? ` with '${err.plugin}' plugin` : '')}: ${err.message}`,
plugin: err.plugin
});
});
}
77 changes: 0 additions & 77 deletions src/utils/transformChunk.ts

This file was deleted.

35 changes: 35 additions & 0 deletions test/hooks/index.js
Expand Up @@ -615,6 +615,41 @@ module.exports = input;
});
});

it('supports renderChunk in place of transformBundle and transformChunk', () => {
let calledHook = false;
return rollup
.rollup({
input: 'input',
experimentalCodeSplitting: true,
plugins: [
loader({ input: `alert('hello')` }),
{
renderChunk (code, chunk, options) {
calledHook = true;
assert.equal(chunk.fileName, 'input.js');
assert.equal(chunk.isEntry, true);
assert.equal(chunk.exports.length, 0);
assert.ok(chunk.modules['input']);
try {
this.emitAsset('test.ext', 'hello world');
}
catch (e) {
assert.equal(e.code, 'ASSETS_ALREADY_FINALISED');
}
}
}
]
})
.then(bundle => {
return bundle.generate({
format: 'es',
assetFileNames: '[name][extname]'
});
})
.then(() => {
assert.equal(calledHook, true);
});
});

it('passes bundle object to generateBundle hook', () => {
return rollup
Expand Down

0 comments on commit 68cdff5

Please sign in to comment.