diff --git a/README.md b/README.md index bd6c7def..9b6515ad 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,12 @@ interface Context { sourceMap: any /** Resource path */ id: string + /** Files to watch */ + dependencies: Set + /** Emit a waring */ + warn: PluginContext.warn + /** https://rollupjs.org/guide/en#plugin-context */ + plugin: PluginContext } interface Payload { diff --git a/src/index.js b/src/index.js index af1102f3..3d46eae7 100644 --- a/src/index.js +++ b/src/index.js @@ -66,12 +66,24 @@ export default (options = {}) => { options.onImport(id) } - const res = await loaders.process({ - code, - map: undefined, + const loaderContext = { id, - sourceMap - }) + sourceMap, + dependencies: new Set(), + warn: this.warn.bind(this), + plugin: this + } + const res = await loaders.process( + { + code, + map: undefined + }, + loaderContext + ) + + for (const dep of loaderContext.dependencies) { + this.addWatchFile(dep) + } if (postcssLoaderOptions.extract) { extracted.set(id, res.extracted) diff --git a/src/less-loader.js b/src/less-loader.js index 98b9d38b..e9b721b6 100644 --- a/src/less-loader.js +++ b/src/less-loader.js @@ -8,12 +8,16 @@ export default { async process({ code }) { const less = importCwd('less') - let { css, map } = await pify(less.render.bind(less))(code, { + let { css, map, imports } = await pify(less.render.bind(less))(code, { ...this.options, sourceMap: this.sourceMap && {}, filename: this.id }) + for (const dep of imports) { + this.dependencies.add(dep) + } + if (map) { map = JSON.parse(map) map.sources = map.sources.map(source => humanlizePath(source)) diff --git a/src/loaders.js b/src/loaders.js index 309933e3..ee9e0c22 100644 --- a/src/loaders.js +++ b/src/loaders.js @@ -57,22 +57,44 @@ export default class Loaders { }) } - process({ code, map, id, sourceMap }) { - return series(this.use.slice().reverse().map(([name, options]) => { - const loader = this.getLoader(name) - const loaderContext = { - options: options || {}, - id, - sourceMap - } - return v => { - if (loader.alwaysProcess || matchFile(id, loader.test)) { - return loader.process.call(loaderContext, v) - } - // Otherwise directly return input value - return v - } - }), { code, map }) + /** + * Process the resource with loaders in serial + * @param {object} resource + * @param {string} resource.code + * @param {any} resource.map + * @param {object} context + * @param {string} context.id The absolute path to resource + * @param {boolean | 'inline'} context.sourceMap + * @param {Set} context.dependencies A set of dependencies to watch + * @returns {{code: string, map?: any}} + */ + process({ code, map }, context) { + return series( + this.use + .slice() + .reverse() + .map(([name, options]) => { + const loader = this.getLoader(name) + const loaderContext = Object.assign( + { + options: options || {} + }, + context + ) + + return v => { + if ( + loader.alwaysProcess || + matchFile(loaderContext.id, loader.test) + ) { + return loader.process.call(loaderContext, v) + } + // Otherwise directly return input value + return v + } + }), + { code, map } + ) } getLoader(name) { diff --git a/src/postcss-loader.js b/src/postcss-loader.js index bae6ad24..9d95c4b3 100644 --- a/src/postcss-loader.js +++ b/src/postcss-loader.js @@ -85,7 +85,10 @@ export default { ...options.modules, getJSON(filepath, json, outpath) { modulesExported[filepath] = json - if (typeof options.modules === 'object' && typeof options.modules.getJSON === 'function') { + if ( + typeof options.modules === 'object' && + typeof options.modules.getJSON === 'function' + ) { return options.modules.getJSON(filepath, json, outpath) } } @@ -121,6 +124,17 @@ export default { } const res = await postcss(plugins).process(code, postcssOpts) + + for (const msg of res.messages) { + if (msg.type === 'dependency') { + this.dependencies.add(msg.file) + } + } + + for (const warning of res.warnings()) { + this.warn(warning) + } + const outputMap = res.map && JSON.parse(res.map.toString()) if (outputMap && outputMap.sources) { outputMap.sources = outputMap.sources.map(v => normalizePath(v)) @@ -142,7 +156,7 @@ export default { // But skip this when namedExports is a function // Since a user like you can manually log that if you want if (name !== newName && typeof options.namedExports !== 'function') { - console.warn( + this.warn( `Exported "${name}" as "${newName}" in ${humanlizePath(this.id)}` ) } diff --git a/src/sass-loader.js b/src/sass-loader.js index d0d6cdef..9cc077c4 100644 --- a/src/sass-loader.js +++ b/src/sass-loader.js @@ -59,7 +59,10 @@ export default { resolvePromise(partialUrl, options) .then(finishImport) .catch(err => { - if (err.code === 'MODULE_NOT_FOUND' || err.code === 'ENOENT') { + if ( + err.code === 'MODULE_NOT_FOUND' || + err.code === 'ENOENT' + ) { resolvePromise(moduleUrl, options) .then(finishImport) .catch(next) @@ -69,12 +72,17 @@ export default { }) } ].concat(this.options.importer || []) - }).then(res => - resolve({ - code: res.css.toString(), - map: res.map && res.map.toString() + }) + .then(res => { + for (const file of res.stats.includedFiles) { + this.dependencies.add(file) + } + resolve({ + code: res.css.toString(), + map: res.map && res.map.toString() + }) }) - ).catch(reject) + .catch(reject) ) }) } diff --git a/src/stylus-loader.js b/src/stylus-loader.js index 439ca2fa..001a5979 100644 --- a/src/stylus-loader.js +++ b/src/stylus-loader.js @@ -14,6 +14,10 @@ export default { }) const css = await pify(style.render.bind(style))() + const deps = style.deps() + for (const dep of deps) { + this.dependencies.add(dep) + } return { code: css,