diff --git a/src/index.js b/src/index.js index 37a9e5ab..06d30585 100644 --- a/src/index.js +++ b/src/index.js @@ -24,7 +24,7 @@ import { getModuleCode, getModulesPlugins, normalizeSourceMap, - sortImports, + sort, } from './utils'; export default async function loader(content, map, meta) { @@ -48,12 +48,18 @@ export default async function loader(content, map, meta) { return; } + const replacements = []; + const exports = []; + const needUseModulesPlugins = shouldUseModulesPlugins(options); if (needUseModulesPlugins) { plugins.push(...getModulesPlugins(options, this)); } + const importPluginImports = []; + const importPluginApi = []; + if (shouldUseImportPlugin(options)) { const resolver = this.getResolve({ mainFields: ['css', 'style', 'main', '...'], @@ -65,6 +71,8 @@ export default async function loader(content, map, meta) { plugins.push( importParser({ + imports: importPluginImports, + api: importPluginApi, context: this.context, rootContext: this.rootContext, filter: getFilter(options.import, this.resourcePath), @@ -78,6 +86,8 @@ export default async function loader(content, map, meta) { ); } + const urlPluginImports = []; + if (shouldUseURLPlugin(options)) { const urlResolver = this.getResolve({ mainFields: ['asset'], @@ -87,6 +97,8 @@ export default async function loader(content, map, meta) { plugins.push( urlParser({ + imports: urlPluginImports, + replacements, context: this.context, rootContext: this.rootContext, filter: getFilter(options.url, this.resourcePath), @@ -96,6 +108,9 @@ export default async function loader(content, map, meta) { ); } + const icssPluginImports = []; + const icssPluginApi = []; + if (needUseModulesPlugins) { const icssResolver = this.getResolve({ mainFields: ['css', 'style', 'main', '...'], @@ -106,6 +121,10 @@ export default async function loader(content, map, meta) { plugins.push( icssParser({ + imports: icssPluginImports, + api: icssPluginApi, + replacements, + exports, context: this.context, rootContext: this.rootContext, resolver: icssResolver, @@ -163,34 +182,23 @@ export default async function loader(content, map, meta) { this.emitWarning(new Warning(warning)); } - const imports = []; - const apiImports = []; - const replacements = []; - const exports = []; - - for (const message of result.messages) { - // eslint-disable-next-line default-case - switch (message.type) { - case 'import': - imports.push(message.value); - break; - case 'api-import': - apiImports.push(message.value); - break; - case 'replacement': - replacements.push(message.value); - break; - case 'export': - exports.push(message.value); - break; - } + const imports = [] + .concat(icssPluginImports.sort(sort)) + .concat(importPluginImports.sort(sort)) + .concat(urlPluginImports.sort(sort)); + const api = [] + .concat(importPluginApi.sort(sort)) + .concat(icssPluginApi.sort(sort)); + + if (options.modules.exportOnlyLocals !== true) { + imports.unshift({ + importName: '___CSS_LOADER_API_IMPORT___', + url: stringifyRequest(this, require.resolve('./runtime/api')), + }); } - imports.sort(sortImports); - apiImports.sort(sortImports); - - const importCode = getImportCode(this, imports, options); - const moduleCode = getModuleCode(result, apiImports, replacements, options); + const importCode = getImportCode(imports, options); + const moduleCode = getModuleCode(result, api, replacements, options); const exportCode = getExportCode(exports, replacements, options); callback(null, `${importCode}${moduleCode}${exportCode}`); diff --git a/src/plugins/postcss-icss-parser.js b/src/plugins/postcss-icss-parser.js index 6ff4b5d5..939c78a9 100644 --- a/src/plugins/postcss-icss-parser.js +++ b/src/plugins/postcss-icss-parser.js @@ -5,7 +5,7 @@ import { normalizeUrl, resolveRequests, requestify } from '../utils'; export default postcss.plugin( 'postcss-icss-parser', - (options) => async (css, result) => { + (options) => async (css) => { const importReplacements = Object.create(null); const { icssImports, icssExports } = extractICSS(css); const imports = new Map(); @@ -58,28 +58,14 @@ export default postcss.plugin( importName = `___CSS_LOADER_ICSS_IMPORT_${imports.size}___`; imports.set(importKey, importName); - result.messages.push( - { - type: 'import', - value: { - importName, - url: options.urlHandler(newUrl), - icss: true, - order: 0, - index, - }, - }, - { - type: 'api-import', - value: { - type: 'internal', - importName, - dedupe: true, - order: 0, - index, - }, - } - ); + options.imports.push({ + importName, + url: options.urlHandler(newUrl), + icss: true, + index, + }); + + options.api.push({ importName, dedupe: true, index }); } for (const [replacementIndex, token] of Object.keys(tokens).entries()) { @@ -88,10 +74,7 @@ export default postcss.plugin( importReplacements[token] = replacementName; - result.messages.push({ - type: 'replacement', - value: { type: 'icss', replacementName, importName, localName }, - }); + options.replacements.push({ replacementName, importName, localName }); } } @@ -102,7 +85,7 @@ export default postcss.plugin( for (const name of Object.keys(icssExports)) { const value = replaceValueSymbols(icssExports[name], importReplacements); - result.messages.push({ type: 'export', value: { name, value } }); + options.exports.push({ name, value }); } } ); diff --git a/src/plugins/postcss-import-parser.js b/src/plugins/postcss-import-parser.js index 5218150f..9fc64ce3 100644 --- a/src/plugins/postcss-import-parser.js +++ b/src/plugins/postcss-import-parser.js @@ -179,31 +179,20 @@ export default postcss.plugin(pluginName, (options) => async (css, result) => { importName = `___CSS_LOADER_AT_RULE_IMPORT_${imports.size}___`; imports.set(importKey, importName); - result.messages.push({ - type: 'import', - value: { - order: 1, - importName, - url: options.urlHandler(newUrl), - index, - }, + options.imports.push({ + importName, + url: options.urlHandler(newUrl), + index, }); } - result.messages.push({ - type: 'api-import', - value: { order: 1, type: 'internal', importName, media, index }, - }); + options.api.push({ importName, media, index }); // eslint-disable-next-line no-continue continue; } - result.messages.push({ - pluginName, - type: 'api-import', - value: { order: 1, type: 'external', url, media, index }, - }); + options.api.push({ url, media, index }); } return Promise.resolve(); diff --git a/src/plugins/postcss-url-parser.js b/src/plugins/postcss-url-parser.js index 7bd5e19c..2a5ee694 100644 --- a/src/plugins/postcss-url-parser.js +++ b/src/plugins/postcss-url-parser.js @@ -61,11 +61,7 @@ function walkCss(css, result, options, callback) { }; if (shouldHandleRule(rule, decl, result)) { - accumulator.push({ - decl, - rule, - parsed, - }); + accumulator.push({ decl, rule, parsed }); } // Do not traverse inside `url` @@ -91,11 +87,7 @@ function walkCss(css, result, options, callback) { }; if (shouldHandleRule(rule, decl, result)) { - accumulator.push({ - decl, - rule, - parsed, - }); + accumulator.push({ decl, rule, parsed }); } } else if (type === 'string') { const rule = { @@ -106,11 +98,7 @@ function walkCss(css, result, options, callback) { }; if (shouldHandleRule(rule, decl, result)) { - accumulator.push({ - decl, - rule, - parsed, - }); + accumulator.push({ decl, rule, parsed }); } } } @@ -166,15 +154,10 @@ export default postcss.plugin(pluginName, (options) => async (css, result) => { } if (!hasUrlImportHelper) { - result.messages.push({ - pluginName, - type: 'import', - value: { - order: 2, - importName: '___CSS_LOADER_GET_URL_IMPORT___', - url: options.urlHandler(require.resolve('../runtime/getUrl.js')), - index: 1, - }, + options.imports.push({ + importName: '___CSS_LOADER_GET_URL_IMPORT___', + url: options.urlHandler(require.resolve('../runtime/getUrl.js')), + index: -1, }); hasUrlImportHelper = true; @@ -216,10 +199,10 @@ export default postcss.plugin(pluginName, (options) => async (css, result) => { importName = `___CSS_LOADER_URL_IMPORT_${imports.size}___`; imports.set(importKey, importName); - result.messages.push({ - pluginName, - type: 'import', - value: { importName, url: options.urlHandler(newUrl), index, order: 3 }, + options.imports.push({ + importName, + url: options.urlHandler(newUrl), + index, }); } @@ -231,10 +214,11 @@ export default postcss.plugin(pluginName, (options) => async (css, result) => { replacementName = `___CSS_LOADER_URL_REPLACEMENT_${replacements.size}___`; replacements.set(replacementKey, replacementName); - result.messages.push({ - pluginName, - type: 'replacement', - value: { type: 'url', replacementName, importName, hash, needQuotes }, + options.replacements.push({ + replacementName, + importName, + hash, + needQuotes, }); } diff --git a/src/utils.js b/src/utils.js index e5b75cda..9effda01 100644 --- a/src/utils.js +++ b/src/utils.js @@ -4,12 +4,7 @@ */ import path from 'path'; -import { - stringifyRequest, - urlToRequest, - interpolateName, - isUrlRequest, -} from 'loader-utils'; +import { urlToRequest, interpolateName, isUrlRequest } from 'loader-utils'; import normalizePath from 'normalize-path'; import cssesc from 'cssesc'; import modulesValues from 'postcss-modules-values'; @@ -327,20 +322,9 @@ function getPreRequester({ loaders, loaderIndex }) { }; } -function getImportCode(loaderContext, imports, options) { +function getImportCode(imports, options) { let code = ''; - if (options.modules.exportOnlyLocals !== true) { - const apiUrl = stringifyRequest( - loaderContext, - require.resolve('./runtime/api') - ); - - code += options.esModule - ? `import ___CSS_LOADER_API_IMPORT___ from ${apiUrl};\n` - : `var ___CSS_LOADER_API_IMPORT___ = require(${apiUrl});\n`; - } - for (const item of imports) { const { importName, url, icss } = item; @@ -354,7 +338,7 @@ function getImportCode(loaderContext, imports, options) { return code ? `// Imports\n${code}` : ''; } -function getModuleCode(result, apiImports, replacements, options) { +function getModuleCode(result, api, replacements, options) { if (options.modules.exportOnlyLocals === true) { return 'var ___CSS_LOADER_EXPORT___ = {};\n'; } @@ -364,25 +348,31 @@ function getModuleCode(result, apiImports, replacements, options) { let code = JSON.stringify(css); let beforeCode = `var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(${options.sourceMap});\n`; - for (const item of apiImports) { - const { type, media, dedupe } = item; - - beforeCode += - type === 'internal' - ? `___CSS_LOADER_EXPORT___.i(${item.importName}${ - media ? `, ${JSON.stringify(media)}` : dedupe ? ', ""' : '' - }${dedupe ? ', true' : ''});\n` - : `___CSS_LOADER_EXPORT___.push([module.id, ${JSON.stringify( - `@import url(${item.url});` - )}${media ? `, ${JSON.stringify(media)}` : ''}]);\n`; - } + for (const item of api) { + const { url, media, dedupe } = item; - for (const replacement of replacements) { - const { replacementName, importName, type } = replacement; + beforeCode += url + ? `___CSS_LOADER_EXPORT___.push([module.id, ${JSON.stringify( + `@import url(${url});` + )}${media ? `, ${JSON.stringify(media)}` : ''}]);\n` + : `___CSS_LOADER_EXPORT___.i(${item.importName}${ + media ? `, ${JSON.stringify(media)}` : dedupe ? ', ""' : '' + }${dedupe ? ', true' : ''});\n`; + } - if (type === 'url') { - const { hash, needQuotes } = replacement; + for (const item of replacements) { + const { replacementName, importName, localName } = item; + if (localName) { + code = code.replace(new RegExp(replacementName, 'g'), () => + options.modules.namedExport + ? `" + ${importName}_NAMED___[${JSON.stringify( + camelCase(localName) + )}] + "` + : `" + ${importName}.locals[${JSON.stringify(localName)}] + "` + ); + } else { + const { hash, needQuotes } = item; const getUrlOptions = [] .concat(hash ? [`hash: ${JSON.stringify(hash)}`] : []) .concat(needQuotes ? 'needQuotes: true' : []); @@ -390,21 +380,10 @@ function getModuleCode(result, apiImports, replacements, options) { getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(', ')} }` : ''; beforeCode += `var ${replacementName} = ___CSS_LOADER_GET_URL_IMPORT___(${importName}${preparedOptions});\n`; - code = code.replace( new RegExp(replacementName, 'g'), () => `" + ${replacementName} + "` ); - } else { - const { localName } = replacement; - - code = code.replace(new RegExp(replacementName, 'g'), () => - options.modules.namedExport - ? `" + ${importName}_NAMED___[${JSON.stringify( - camelCase(localName) - )}] + "` - : `" + ${importName}.locals[${JSON.stringify(localName)}] + "` - ); } } @@ -472,16 +451,11 @@ function getExportCode(exports, replacements, options) { } } - for (const replacement of replacements) { - const { replacementName, type } = replacement; + for (const item of replacements) { + const { replacementName, localName } = item; - if (type === 'url') { - localsCode = localsCode.replace( - new RegExp(replacementName, 'g'), - () => `" + ${replacementName} + "` - ); - } else { - const { importName, localName } = replacement; + if (localName) { + const { importName } = item; localsCode = localsCode.replace(new RegExp(replacementName, 'g'), () => options.modules.namedExport @@ -490,6 +464,11 @@ function getExportCode(exports, replacements, options) { )}] + "` : `" + ${importName}.locals[${JSON.stringify(localName)}] + "` ); + } else { + localsCode = localsCode.replace( + new RegExp(replacementName, 'g'), + () => `" + ${replacementName} + "` + ); } } @@ -545,11 +524,8 @@ function isUrlRequestable(url) { return isUrlRequest(url); } -function sortImports(a, b) { - return ( - (b.order < a.order) - (a.order < b.order) || - (b.index < a.index) - (a.index < b.index) - ); +function sort(a, b) { + return a.index - b.index; } export { @@ -569,5 +545,5 @@ export { getExportCode, resolveRequests, isUrlRequestable, - sortImports, + sort, }; diff --git a/test/__snapshots__/modules-option.test.js.snap b/test/__snapshots__/modules-option.test.js.snap index f061ac8b..c0ed06df 100644 --- a/test/__snapshots__/modules-option.test.js.snap +++ b/test/__snapshots__/modules-option.test.js.snap @@ -1187,6 +1187,7 @@ import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../../src/index.js??[id import ___CSS_LOADER_GET_URL_IMPORT___ from \\"../../../../src/runtime/getUrl.js\\"; import ___CSS_LOADER_URL_IMPORT_0___ from \\"../../url/img.png\\"; var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false); +___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___, \\"(min-width: 100px)\\"); ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true); ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_1___, \\"\\", true); ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_2___, \\"\\", true); @@ -1195,7 +1196,6 @@ ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_4___, \\"\\", true); ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_5___, \\"\\", true); ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_6___, \\"\\", true); ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_7___, \\"\\", true); -___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___, \\"(min-width: 100px)\\"); var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___); // Module ___CSS_LOADER_EXPORT___.push([module.id, \\"._2ZmR2b3YBVn0i8sme-abcC {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"v-def\\"] + \\";\\\\n}\\\\n\\\\n._3dxIylSbTBEe450DFBxy5D {\\\\n color: blue;\\\\n}\\\\n\\\\n.EcQSwQce4PuQ5vNAybT9N {\\\\n display: block;\\\\n}\\\\n\\\\n.hTH4alr_d-S0jPncN6ib3 {\\\\n width: \\" + ___CSS_LOADER_ICSS_IMPORT_1___.locals[\\"v-something\\"] + \\";\\\\n}\\\\n\\\\n._7sobwviowI6_CZkzLjYZG {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"v-other\\"] + \\";\\\\n}\\\\n\\\\n.YpDepip9R1BGGAy-rGgvc {\\\\n prop: \\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"v-def\\"] + \\";\\\\n duplicate: \\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"v-other\\"] + \\";\\\\n}\\\\n\\\\n._3dfrN27nghAjb3tcT6R_Ov {\\\\n color: red;\\\\n}\\\\n\\\\n._3aPunKIij5oyAtcB6y9-Xm {\\\\n color: yellow;\\\\n}\\\\n\\\\n._3Qp0o615k38gm2l4OVRknw {\\\\n color: gray;\\\\n}\\\\n\\\\n._2Zsff12VKF2NbAGVE1sdzC {\\\\n color: gray;\\\\n}\\\\n\\\\n._3itMfHbLQSSkBisENyA8TF {\\\\n color: gainsboro;\\\\n}\\\\n\\\\n._2ChGydqcGYRLzAo3_Iomr2 {\\\\n color: gainsboro;\\\\n}\\\\n\\\\n._1ai7yu9kkZ_8JwK0EMbe6U {\\\\n color: #BF4040;\\\\n}\\\\n\\\\n.OX01CBO1Ma7xJh6yAybXq {\\\\n color: black;\\\\n}\\\\n\\\\n@media (min-width: 960px) {\\\\n ._2Yk-wvfy8t_ESEwwB1Fc0y {\\\\n padding: 0 20px;\\\\n }\\\\n}\\\\n\\\\n.\\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"s-white\\"] + \\" {\\\\n color: white;\\\\n}\\\\n\\\\n@media \\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"m-small\\"] + \\" {\\\\n ._2Yk-wvfy8t_ESEwwB1Fc0y {\\\\n padding: 20px 20px;\\\\n }\\\\n}\\\\n@value v-comment: /* comment */;\\\\n\\\\n._2PhbElc8FsODw7KMuxWJyk {\\\\n v-ident: validIdent;\\\\n v-pre-defined-ident: left;\\\\n v-string: 'content';\\\\n v-string-1: '';\\\\n v-url: url(https://www.exammple.com/images/my-background.png);\\\\n v-url-1: url('https://www.exammple.com/images/my-background.png');\\\\n v-url-2: url(\\\\\\"https://www.exammple.com/images/my-background.png\\\\\\");\\\\n v-integer: 100;\\\\n v-integer-1: -100;\\\\n v-integer-2: +100;\\\\n v-number: .60;\\\\n v-number-1: -456.8;\\\\n v-number-2: -3.4e-2;\\\\n v-dimension: 12px;\\\\n v-percentage: 100%;\\\\n v-hex: #fff;\\\\n v-comment: v-comment 10px v-comment;\\\\n v-function: rgb(0,0,0);\\\\n v-unicode-range: U+0025-00FF;\\\\n mutliple: #fff .60 100%;\\\\n}\\\\n\\\\n\\\\na {\\\\n content: 'content';\\\\n}\\\\n\\\\n@supports (content: 'content') {\\\\n a {\\\\n content: 'content';\\\\n }\\\\n}\\\\n\\\\n[class~='content'] {\\\\n color:green;\\\\n}\\\\n\\\\n._1qvhWcgsRpzv9-_jaooxI0 {\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\\\n._1-QX-dLNLF1zFn-cPfLHcH {\\\\n background: red;\\\\n}\\\\n\\", \\"\\"]); @@ -1254,6 +1254,14 @@ export default ___CSS_LOADER_EXPORT___; exports[`"modules" option should support resolving in composes: result 1`] = ` Array [ + Array [ + "../../src/index.js?[ident]!./modules/composes/test-other.css", + "._24axXNO_oC23T0D0YAz-0Y { + d: d; +} +", + "(min-width: 100px)", + ], Array [ "../../src/index.js?[ident]!./modules/composes/values.css", " @@ -1314,14 +1322,6 @@ Array [ }", "", ], - Array [ - "../../src/index.js?[ident]!./modules/composes/test-other.css", - "._24axXNO_oC23T0D0YAz-0Y { - d: d; -} -", - "(min-width: 100px)", - ], Array [ "./modules/composes/composes.css", "._2ZmR2b3YBVn0i8sme-abcC {