Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 56 additions & 38 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ const StaticPlugin = require('./plugins/static_plugin')
const FsPlugin = require('./plugins/fs_plugin')
const micromatch = require('micromatch')
const union = require('lodash.union')
const merge = require('lodash.merge')
const postcssImport = require('postcss-import')
const BrowserSyncPlugin = require('browser-sync-webpack-plugin')
const {accessSync} = require('fs')
const hygienist = require('hygienist-middleware')
const merge = require('lodash.merge')
const SpikeUtils = require('spike-util')

/**
Expand Down Expand Up @@ -55,7 +55,7 @@ module.exports = class Config {
stringifier: Joi.object(),
syntax: Joi.object()
}),
babel: Joi.object().default({}),
babel: Joi.object(),
cleanUrls: Joi.bool().default(true),
jade: Joi.object().default({}),
dumpDirs: Joi.array().default(['views', 'assets']),
Expand Down Expand Up @@ -99,7 +99,26 @@ module.exports = class Config {
// string or array), then push ['node_modules', '.git', outputDir] to make
// sure they're not watched
res.server.watchOptions.ignored = Array.prototype.concat(res.server.watchOptions.ignored)
res.server.watchOptions.ignored = union(res.server.watchOptions.ignored, ['node_modules', '.git', res.outputDir])
res.server.watchOptions.ignored = union(res.server.watchOptions.ignored, [
'node_modules',
'.git',
res.outputDir
])

// core ignores
res.ignore.unshift(
'node_modules/**', // node_modules folder
`${res.outputDir}/**`, // anything in the public folder
'.git/**', // any git content
'package.json', // the primary package.json file
'**/.DS_Store', // any dumb DS Store file
'app.js', // primary config
'app.*.js' // any environment config
)

// Loader excludes use absolute paths for some reason, so we add the context
// to the beginning of the path so users can input them as relative to root.
res.ignore = res.ignore.map((i) => path.join(res.root, i))

// Here we set up the matchers that will watch for newly added files to the
// project.
Expand Down Expand Up @@ -143,7 +162,23 @@ module.exports = class Config {
* @return {Class} returns self, but with the properties of a webpack config
*/
transformSpikeOptionsToWebpack (opts) {
this.entry = opts.entry
// `disallow` options would break spike if modified.
const disallow = ['output', 'resolveLoader', 'spike', 'plugins', 'context', 'jade', 'postcss']

// `noCopy` options are spike-specific and shouldn't be directly added to
// webpack's config
const noCopy = ['root', 'matchers', 'env', 'locals', 'server', 'cleanUrls', 'dumpDirs', 'ignore', 'vendor', 'outputDir']

// All options other than `disallow` or `noCopy` are added directly to
// webpack's config object
const filteredOpts = removeKeys(opts, disallow.concat(noCopy))
Object.assign(this, filteredOpts)

// `noCopy` options are added under the `spike` property
this.spike = { files: {} }
Object.assign(this.spike, filterKeys(opts, noCopy))

// Now we run some spike-specific config transforms
this.context = opts.root

this.output = {
Expand All @@ -159,22 +194,13 @@ module.exports = class Config {
]
}

// core ignores
opts.ignore.unshift(
'node_modules/**', // node_modules folder
`${opts.outputDir}/**`, // anything in the public folder
'.git/**', // any git content
'package.json', // the primary package.json file
'**/.DS_Store', // any dumb DS Store file
'app.js', // primary config
'app.*.js' // any environment config
)
// If the user has passed options, accept them, but the ones set above take
// priority if there's a conflict, as they are essential to spike.
if (opts.resolveLoader) {
this.resolveLoader = merge(opts.resolveLoader, this.resolveLoader)
}

// Loader excludes use absolute paths for some reason, so we add the context
// to the beginning of the path so users can input them as relative to root.
opts.ignore = opts.ignore.map((i) => path.join(this.context, i))
const reIgnores = opts.ignore.map(mmToRe)

const spikeLoaders = [
{
exclude: reIgnores,
Expand All @@ -195,7 +221,6 @@ module.exports = class Config {
}
]

this.module = opts.module
this.module.loaders = opts.module.loaders.concat(spikeLoaders)

this.postcss = function (wp) {
Expand All @@ -208,25 +233,8 @@ module.exports = class Config {
pretty: true
}, opts.jade)

this.modulesDirectories = opts.modulesDirectories

// for spike-specific shared config and utilities
this.spike = {
files: {},
env: opts.env,
locals: opts.locals,
server: opts.server,
dumpDirs: opts.dumpDirs,
ignore: opts.ignore,
vendor: opts.vendor,
matchers: opts.matchers
}

this.babel = opts.babel

const util = new SpikeUtils(this)

// TODO: revisit this before a stable launch
this.plugins = [new FsPlugin(util)]
.concat(opts.plugins)
.concat([
Expand All @@ -240,8 +248,6 @@ module.exports = class Config {
} })
])

if (this.resolve) this.resolve = opts.resolve

return this
}

