Skip to content

Commit

Permalink
feat(replace): allow plugin to operate as an output plugin (#55)
Browse files Browse the repository at this point in the history
* add execution hook to replace

* add tests to replacement

* break apart the executeReplacements function

* move sourcemap tests to separate module, test out different configurations

* fix(tests): correct sourcemaps test

* clean up eslint-disable directives
  • Loading branch information
darthtrevino authored and shellscape committed Dec 3, 2019
1 parent d6f988c commit 0af323a
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 83 deletions.
61 changes: 37 additions & 24 deletions packages/replace/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,44 +42,57 @@ export default function replace(options = {}) {
const keys = Object.keys(functionValues)
.sort(longest)
.map(escape);

const pattern = delimiters
? new RegExp(`${escape(delimiters[0])}(${keys.join('|')})${escape(delimiters[1])}`, 'g')
: new RegExp(`\\b(${keys.join('|')})\\b`, 'g');

return {
name: 'replace',

transform(code, id) {
renderChunk(code, chunk) {
const id = chunk.fileName;
if (!keys.length) return null;
if (!filter(id)) return null;
return executeReplacement(code, id);
},

const magicString = new MagicString(code);

let hasReplacements = false;
let match;
let start;
let end;
let replacement;

// eslint-disable-next-line no-cond-assign
while ((match = pattern.exec(code))) {
hasReplacements = true;
transform(code, id) {
if (!keys.length) return null;
if (!filter(id)) return null;
return executeReplacement(code, id);
}
};

start = match.index;
end = start + match[0].length;
replacement = String(functionValues[match[1]](id));
function executeReplacement(code, id) {
const magicString = new MagicString(code);
if (!codeHasReplacements(code, id, magicString)) {
return null;
}

magicString.overwrite(start, end, replacement);
}
const result = { code: magicString.toString() };
if (isSourceMapEnabled()) {
result.map = magicString.generateMap({ hires: true });
}
return result;
}

if (!hasReplacements) return null;
function codeHasReplacements(code, id, magicString) {
let result = false;
let match;

const result = { code: magicString.toString() };
if (options.sourceMap !== false && options.sourcemap !== false)
result.map = magicString.generateMap({ hires: true });
// eslint-disable-next-line no-cond-assign
while ((match = pattern.exec(code))) {
result = true;

return result;
const start = match.index;
const end = start + match[0].length;
const replacement = String(functionValues[match[1]](id));
magicString.overwrite(start, end, replacement);
}
};
return result;
}

function isSourceMapEnabled() {
return options.sourceMap !== false && options.sourcemap !== false;
}
}
74 changes: 15 additions & 59 deletions packages/replace/test/misc.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-disable consistent-return, global-require, import/no-dynamic-require */
/* eslint-disable consistent-return */

const test = require('ava');
const { rollup } = require('rollup');
const { SourceMapConsumer } = require('source-map');
const { getLocator } = require('locate-character');

const replace = require('../dist/rollup-plugin-replace.cjs.js');

Expand Down Expand Up @@ -33,78 +31,36 @@ test('does not mutate the values map properties', async (t) => {
t.deepEqual(valuesMap, { ANSWER: '42' });
});

test('generates sourcemaps', async (t) => {
test('can be configured with output plugins', async (t) => {
const bundle = await rollup({
input: 'main.js',
onwarn(warning) {
throw new Error(warning.message);
},
plugins: [
replace({ values: { ANSWER: '42' } }),
{
resolveId(id) {
return id;
},
load(importee) {
if (importee === 'main.js') {
return 'import value from "other.js";\nlog(value);';
}
if (importee === 'other.js') {
return 'export default ANSWER;';
return 'log("environment", process.env.NODE_ENV);';
}
}
}
]
});

const { code, map } = getOutputFromGenerated(
await bundle.generate({ format: 'es', sourcemap: true })
await bundle.generate({
format: 'es',
sourcemap: true,
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
delimiters: ['', '']
})
]
})
);

await SourceMapConsumer.with(map, null, async (smc) => {
const locator = getLocator(code, { offsetLine: 1 });

let generatedLoc = locator('42');
let loc = smc.originalPositionFor(generatedLoc);
t.snapshot(generatedLoc);
t.snapshot(loc);

generatedLoc = locator('log');
loc = smc.originalPositionFor(generatedLoc);
t.snapshot(generatedLoc);
t.snapshot(loc);
});
});

test('does not generate sourcemaps if disabled', async (t) => {
let warned = false;

const bundle = await rollup({
input: 'main.js',
onwarn(warning) {
t.snapshot(warning.message);
warned = true;
},
plugins: [
replace({ values: { ANSWER: '42' }, sourcemap: false }),
{
resolveId(id) {
return id;
},

load(importee) {
if (importee === 'main.js') {
return 'import value from "other.js";\nlog(value);';
}
if (importee === 'other.js') {
return 'export default ANSWER;';
}
}
}
]
});

t.truthy(!warned);
await bundle.generate({ format: 'es', sourcemap: true });
t.truthy(warned);
t.is(code.trim(), 'log("environment", "production");');
t.truthy(map);
});
185 changes: 185 additions & 0 deletions packages/replace/test/snapshots/sourcemaps.js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Snapshot report for `test/sourcemaps.js`

The actual snapshot is saved in `sourcemaps.js.snap`.

Generated by [AVA](https://ava.li).

## generates sourcemaps when sourcemap is used as an output argument

> Snapshot 1
{
character: 12,
column: 12,
line: 1,
}

> Snapshot 2
{
column: 15,
line: 1,
name: null,
source: 'other.js',
}

> Snapshot 3
{
character: 17,
column: 0,
line: 3,
}

> Snapshot 4
{
column: 0,
line: 2,
name: null,
source: 'main.js',
}

## generates sourcemaps if enabled in plugin

> Snapshot 1
{
character: 12,
column: 12,
line: 1,
}

> Snapshot 2
{
column: 15,
line: 1,
name: null,
source: 'other.js',
}

> Snapshot 3
{
character: 17,
column: 0,
line: 3,
}

> Snapshot 4
{
column: 0,
line: 2,
name: null,
source: 'main.js',
}

## generates sourcemaps if enabled in plugin (camelcase)

> Snapshot 1
{
character: 12,
column: 12,
line: 1,
}

> Snapshot 2
{
column: 15,
line: 1,
name: null,
source: 'other.js',
}

> Snapshot 3
{
character: 17,
column: 0,
line: 3,
}

> Snapshot 4
{
column: 0,
line: 2,
name: null,
source: 'main.js',
}

## generates sourcemaps by dfault

> Snapshot 1
{
character: 12,
column: 12,
line: 1,
}

> Snapshot 2
{
column: 15,
line: 1,
name: null,
source: 'other.js',
}

> Snapshot 3
{
character: 17,
column: 0,
line: 3,
}

> Snapshot 4
{
column: 0,
line: 2,
name: null,
source: 'main.js',
}

## generates sourcemaps by default

> Snapshot 1
{
character: 12,
column: 12,
line: 1,
}

> Snapshot 2
{
column: 15,
line: 1,
name: null,
source: 'other.js',
}

> Snapshot 3
{
character: 17,
column: 0,
line: 3,
}

> Snapshot 4
{
column: 0,
line: 2,
name: null,
source: 'main.js',
}
Binary file added packages/replace/test/snapshots/sourcemaps.js.snap
Binary file not shown.
Loading

0 comments on commit 0af323a

Please sign in to comment.