Skip to content

Commit

Permalink
feat(index): support posthtml.config.js && result.messages
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-ciniawsky committed Dec 16, 2017
1 parent 7bd5896 commit e05b44c
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 45 deletions.
15 changes: 15 additions & 0 deletions lib/Error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class LoaderError extends Error {
constructor(err) {
super(err);

this.name = 'PostHTML Loader';
this.message = `\n\n${err.message}\n`;

// TODO(michael-ciniawsky)
// add 'SyntaxError', 'PluginError', 'PluginWarning'

Error.captureStackTrace(this, this.constructor);
}
}

module.exports = LoaderError;
195 changes: 150 additions & 45 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,163 @@
'use strict'

const path = require('path')

const loaderUtils = require('loader-utils')
const validateOptions = require('schema-utils')

const schema = require('./options.json')

const posthtml = require('posthtml')
const posthtmlrc = require('posthtml-load-config')

module.exports = function (source) {
if (this.cacheable) this.cacheable()
const parseOptions = require('./options')

// configure options
const qs = loaderUtils.parseQuery(this.query)
const LoaderError = require('./Error')
/**
* PostHTML Loader
*
* @author Michael Ciniawsky <michael.ciniawsky@gmail.com> (@michael-ciniawsky)
* @license MIT
*
* @version 1.0.0
*
* @requires loader-utils
* @requires schema-utils
*
* @requires posthtml
* @requires posthtml-load-config
*
* @method posthtml-loader
*
* @param {String} html HTML
*
* @return {String} html HTML
*/
module.exports = function loader (html, map, meta) {
// Loader Options
const options = loaderUtils.getOptions(this) || {}

validateOptions(schema, options, 'PostHTML Loader')

// Make the loader async
const cb = this.async()
let config
try {
config = parseOptions.call(this, this.options.posthtml, qs)
} catch (err) {
return cb(err)
}

// configure custom parser argument if necessary
const processArgs = [source.toString()]
if (config.parser) { processArgs.push({ parser: config.parser }) }

// run posthtml
const ph = posthtml(config.plugins)
ph.process.apply(ph, processArgs)
.then((result) => cb(null, result.html), cb)
}
const file = this.resourcePath

function parseOptions (config = [], qs = {}) {
const res = {}
Promise.resolve().then(() => {
const length = Object.keys(options)
.filter((option) => {
switch (option) {
case 'ident':
case 'config':
return
default:
return option
}
})
.length

// if we have a function, run it
if (typeof config === 'function') { config = config.call(this, this) }
if (length) {
return parseOptions.call(this, options)
}

// if it's not an object at this point, error
if (typeof config !== 'object') {
throw new Error('Configuration must return an array or object')
}
const rc = {
path: path.dirname(file),
ctx: {
file: {
extname: path.extname(file),
dirname: path.dirname(file),
basename: path.basename(file)
},
options: {}
}
}

// if we now have an array, that represents the plugins directly
if (Array.isArray(config)) {
res.plugins = config
// if not, it's an object. if a plugin pack is being used, use it.
// otherwise, use default plugins
} else {
res.plugins = qs.pack ? config[qs.pack] : config.plugins
}
if (options.config) {
if (options.config.path) {
rc.path = path.resolve(options.config.path)
}

// load in the custom parser if there is one
if (config.parser) { res.parser = config.parser }
if (options.config.ctx) {
rc.ctx.options = options.config.ctx
}
}

// try loading custom parser from query
if (qs.parser) {
res.parser = require(qs.parser)
}
return posthtmlrc(rc.ctx, rc.path, { argv: false })
})
.then((config) => {
if (!config) config = {}

return res
}
if (config.file) this.addDependency(config.file)

if (config.options) {
// Disable overriding `options.to` (`posthtml.config.js`)
if (config.options.to) delete config.options.to
// Disable overriding `options.from` (`posthtml.config.js`)
if (config.options.from) delete config.options.from
}

let plugins = config.plugins || []
let options = Object.assign(
{ from: file, to: file },
config.options
)

if (typeof options.parser === 'string') {
options.parser = require(options.parser)()
}

// TODO(michael-ciniawsky) enable if when custom renderer available
// if (typeof options.render === 'string') {
// options.render = require(options.render)()
// }

return posthtml(plugins)
.process(html, options)
.then((result) => {
if (result.messages) {
result.messages.forEach((msg) => {
switch (msg.type) {
case 'error':
this.emitError(msg.message)

module.exports.parseOptions = parseOptions
break
case 'warning':
this.emitWarning(msg.message)

break
case 'dependency':
this.addDependency(msg.file)

break
default:
break
}
})
}

html = result.html

if (this.loaderIndex === 0) {
html = `export default \`${html}\``

cb(null, html)

return null
}

if (!meta) meta = {}

meta.ast = { type: 'posthtml', root: result.tree }
meta.messages = result.messages

cb(null, html, map, meta)

return null
})
})
.catch((err) => {
cb(new LoaderError(err))

return null
})
}
22 changes: 22 additions & 0 deletions lib/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

module.exports = function parseOptions (params) {
if (typeof params.plugins === 'function') {
params.plugins = params.plugins.call(this, this)
}

let plugins

if (typeof params.plugins === 'undefined') plugins = []
else if (Array.isArray(params.plugins)) plugins = params.plugins
else plugins = [ params.plugins ]

const options = {}

if (typeof params !== 'undefined') {
options.parser = params.parser
// options.render = params.render
}

return Promise.resolve({ options: options, plugins: plugins })
}

0 comments on commit e05b44c

Please sign in to comment.