Expand Down Expand Up @@ -279,3 +285,15 @@ function loadFile (f) {
function mmToRe (mm) {
return micromatch.makeRe(mm)
}

function removeKeys (obj, keys) {
const res = {}
for (const k in obj) { if (keys.indexOf(k) < 0) res[k] = obj[k] }
return res
}

function filterKeys (obj, keys) {
const res = {}
for (const k in obj) { if (keys.indexOf(k) > 0) res[k] = obj[k] }
return res
}
8 changes: 4 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Sprout = require('sprout')
const Joi = require('joi')
const {EventEmitter} = require('events')
const Config = require('./config')
const Errors = require('./errors')
const {Error, Warning} = require('./errors')

/**
* @class Spike
Expand Down Expand Up @@ -157,17 +157,17 @@ function npmInstall (opts) {
*/
function compileCallback (id, err, stats) {
if (err) {
return this.emit('error', new Errors.Error({ id: id, message: err }))
return this.emit('error', new Error({ id: id, message: err }))
}
// Webpack "soft errors" are classified as warnings in spike. An error is
// an error. If it doesn't break the build, it's a warning.
const jsonStats = stats.toJson()
if (jsonStats.errors.length) {
this.emit('warning', new Errors.Warning({ id: id, message: jsonStats.errors }))
this.emit('warning', new Warning({ id: id, message: jsonStats.errors }))
}
/* istanbul ignore next */
if (jsonStats.warnings.length) {
this.emit('warning', new Errors.Warning({ id: id, message: jsonStats.warnings }))
this.emit('warning', new Warning({ id: id, message: jsonStats.warnings }))
}

this.emit('compile', { id: id, stats: stats })
Expand Down
4 changes: 2 additions & 2 deletions test/_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ exports.compileFixture = function compileFixture (t, name, options = {}) {
const project = new Spike(Object.assign(options, { root: testPath }))
const publicPath = path.join(testPath, 'public')

return When.promise(function (resolve, reject) {
return When.promise((resolve, reject) => {
project.on('error', reject)
project.on('compile', function (res) { resolve({res, publicPath}) })
project.on('compile', (res) => { resolve({res, publicPath}) })

project.compile()
})
Expand Down
16 changes: 15 additions & 1 deletion test/app_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ test('API config overrides app.js config', (t) => {
})
})

test('Throws error for invalid app.js syntax', (t) => {
test('throws error for invalid app.js syntax', (t) => {
return t.throws(() => compileFixture(t, 'app_config_error'), /SyntaxError: Unexpected token :/)
})

test('adds custom options to the webpack config object', (t) => {
return compileFixture(t, 'app_config', { customOption: 'wow!' })
.then(({res}) => {
t.truthy(res.stats.compilation.options.customOption === 'wow!')
})
})

test('does not allow certain options to be configured', (t) => {
return compileFixture(t, 'app_config', { context: 'override!' })
.then(({res}) => {
t.truthy(res.stats.compilation.options.context !== 'override!')
})
})