Skip to content

Commit e05b44c

Browse files
feat(index): support posthtml.config.js && result.messages
1 parent 7bd5896 commit e05b44c

File tree

3 files changed

+187
-45
lines changed

3 files changed

+187
-45
lines changed

lib/Error.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class LoaderError extends Error {
2+
constructor(err) {
3+
super(err);
4+
5+
this.name = 'PostHTML Loader';
6+
this.message = `\n\n${err.message}\n`;
7+
8+
// TODO(michael-ciniawsky)
9+
// add 'SyntaxError', 'PluginError', 'PluginWarning'
10+
11+
Error.captureStackTrace(this, this.constructor);
12+
}
13+
}
14+
15+
module.exports = LoaderError;

lib/index.js

+150-45
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,163 @@
1+
'use strict'
2+
3+
const path = require('path')
4+
15
const loaderUtils = require('loader-utils')
6+
const validateOptions = require('schema-utils')
7+
8+
const schema = require('./options.json')
9+
210
const posthtml = require('posthtml')
11+
const posthtmlrc = require('posthtml-load-config')
312

4-
module.exports = function (source) {
5-
if (this.cacheable) this.cacheable()
13+
const parseOptions = require('./options')
614

7-
// configure options
8-
const qs = loaderUtils.parseQuery(this.query)
15+
const LoaderError = require('./Error')
16+
/**
17+
* PostHTML Loader
18+
*
19+
* @author Michael Ciniawsky <[email protected]> (@michael-ciniawsky)
20+
* @license MIT
21+
*
22+
* @version 1.0.0
23+
*
24+
* @requires loader-utils
25+
* @requires schema-utils
26+
*
27+
* @requires posthtml
28+
* @requires posthtml-load-config
29+
*
30+
* @method posthtml-loader
31+
*
32+
* @param {String} html HTML
33+
*
34+
* @return {String} html HTML
35+
*/
36+
module.exports = function loader (html, map, meta) {
37+
// Loader Options
38+
const options = loaderUtils.getOptions(this) || {}
39+
40+
validateOptions(schema, options, 'PostHTML Loader')
41+
42+
// Make the loader async
943
const cb = this.async()
10-
let config
11-
try {
12-
config = parseOptions.call(this, this.options.posthtml, qs)
13-
} catch (err) {
14-
return cb(err)
15-
}
16-
17-
// configure custom parser argument if necessary
18-
const processArgs = [source.toString()]
19-
if (config.parser) { processArgs.push({ parser: config.parser }) }
20-
21-
// run posthtml
22-
const ph = posthtml(config.plugins)
23-
ph.process.apply(ph, processArgs)
24-
.then((result) => cb(null, result.html), cb)
25-
}
44+
const file = this.resourcePath
2645

27-
function parseOptions (config = [], qs = {}) {
28-
const res = {}
46+
Promise.resolve().then(() => {
47+
const length = Object.keys(options)
48+
.filter((option) => {
49+
switch (option) {
50+
case 'ident':
51+
case 'config':
52+
return
53+
default:
54+
return option
55+
}
56+
})
57+
.length
2958

30-
// if we have a function, run it
31-
if (typeof config === 'function') { config = config.call(this, this) }
59+
if (length) {
60+
return parseOptions.call(this, options)
61+
}
3262

33-
// if it's not an object at this point, error
34-
if (typeof config !== 'object') {
35-
throw new Error('Configuration must return an array or object')
36-
}
63+
const rc = {
64+
path: path.dirname(file),
65+
ctx: {
66+
file: {
67+
extname: path.extname(file),
68+
dirname: path.dirname(file),
69+
basename: path.basename(file)
70+
},
71+
options: {}
72+
}
73+
}
3774

38-
// if we now have an array, that represents the plugins directly
39-
if (Array.isArray(config)) {
40-
res.plugins = config
41-
// if not, it's an object. if a plugin pack is being used, use it.
42-
// otherwise, use default plugins
43-
} else {
44-
res.plugins = qs.pack ? config[qs.pack] : config.plugins
45-
}
75+
if (options.config) {
76+
if (options.config.path) {
77+
rc.path = path.resolve(options.config.path)
78+
}
4679

47-
// load in the custom parser if there is one
48-
if (config.parser) { res.parser = config.parser }
80+
if (options.config.ctx) {
81+
rc.ctx.options = options.config.ctx
82+
}
83+
}
4984

50-
// try loading custom parser from query
51-
if (qs.parser) {
52-
res.parser = require(qs.parser)
53-
}
85+
return posthtmlrc(rc.ctx, rc.path, { argv: false })
86+
})
87+
.then((config) => {
88+
if (!config) config = {}
5489

55-
return res
56-
}
90+
if (config.file) this.addDependency(config.file)
91+
92+
if (config.options) {
93+
// Disable overriding `options.to` (`posthtml.config.js`)
94+
if (config.options.to) delete config.options.to
95+
// Disable overriding `options.from` (`posthtml.config.js`)
96+
if (config.options.from) delete config.options.from
97+
}
98+
99+
let plugins = config.plugins || []
100+
let options = Object.assign(
101+
{ from: file, to: file },
102+
config.options
103+
)
104+
105+
if (typeof options.parser === 'string') {
106+
options.parser = require(options.parser)()
107+
}
108+
109+
// TODO(michael-ciniawsky) enable if when custom renderer available
110+
// if (typeof options.render === 'string') {
111+
// options.render = require(options.render)()
112+
// }
113+
114+
return posthtml(plugins)
115+
.process(html, options)
116+
.then((result) => {
117+
if (result.messages) {
118+
result.messages.forEach((msg) => {
119+
switch (msg.type) {
120+
case 'error':
121+
this.emitError(msg.message)
57122

58-
module.exports.parseOptions = parseOptions
123+
break
124+
case 'warning':
125+
this.emitWarning(msg.message)
126+
127+
break
128+
case 'dependency':
129+
this.addDependency(msg.file)
130+
131+
break
132+
default:
133+
break
134+
}
135+
})
136+
}
137+
138+
html = result.html
139+
140+
if (this.loaderIndex === 0) {
141+
html = `export default \`${html}\``
142+
143+
cb(null, html)
144+
145+
return null
146+
}
147+
148+
if (!meta) meta = {}
149+
150+
meta.ast = { type: 'posthtml', root: result.tree }
151+
meta.messages = result.messages
152+
153+
cb(null, html, map, meta)
154+
155+
return null
156+
})
157+
})
158+
.catch((err) => {
159+
cb(new LoaderError(err))
160+
161+
return null
162+
})
163+
}

lib/options.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict'
2+
3+
module.exports = function parseOptions (params) {
4+
if (typeof params.plugins === 'function') {
5+
params.plugins = params.plugins.call(this, this)
6+
}
7+
8+
let plugins
9+
10+
if (typeof params.plugins === 'undefined') plugins = []
11+
else if (Array.isArray(params.plugins)) plugins = params.plugins
12+
else plugins = [ params.plugins ]
13+
14+
const options = {}
15+
16+
if (typeof params !== 'undefined') {
17+
options.parser = params.parser
18+
// options.render = params.render
19+
}
20+
21+
return Promise.resolve({ options: options, plugins: plugins })
22+
}

0 commit comments

Comments
 (0)