From b5c93798e0b714a40ee3722ef0e5daa6c1c5ef41 Mon Sep 17 00:00:00 2001 From: Alexander Krasnoyarov Date: Thu, 8 Oct 2020 14:46:04 +0300 Subject: [PATCH] feat: postcss@8 (#1204) --- src/plugins/postcss-icss-parser.js | 158 +++++----- src/plugins/postcss-import-parser.js | 382 ++++++++++++------------- src/plugins/postcss-url-parser.js | 370 ++++++++++++------------ test/__snapshots__/loader.test.js.snap | 25 ++ test/loader.test.js | 3 +- 5 files changed, 490 insertions(+), 448 deletions(-) diff --git a/src/plugins/postcss-icss-parser.js b/src/plugins/postcss-icss-parser.js index 939c78a9..7e77d703 100644 --- a/src/plugins/postcss-icss-parser.js +++ b/src/plugins/postcss-icss-parser.js @@ -1,91 +1,99 @@ -import postcss from 'postcss'; import { extractICSS, replaceValueSymbols, replaceSymbols } from 'icss-utils'; import { normalizeUrl, resolveRequests, requestify } from '../utils'; -export default postcss.plugin( - 'postcss-icss-parser', - (options) => async (css) => { - const importReplacements = Object.create(null); - const { icssImports, icssExports } = extractICSS(css); - const imports = new Map(); - const tasks = []; - - // eslint-disable-next-line guard-for-in - for (const url in icssImports) { - const tokens = icssImports[url]; - - if (Object.keys(tokens).length === 0) { - // eslint-disable-next-line no-continue - continue; +const plugin = (options = {}) => { + return { + postcssPlugin: 'postcss-icss-parser', + async OnceExit(root) { + const importReplacements = Object.create(null); + const { icssImports, icssExports } = extractICSS(root); + const imports = new Map(); + const tasks = []; + + // eslint-disable-next-line guard-for-in + for (const url in icssImports) { + const tokens = icssImports[url]; + + if (Object.keys(tokens).length === 0) { + // eslint-disable-next-line no-continue + continue; + } + + let normalizedUrl = url; + let prefix = ''; + + const queryParts = normalizedUrl.split('!'); + + if (queryParts.length > 1) { + normalizedUrl = queryParts.pop(); + prefix = queryParts.join('!'); + } + + const request = requestify( + normalizeUrl(normalizedUrl, true), + options.rootContext + ); + const doResolve = async () => { + const { resolver, context } = options; + const resolvedUrl = await resolveRequests(resolver, context, [ + ...new Set([normalizedUrl, request]), + ]); + + return { url: resolvedUrl, prefix, tokens }; + }; + + tasks.push(doResolve()); } - let normalizedUrl = url; - let prefix = ''; + const results = await Promise.all(tasks); - const queryParts = normalizedUrl.split('!'); + for (let index = 0; index <= results.length - 1; index++) { + const { url, prefix, tokens } = results[index]; + const newUrl = prefix ? `${prefix}!${url}` : url; + const importKey = newUrl; + let importName = imports.get(importKey); - if (queryParts.length > 1) { - normalizedUrl = queryParts.pop(); - prefix = queryParts.join('!'); - } + if (!importName) { + importName = `___CSS_LOADER_ICSS_IMPORT_${imports.size}___`; + imports.set(importKey, importName); - const request = requestify( - normalizeUrl(normalizedUrl, true), - options.rootContext - ); - const doResolve = async () => { - const { resolver, context } = options; - const resolvedUrl = await resolveRequests(resolver, context, [ - ...new Set([normalizedUrl, request]), - ]); - - return { url: resolvedUrl, prefix, tokens }; - }; - - tasks.push(doResolve()); - } - - const results = await Promise.all(tasks); - - for (let index = 0; index <= results.length - 1; index++) { - const { url, prefix, tokens } = results[index]; - const newUrl = prefix ? `${prefix}!${url}` : url; - const importKey = newUrl; - let importName = imports.get(importKey); - - if (!importName) { - importName = `___CSS_LOADER_ICSS_IMPORT_${imports.size}___`; - imports.set(importKey, importName); - - options.imports.push({ - importName, - url: options.urlHandler(newUrl), - icss: true, - index, - }); - - options.api.push({ importName, dedupe: true, index }); - } + options.imports.push({ + importName, + url: options.urlHandler(newUrl), + icss: true, + index, + }); - for (const [replacementIndex, token] of Object.keys(tokens).entries()) { - const replacementName = `___CSS_LOADER_ICSS_IMPORT_${index}_REPLACEMENT_${replacementIndex}___`; - const localName = tokens[token]; + options.api.push({ importName, dedupe: true, index }); + } - importReplacements[token] = replacementName; + for (const [replacementIndex, token] of Object.keys(tokens).entries()) { + const replacementName = `___CSS_LOADER_ICSS_IMPORT_${index}_REPLACEMENT_${replacementIndex}___`; + const localName = tokens[token]; - options.replacements.push({ replacementName, importName, localName }); + importReplacements[token] = replacementName; + + options.replacements.push({ replacementName, importName, localName }); + } } - } - if (Object.keys(importReplacements).length > 0) { - replaceSymbols(css, importReplacements); - } + if (Object.keys(importReplacements).length > 0) { + replaceSymbols(root, importReplacements); + } + + for (const name of Object.keys(icssExports)) { + const value = replaceValueSymbols( + icssExports[name], + importReplacements + ); + + options.exports.push({ name, value }); + } + }, + }; +}; - for (const name of Object.keys(icssExports)) { - const value = replaceValueSymbols(icssExports[name], importReplacements); +plugin.postcss = true; - options.exports.push({ name, value }); - } - } -); +export default plugin; diff --git a/src/plugins/postcss-import-parser.js b/src/plugins/postcss-import-parser.js index 399a06a7..f67d8576 100644 --- a/src/plugins/postcss-import-parser.js +++ b/src/plugins/postcss-import-parser.js @@ -1,6 +1,3 @@ -import { promisify } from 'util'; - -import postcss from 'postcss'; import valueParser from 'postcss-value-parser'; import { @@ -10,191 +7,194 @@ import { requestify, } from '../utils'; -const pluginName = 'postcss-import-parser'; - -function walkAtRules(css, result, options, callback) { - const accumulator = []; - - css.walkAtRules(/^import$/i, (atRule) => { - // Convert only top-level @import - if (atRule.parent.type !== 'root') { - return; - } - - // Nodes do not exists - `@import url('http://') :root {}` - if (atRule.nodes) { - result.warn( - "It looks like you didn't end your @import statement correctly. Child nodes are attached to it.", - { node: atRule } - ); - - return; - } - - const { nodes: paramsNodes } = valueParser(atRule.params); - - // No nodes - `@import ;` - // Invalid type - `@import foo-bar;` - if ( - paramsNodes.length === 0 || - (paramsNodes[0].type !== 'string' && paramsNodes[0].type !== 'function') - ) { - result.warn(`Unable to find uri in "${atRule.toString()}"`, { - node: atRule, - }); - - return; - } - - let isStringValue; - let url; - - if (paramsNodes[0].type === 'string') { - isStringValue = true; - url = paramsNodes[0].value; - } else { - // Invalid function - `@import nourl(test.css);` - if (paramsNodes[0].value.toLowerCase() !== 'url') { - result.warn(`Unable to find uri in "${atRule.toString()}"`, { - node: atRule, - }); - - return; - } - - isStringValue = - paramsNodes[0].nodes.length !== 0 && - paramsNodes[0].nodes[0].type === 'string'; - url = isStringValue - ? paramsNodes[0].nodes[0].value - : valueParser.stringify(paramsNodes[0].nodes); - } - - // Empty url - `@import "";` or `@import url();` - if (url.trim().length === 0) { - result.warn(`Unable to find uri in "${atRule.toString()}"`, { - node: atRule, - }); - - return; - } - - accumulator.push({ - atRule, - url, - isStringValue, - mediaNodes: paramsNodes.slice(1), - }); - }); - - callback(null, accumulator); -} - -const asyncWalkAtRules = promisify(walkAtRules); - -export default postcss.plugin(pluginName, (options) => async (css, result) => { - const parsedResults = await asyncWalkAtRules(css, result, options); - - if (parsedResults.length === 0) { - return Promise.resolve(); - } - - const imports = new Map(); - const tasks = []; - - for (const parsedResult of parsedResults) { - const { atRule, url, isStringValue, mediaNodes } = parsedResult; - - let normalizedUrl = url; - let prefix = ''; - - const isRequestable = isUrlRequestable(normalizedUrl); - - if (isRequestable) { - const queryParts = normalizedUrl.split('!'); - - if (queryParts.length > 1) { - normalizedUrl = queryParts.pop(); - prefix = queryParts.join('!'); - } - - normalizedUrl = normalizeUrl(normalizedUrl, isStringValue); - - // Empty url after normalize - `@import '\ - // \ - // \ - // '; - if (normalizedUrl.trim().length === 0) { - result.warn(`Unable to find uri in "${atRule.toString()}"`, { - node: atRule, - }); - - // eslint-disable-next-line no-continue - continue; - } - } - - let media; - - if (mediaNodes.length > 0) { - media = valueParser.stringify(mediaNodes).trim().toLowerCase(); - } - - if (options.filter && !options.filter(normalizedUrl, media)) { - // eslint-disable-next-line no-continue - continue; - } - - atRule.remove(); - - if (isRequestable) { - const request = requestify(normalizedUrl, options.rootContext); - - tasks.push( - (async () => { - const { resolver, context } = options; - const resolvedUrl = await resolveRequests(resolver, context, [ - ...new Set([request, normalizedUrl]), - ]); - - return { url: resolvedUrl, media, prefix, isRequestable }; - })() - ); - } else { - tasks.push({ url, media, prefix, isRequestable }); - } - } - - const results = await Promise.all(tasks); - - for (let index = 0; index <= results.length - 1; index++) { - const { url, isRequestable, media } = results[index]; - - if (isRequestable) { - const { prefix } = results[index]; - const newUrl = prefix ? `${prefix}!${url}` : url; - const importKey = newUrl; - let importName = imports.get(importKey); - - if (!importName) { - importName = `___CSS_LOADER_AT_RULE_IMPORT_${imports.size}___`; - imports.set(importKey, importName); - - options.imports.push({ - importName, - url: options.urlHandler(newUrl), - index, - }); - } - - options.api.push({ importName, media, index }); - - // eslint-disable-next-line no-continue - continue; - } - - options.api.push({ url, media, index }); - } - - return Promise.resolve(); -}); +const plugin = (options = {}) => { + return { + postcssPlugin: 'postcss-import-parser', + prepare(result) { + const atRuleParsedResults = []; + + return { + AtRule: { + import(atRule) { + // Convert only top-level @import + if (atRule.parent.type !== 'root') { + return; + } + + // Nodes do not exists - `@import url('http://') :root {}` + if (atRule.nodes) { + result.warn( + "It looks like you didn't end your @import statement correctly. Child nodes are attached to it.", + { node: atRule } + ); + + return; + } + + const { nodes: paramsNodes } = valueParser(atRule.params); + + // No nodes - `@import ;` + // Invalid type - `@import foo-bar;` + if ( + paramsNodes.length === 0 || + (paramsNodes[0].type !== 'string' && + paramsNodes[0].type !== 'function') + ) { + result.warn(`Unable to find uri in "${atRule.toString()}"`, { + node: atRule, + }); + + return; + } + + let isStringValue; + let url; + + if (paramsNodes[0].type === 'string') { + isStringValue = true; + url = paramsNodes[0].value; + } else { + // Invalid function - `@import nourl(test.css);` + if (paramsNodes[0].value.toLowerCase() !== 'url') { + result.warn(`Unable to find uri in "${atRule.toString()}"`, { + node: atRule, + }); + + return; + } + + isStringValue = + paramsNodes[0].nodes.length !== 0 && + paramsNodes[0].nodes[0].type === 'string'; + url = isStringValue + ? paramsNodes[0].nodes[0].value + : valueParser.stringify(paramsNodes[0].nodes); + } + + // Empty url - `@import "";` or `@import url();` + if (url.trim().length === 0) { + result.warn(`Unable to find uri in "${atRule.toString()}"`, { + node: atRule, + }); + + return; + } + + atRuleParsedResults.push({ + atRule, + url, + isStringValue, + mediaNodes: paramsNodes.slice(1), + }); + }, + }, + async OnceExit() { + if (atRuleParsedResults.length === 0) { + return; + } + + const imports = new Map(); + const tasks = []; + + for (const parsedResult of atRuleParsedResults) { + const { atRule, url, isStringValue, mediaNodes } = parsedResult; + + let normalizedUrl = url; + let prefix = ''; + + const isRequestable = isUrlRequestable(normalizedUrl); + + if (isRequestable) { + const queryParts = normalizedUrl.split('!'); + + if (queryParts.length > 1) { + normalizedUrl = queryParts.pop(); + prefix = queryParts.join('!'); + } + + normalizedUrl = normalizeUrl(normalizedUrl, isStringValue); + + // Empty url after normalize - `@import '\ + // \ + // \ + // '; + if (normalizedUrl.trim().length === 0) { + result.warn(`Unable to find uri in "${atRule.toString()}"`, { + node: atRule, + }); + + // eslint-disable-next-line no-continue + continue; + } + } + + let media; + + if (mediaNodes.length > 0) { + media = valueParser.stringify(mediaNodes).trim().toLowerCase(); + } + + if (options.filter && !options.filter(normalizedUrl, media)) { + // eslint-disable-next-line no-continue + continue; + } + + atRule.remove(); + + if (isRequestable) { + const request = requestify(normalizedUrl, options.rootContext); + + tasks.push( + (async () => { + const { resolver, context } = options; + const resolvedUrl = await resolveRequests(resolver, context, [ + ...new Set([request, normalizedUrl]), + ]); + + return { url: resolvedUrl, media, prefix, isRequestable }; + })() + ); + } else { + tasks.push({ url, media, prefix, isRequestable }); + } + } + + const results = await Promise.all(tasks); + + for (let index = 0; index <= results.length - 1; index++) { + const { url, isRequestable, media } = results[index]; + + if (isRequestable) { + const { prefix } = results[index]; + const newUrl = prefix ? `${prefix}!${url}` : url; + const importKey = newUrl; + let importName = imports.get(importKey); + + if (!importName) { + importName = `___CSS_LOADER_AT_RULE_IMPORT_${imports.size}___`; + imports.set(importKey, importName); + + options.imports.push({ + importName, + url: options.urlHandler(newUrl), + index, + }); + } + + options.api.push({ importName, media, index }); + + // eslint-disable-next-line no-continue + continue; + } + + options.api.push({ url, media, index }); + } + }, + }; + }, + }; +}; + +plugin.postcss = true; + +export default plugin; diff --git a/src/plugins/postcss-url-parser.js b/src/plugins/postcss-url-parser.js index bb14afb3..7156ce73 100644 --- a/src/plugins/postcss-url-parser.js +++ b/src/plugins/postcss-url-parser.js @@ -1,6 +1,3 @@ -import { promisify } from 'util'; - -import postcss from 'postcss'; import valueParser from 'postcss-value-parser'; import { @@ -10,20 +7,20 @@ import { isUrlRequestable, } from '../utils'; -const pluginName = 'postcss-url-parser'; - const isUrlFunc = /url/i; const isImageSetFunc = /^(?:-webkit-)?image-set$/i; -const needParseDecl = /(?:url|(?:-webkit-)?image-set)\(/i; +const needParseDeclaration = /(?:url|(?:-webkit-)?image-set)\(/i; function getNodeFromUrlFunc(node) { return node.nodes && node.nodes[0]; } -function shouldHandleRule(rule, decl, result) { +function shouldHandleRule(rule, declaration, result) { // https://www.w3.org/TR/css-syntax-3/#typedef-url-token if (rule.url.replace(/^[\s]+|[\s]+$/g, '').length === 0) { - result.warn(`Unable to find uri in '${decl.toString()}'`, { node: decl }); + result.warn(`Unable to find uri in '${declaration.toString()}'`, { + node: declaration, + }); return false; } @@ -35,201 +32,214 @@ function shouldHandleRule(rule, decl, result) { return true; } -function walkCss(css, result, options, callback) { - const accumulator = []; - - css.walkDecls((decl) => { - if (!needParseDecl.test(decl.value)) { - return; - } - - const parsed = valueParser(decl.value); - - parsed.walk((node) => { - if (node.type !== 'function') { - return; - } - - if (isUrlFunc.test(node.value)) { - const { nodes } = node; - const isStringValue = nodes.length !== 0 && nodes[0].type === 'string'; - const url = isStringValue - ? nodes[0].value - : valueParser.stringify(nodes); - - const rule = { - node: getNodeFromUrlFunc(node), - url, - needQuotes: false, - isStringValue, - }; - - if (shouldHandleRule(rule, decl, result)) { - accumulator.push({ decl, rule, parsed }); - } - - // Do not traverse inside `url` - // eslint-disable-next-line consistent-return - return false; - } else if (isImageSetFunc.test(node.value)) { - for (const nNode of node.nodes) { - const { type, value } = nNode; - - if (type === 'function' && isUrlFunc.test(value)) { - const { nodes } = nNode; - const isStringValue = - nodes.length !== 0 && nodes[0].type === 'string'; - const url = isStringValue - ? nodes[0].value - : valueParser.stringify(nodes); - - const rule = { - node: getNodeFromUrlFunc(nNode), - url, - needQuotes: false, - isStringValue, - }; +const plugin = (options = {}) => { + return { + postcssPlugin: 'postcss-url-parser', + prepare(result) { + const declarationParsedResults = []; + + return { + async Declaration(declaration) { + if (!needParseDeclaration.test(declaration.value)) { + return; + } - if (shouldHandleRule(rule, decl, result)) { - accumulator.push({ decl, rule, parsed }); + const parsed = valueParser(declaration.value); + + parsed.walk((valueNode) => { + if (valueNode.type !== 'function') { + return; } - } else if (type === 'string') { - const rule = { - node: nNode, - url: value, - needQuotes: true, - isStringValue: true, - }; - - if (shouldHandleRule(rule, decl, result)) { - accumulator.push({ decl, rule, parsed }); + + if (isUrlFunc.test(valueNode.value)) { + const { nodes } = valueNode; + const isStringValue = + nodes.length !== 0 && nodes[0].type === 'string'; + const url = isStringValue + ? nodes[0].value + : valueParser.stringify(nodes); + + const rule = { + node: getNodeFromUrlFunc(valueNode), + url, + needQuotes: false, + isStringValue, + }; + + if (shouldHandleRule(rule, declaration, result)) { + declarationParsedResults.push({ declaration, rule, parsed }); + } + + // Do not traverse inside `url` + // eslint-disable-next-line consistent-return + return false; + } else if (isImageSetFunc.test(valueNode.value)) { + for (const nNode of valueNode.nodes) { + const { type, value } = nNode; + + if (type === 'function' && isUrlFunc.test(value)) { + const { nodes } = nNode; + const isStringValue = + nodes.length !== 0 && nodes[0].type === 'string'; + const url = isStringValue + ? nodes[0].value + : valueParser.stringify(nodes); + + const rule = { + node: getNodeFromUrlFunc(nNode), + url, + needQuotes: false, + isStringValue, + }; + + if (shouldHandleRule(rule, declaration, result)) { + declarationParsedResults.push({ + declaration, + rule, + parsed, + }); + } + } else if (type === 'string') { + const rule = { + node: nNode, + url: value, + needQuotes: true, + isStringValue: true, + }; + + if (shouldHandleRule(rule, declaration, result)) { + declarationParsedResults.push({ + declaration, + rule, + parsed, + }); + } + } + } + + // Do not traverse inside `image-set` + // eslint-disable-next-line consistent-return + return false; } + }); + }, + async OnceExit() { + if (declarationParsedResults.length === 0) { + return; } - } - // Do not traverse inside `image-set` - // eslint-disable-next-line consistent-return - return false; - } - }); - }); + const tasks = []; + const imports = new Map(); + const replacements = new Map(); - callback(null, accumulator); -} + let hasUrlImportHelper = false; -const asyncWalkCss = promisify(walkCss); + for (const parsedResult of declarationParsedResults) { + const { url, isStringValue } = parsedResult.rule; -export default postcss.plugin(pluginName, (options) => async (css, result) => { - const parsedResults = await asyncWalkCss(css, result, options); + let normalizedUrl = url; + let prefix = ''; - if (parsedResults.length === 0) { - return Promise.resolve(); - } + const queryParts = normalizedUrl.split('!'); - const tasks = []; - const imports = new Map(); - const replacements = new Map(); + if (queryParts.length > 1) { + normalizedUrl = queryParts.pop(); + prefix = queryParts.join('!'); + } - let hasUrlImportHelper = false; + normalizedUrl = normalizeUrl(normalizedUrl, isStringValue); - for (const parsedResult of parsedResults) { - const { url, isStringValue } = parsedResult.rule; + if (!options.filter(normalizedUrl)) { + // eslint-disable-next-line no-continue + continue; + } - let normalizedUrl = url; - let prefix = ''; + if (!hasUrlImportHelper) { + options.imports.push({ + importName: '___CSS_LOADER_GET_URL_IMPORT___', + url: options.urlHandler( + require.resolve('../runtime/getUrl.js') + ), + index: -1, + }); - const queryParts = normalizedUrl.split('!'); + hasUrlImportHelper = true; + } - if (queryParts.length > 1) { - normalizedUrl = queryParts.pop(); - prefix = queryParts.join('!'); - } + const splittedUrl = normalizedUrl.split(/(\?)?#/); + const [pathname, query, hashOrQuery] = splittedUrl; - normalizedUrl = normalizeUrl(normalizedUrl, isStringValue); + let hash = query ? '?' : ''; + hash += hashOrQuery ? `#${hashOrQuery}` : ''; - if (!options.filter(normalizedUrl)) { - // eslint-disable-next-line no-continue - continue; - } + const request = requestify(pathname, options.rootContext); - if (!hasUrlImportHelper) { - options.imports.push({ - importName: '___CSS_LOADER_GET_URL_IMPORT___', - url: options.urlHandler(require.resolve('../runtime/getUrl.js')), - index: -1, - }); + tasks.push( + (async () => { + const { resolver, context } = options; + const resolvedUrl = await resolveRequests(resolver, context, [ + ...new Set([request, normalizedUrl]), + ]); - hasUrlImportHelper = true; - } + return { url: resolvedUrl, prefix, hash, parsedResult }; + })() + ); + } - const splittedUrl = normalizedUrl.split(/(\?)?#/); - const [pathname, query, hashOrQuery] = splittedUrl; + const results = await Promise.all(tasks); - let hash = query ? '?' : ''; - hash += hashOrQuery ? `#${hashOrQuery}` : ''; + for (let index = 0; index <= results.length - 1; index++) { + const { + url, + prefix, + hash, + parsedResult: { declaration, rule, parsed }, + } = results[index]; + const newUrl = prefix ? `${prefix}!${url}` : url; + const importKey = newUrl; + let importName = imports.get(importKey); + + if (!importName) { + importName = `___CSS_LOADER_URL_IMPORT_${imports.size}___`; + imports.set(importKey, importName); + + options.imports.push({ + importName, + url: options.urlHandler(newUrl), + index, + }); + } - const request = requestify(pathname, options.rootContext); + const { needQuotes } = rule; + const replacementKey = JSON.stringify({ newUrl, hash, needQuotes }); + let replacementName = replacements.get(replacementKey); - tasks.push( - (async () => { - const { resolver, context } = options; - const resolvedUrl = await resolveRequests(resolver, context, [ - ...new Set([request, normalizedUrl]), - ]); + if (!replacementName) { + replacementName = `___CSS_LOADER_URL_REPLACEMENT_${replacements.size}___`; + replacements.set(replacementKey, replacementName); - return { url: resolvedUrl, prefix, hash, parsedResult }; - })() - ); - } + options.replacements.push({ + replacementName, + importName, + hash, + needQuotes, + }); + } - const results = await Promise.all(tasks); - - for (let index = 0; index <= results.length - 1; index++) { - const { - url, - prefix, - hash, - parsedResult: { decl, rule, parsed }, - } = results[index]; - const newUrl = prefix ? `${prefix}!${url}` : url; - const importKey = newUrl; - let importName = imports.get(importKey); - - if (!importName) { - importName = `___CSS_LOADER_URL_IMPORT_${imports.size}___`; - imports.set(importKey, importName); - - options.imports.push({ - importName, - url: options.urlHandler(newUrl), - index, - }); - } - - const { needQuotes } = rule; - const replacementKey = JSON.stringify({ newUrl, hash, needQuotes }); - let replacementName = replacements.get(replacementKey); - - if (!replacementName) { - replacementName = `___CSS_LOADER_URL_REPLACEMENT_${replacements.size}___`; - replacements.set(replacementKey, replacementName); - - options.replacements.push({ - replacementName, - importName, - hash, - needQuotes, - }); - } - - // eslint-disable-next-line no-param-reassign - rule.node.type = 'word'; - // eslint-disable-next-line no-param-reassign - rule.node.value = replacementName; - - // eslint-disable-next-line no-param-reassign - decl.value = parsed.toString(); - } + // eslint-disable-next-line no-param-reassign + rule.node.type = 'word'; + // eslint-disable-next-line no-param-reassign + rule.node.value = replacementName; + + // eslint-disable-next-line no-param-reassign + declaration.value = parsed.toString(); + } + }, + }; + }, + }; +}; + +plugin.postcss = true; - return Promise.resolve(); -}); +export default plugin; diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index 1fab6764..8db06c70 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -39,6 +39,31 @@ exports[`loader issue #1033: result 1`] = `Object {}`; exports[`loader issue #1033: warnings 1`] = `Array []`; +exports[`loader should not generate console.warn when plugins disabled and hideNothingWarning is "true": errors 1`] = `Array []`; + +exports[`loader should not generate console.warn when plugins disabled and hideNothingWarning is "true": module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../src/runtime/api.js\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]}); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\"\\", \\"\\"]); +// Exports +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`loader should not generate console.warn when plugins disabled and hideNothingWarning is "true": result 1`] = ` +Array [ + Array [ + "./empty.css", + "", + "", + ], +] +`; + +exports[`loader should not generate console.warn when plugins disabled and hideNothingWarning is "true": warnings 1`] = `Array []`; + exports[`loader should reuse \`ast\` from "postcss-loader": errors 1`] = `Array []`; exports[`loader should reuse \`ast\` from "postcss-loader": module 1`] = ` diff --git a/test/loader.test.js b/test/loader.test.js index 9eb33ee2..0f30422e 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -450,8 +450,7 @@ describe('loader', () => { expect(getErrors(stats)).toMatchSnapshot('errors'); }); - // TODO unskip after updating postcss to 8 version - it.skip('should not generate console.warn when plugins disabled and hideNothingWarning is "true"', async () => { + it('should not generate console.warn when plugins disabled and hideNothingWarning is "true"', async () => { jest.spyOn(console, 'warn').mockImplementation(() => {}); const compiler = getCompiler('./empty.js', {