Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"plugins": [
"transform-es2015-modules-commonjs"
"presets": [
[
"env",
{
"node": 4
}
]
]
}
80 changes: 80 additions & 0 deletions __test__/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* eslint-env jest */
import path from 'path'
import fs from 'fs'
import del from 'del'
import webpack from 'webpack'
import { SourceMapConsumer } from 'source-map'
import ButternutWebpackPlugin from '../index'

const buildDir = path.join(__dirname, 'build')

describe('butternut-webpack-plugin', () => {
afterEach(() =>
del(buildDir)
)

describe('sourcemaps', () => {
beforeEach(() =>
run({
devtool: 'sourcemap',
})
)

test('should have sourcemaps with correct filenames', async () => {
const src = await sources()
const srcWithouthWebpackPrefix = src.map(removeWebpackPrefix)
expect(srcWithouthWebpackPrefix).toContain('__test__/resources/a.js')
expect(srcWithouthWebpackPrefix).toContain('__test__/resources/b.js')
expect(srcWithouthWebpackPrefix).toContain('__test__/resources/app.js')
})
})
})

function run (webpackConfigOpts, butternutOpts) {
const compiler = webpack(getConfig(webpackConfigOpts, butternutOpts))
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
if (err) {
return reject(err)
}
return resolve(stats)
})
})
}

async function sources () {
const fileContent = await getFile('bundle.js.map')
const map = JSON.parse(fileContent)
const smc = new SourceMapConsumer(map)
return smc.sources
}

function getFile (file) {
return new Promise((resolve, reject) => {
fs.readFile(path.join(buildDir, file), 'utf-8', (error, data) => {
if (error) {
return reject(error)
}

return resolve(data)
})
})
}

function getConfig (webpackConfigOpts = {}, butternutOpts = {}) {
return {
entry: path.join(__dirname, 'resources/app.js'),
output: {
filename: 'bundle.js',
path: buildDir,
},
plugins: [
new ButternutWebpackPlugin(butternutOpts),
],
devtool: webpackConfigOpts.devtool ? webpackConfigOpts.devtool : undefined,
}
}

function removeWebpackPrefix (s) {
return s.replace('webpack:///', '')
}
1 change: 1 addition & 0 deletions __test__/resources/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'a'
8 changes: 8 additions & 0 deletions __test__/resources/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import a from './a'
import b from './b'

run(a, b)

function run (a, b) {
console.log(a + b) // eslint-disable-line no-console
}
1 change: 1 addition & 0 deletions __test__/resources/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'b'
72 changes: 59 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import ModuleFilenameHelpers from 'webpack/lib/ModuleFilenameHelpers'
import { RawSource } from 'webpack-sources'
import { RawSource, SourceMapSource } from 'webpack-sources'
import { squash } from 'butternut'

const isEntryChunk = chunk => chunk.hasRuntime() && chunk.isInitial()

export default class ButternutPlugin {

constructor (conf = {}) {
Expand All @@ -12,24 +10,51 @@ export default class ButternutPlugin {

apply (compiler) {

const useSourceMap = typeof this.conf.sourceMap === 'undefined'
? !!compiler.options.devtool
: this.conf.sourceMap

compiler.plugin('compilation', compilation => {

if (useSourceMap) {
compilation.plugin('build-module', module => {
module.useSourceMap = true
})
}

compilation.plugin('optimize-chunk-assets', (chunks, callback) => {

for (const chunk of chunks) {
const files = chunk.files
const matchObjectOpts = { test: /\.js($|\?)/i }
const files = getFiles(chunks, compilation, matchObjectOpts)

for (const file of files) {
const matchObjectConfiguration = { test: /\.js($|\?)/i }
for (const file of files) {

if (ModuleFilenameHelpers.matchObject(matchObjectConfiguration, file)) {
const asset = compilation.assets[file]
const code = isEntryChunk(chunk) ? asset.source() : `__assumeDataProperty(global, "webpackJsonp", __abstract("function"))\n ${asset.source()}`
const transformed = squash(code, this.conf)
if (!ModuleFilenameHelpers.matchObject(matchObjectOpts, file)) {
return
}

compilation.assets[file] = new RawSource(transformed.code)
}
const asset = compilation.assets[file]

if (asset.__butternutfied) {
return
}

const { input, inputSourceMap } = getAssetParts(asset, useSourceMap)

// TODO: We need to find a way to pass `inputSourceMap` to squash(),
// so the final output source map will be based on `inputSourceMap`.
const transformed = squash(input, {
check: this.conf.check,
allowDangerousEval: this.conf.allowDangerousEval,
sourceMap: useSourceMap,
})

const source = transformed.map
? new SourceMapSource(transformed.code, file, transformed.map, input, inputSourceMap)
: new RawSource(transformed.code)

asset.__butternutfied = compilation.assets[file] = source

}

callback()
Expand All @@ -38,3 +63,24 @@ export default class ButternutPlugin {
})
}
}

function getFiles (chunks, compilation) {
const files = []
chunks.forEach(chunk => files.push(...chunk.files))
files.push(...compilation.additionalChunkAssets)

return files
}

function getAssetParts (asset, useSourceMap) {
if (useSourceMap && asset.sourceAndMap) {
const sourceAndMap = asset.sourceAndMap()
return { input: sourceAndMap.source, inputSourceMap: sourceAndMap.map }
}

if (useSourceMap) {
return { input: asset.source(), inputSourceMap: asset.map() }
}

return { input: asset.source() }
}
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dist"
],
"scripts": {
"test": "jest",
"build": "rm -rf dist && babel index.js --out-dir dist",
"lint": "eslint index.js"
},
Expand All @@ -19,9 +20,12 @@
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-eslint": "^7.2.3",
"babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
"babel-jest": "^20.0.1",
"babel-preset-env": "^1.4.0",
"del": "^2.2.2",
"eslint": "^3.19.0",
"eslint-config-zavatta": "^4.3.0",
"jest": "^20.0.1",
"webpack": "^2.5.1"
},
"repository": {
Expand Down
Loading