Skip to content

Commit

Permalink
fix: reduce count of require (#1004)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Dec 5, 2019
1 parent e662b61 commit 80e9662
Show file tree
Hide file tree
Showing 12 changed files with 862 additions and 653 deletions.
30 changes: 15 additions & 15 deletions src/index.js
Expand Up @@ -13,7 +13,6 @@ import { importParser, icssParser, urlParser } from './plugins';
import {
normalizeSourceMap,
getModulesPlugins,
getImportPrefix,
getFilter,
getApiCode,
getImportCode,
Expand Down Expand Up @@ -54,20 +53,19 @@ export default function loader(content, map, meta) {
plugins.push(...getModulesPlugins(options, this));
}

// Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
const importPrefix = getImportPrefix(this, options.importLoaders);
const exportType = options.onlyLocals ? 'locals' : 'full';

plugins.push(icssParser());

if (options.import !== false) {
if (options.import !== false && exportType === 'full') {
plugins.push(
importParser({
filter: getFilter(options.import, this.resourcePath),
})
);
}

if (options.url !== false) {
if (options.url !== false && exportType === 'full') {
plugins.push(
urlParser({
filter: getFilter(options.url, this.resourcePath, (value) =>
Expand Down Expand Up @@ -109,22 +107,24 @@ export default function loader(content, map, meta) {
}
}

const isNormalMode = !options.onlyLocals;

const apiCode = isNormalMode ? getApiCode(this, sourceMap) : '';
// Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
const apiCode = exportType === 'full' ? getApiCode(this, sourceMap) : '';
const importCode =
isNormalMode && imports.length > 0
? getImportCode(this, imports, { importPrefix })
imports.length > 0
? getImportCode(this, imports, {
importLoaders: options.importLoaders,
exportType,
})
: '';
const moduleCode =
exportType === 'full'
? getModuleCode(this, result, replacers, sourceMap)
: '';
const moduleCode = isNormalMode
? getModuleCode(this, result, replacers, { sourceMap, importPrefix })
: '';
const exportCode =
exports.length > 0
? getExportCode(this, exports, replacers, {
importPrefix,
localsConvention: options.localsConvention,
onlyLocals: options.onlyLocals,
exportType,
})
: '';

Expand Down
60 changes: 23 additions & 37 deletions src/plugins/postcss-icss-parser.js
@@ -1,63 +1,49 @@
import postcss from 'postcss';
import { extractICSS, replaceValueSymbols, replaceSymbols } from 'icss-utils';
import loaderUtils from 'loader-utils';

const pluginName = 'postcss-icss-parser';

function hasImportMessage(messages, url) {
return messages.find(
(message) =>
message.pluginName === pluginName &&
message.type === 'import' &&
message.value &&
message.value.url === url &&
message.value.media === ''
);
}

export default postcss.plugin(
pluginName,
() =>
function process(css, result) {
const importReplacements = Object.create(null);
const { icssImports, icssExports } = extractICSS(css);

let index = 0;
Object.keys(icssImports).forEach((url, importIndex) => {
const tokens = Object.keys(icssImports[url]);

if (tokens.length === 0) {
return;
}

for (const importUrl of Object.keys(icssImports)) {
const url = loaderUtils.parseString(importUrl);
const importName = `___CSS_LOADER_ICSS_IMPORT_${importIndex}___`;

for (const token of Object.keys(icssImports[importUrl])) {
const name = `___CSS_LOADER_IMPORT___${index}___`;
result.messages.push({
pluginName,
type: 'import',
value: { type: 'icss-import', name: importName, url },
});

tokens.forEach((token, replacementIndex) => {
const name = `___CSS_LOADER_ICSS_IMPORT_${importIndex}_REPLACEMENT_${replacementIndex}___`;
const localName = icssImports[url][token];

index += 1;
importReplacements[token] = name;

result.messages.push({
pluginName,
type: 'replacer',
value: {
type: 'icss-import',
name,
url,
export: icssImports[importUrl][token],
},
value: { type: 'icss-import', name, importName, localName },
});
});
});

if (!hasImportMessage(result.messages, url)) {
result.messages.push({
pluginName,
type: 'import',
value: { type: 'icss-import', url, media: '', name },
});
}
}
if (Object.keys(importReplacements).length > 0) {
replaceSymbols(css, importReplacements);
}

replaceSymbols(css, importReplacements);

for (const exportName of Object.keys(icssExports)) {
const name = exportName;
Object.keys(icssExports).forEach((name) => {
const value = replaceValueSymbols(
icssExports[name],
importReplacements
Expand All @@ -68,6 +54,6 @@ export default postcss.plugin(
type: 'export',
value: { name, value },
});
}
});
}
);
44 changes: 28 additions & 16 deletions src/plugins/postcss-import-parser.js
@@ -1,8 +1,6 @@
import postcss from 'postcss';
import valueParser from 'postcss-value-parser';

import { uniqWith } from '../utils';

const pluginName = 'postcss-import-parser';

function getArg(nodes) {
Expand Down Expand Up @@ -46,7 +44,7 @@ function parseImport(params) {
}

function walkAtRules(css, result, filter) {
const items = [];
const items = new Map();

css.walkAtRules(/^import$/i, (atRule) => {
// Convert only top-level @import
Expand Down Expand Up @@ -79,8 +77,13 @@ function walkAtRules(css, result, filter) {
atRule.remove();

const { url, media } = parsed;
const value = items.get(url);

items.push({ url, media });
if (!value) {
items.set(url, new Set([media]));
} else {
value.add(media);
}
});

return items;
Expand All @@ -90,18 +93,27 @@ export default postcss.plugin(
pluginName,
(options) =>
function process(css, result) {
const traversed = walkAtRules(css, result, options.filter);
const paths = uniqWith(
traversed,
(value, other) => value.url === other.url && value.media === other.media
);

paths.forEach((item) => {
result.messages.push({
pluginName,
type: 'import',
value: { type: '@import', url: item.url, media: item.media },
const items = walkAtRules(css, result, options.filter);

[...items]
.reduce((accumulator, currentValue) => {
const [url, medias] = currentValue;

medias.forEach((media) => {
accumulator.push({ url, media });
});

return accumulator;
}, [])
.forEach((item, index) => {
const { url, media } = item;
const name = `___CSS_LOADER_AT_RULE_IMPORT_${index}___`;

result.messages.push({
pluginName,
type: 'import',
value: { type: '@import', name, url, media },
});
});
});
}
);
94 changes: 50 additions & 44 deletions src/plugins/postcss-url-parser.js
@@ -1,8 +1,6 @@
import postcss from 'postcss';
import valueParser from 'postcss-value-parser';

import { uniqWith, flatten } from '../utils';

const pluginName = 'postcss-url-parser';

const isUrlFunc = /url/i;
Expand Down Expand Up @@ -51,7 +49,7 @@ function walkUrls(parsed, callback) {
});
}

function getUrlsFromValue(value, result, filter, decl = null) {
function getUrlsFromValue(value, result, filter, decl) {
if (!needParseDecl.test(value)) {
return;
}
Expand All @@ -61,14 +59,9 @@ function getUrlsFromValue(value, result, filter, decl = null) {

walkUrls(parsed, (node, url, needQuotes) => {
if (url.trim().replace(/\\[\r\n]/g, '').length === 0) {
result.warn(
`Unable to find uri in '${decl ? decl.toString() : value}'`,
decl
? {
node: decl,
}
: {}
);
result.warn(`Unable to find uri in '${decl ? decl.toString() : value}'`, {
node: decl,
});

return;
}
Expand All @@ -77,24 +70,26 @@ function getUrlsFromValue(value, result, filter, decl = null) {
return;
}

urls.push({ url, needQuotes });
const [normalizedUrl, singleQuery, hashValue] = url.split(/(\?)?#/);
const hash =
singleQuery || hashValue
? `${singleQuery ? '?' : ''}${hashValue ? `#${hashValue}` : ''}`
: '';

urls.push({ node, url: normalizedUrl, hash, needQuotes });
});

// eslint-disable-next-line consistent-return
return { parsed, urls };
}

function walkDeclsWithUrl(css, result, filter) {
function walkDecls(css, result, filter) {
const items = [];

css.walkDecls((decl) => {
const item = getUrlsFromValue(decl.value, result, filter, decl);

if (!item) {
return;
}

if (item.urls.length === 0) {
if (!item || item.urls.length === 0) {
return;
}

Expand All @@ -104,57 +99,68 @@ function walkDeclsWithUrl(css, result, filter) {
return items;
}

function flatten(array) {
return array.reduce((a, b) => a.concat(b), []);
}

function collectUniqueUrlsWithNodes(array) {
return array.reduce((accumulator, currentValue) => {
const { url, needQuotes, hash, node } = currentValue;
const found = accumulator.find(
(item) =>
url === item.url && needQuotes === item.needQuotes && hash === item.hash
);

if (!found) {
accumulator.push({ url, hash, needQuotes, nodes: [node] });
} else {
found.nodes.push(node);
}

return accumulator;
}, []);
}

export default postcss.plugin(
pluginName,
(options) =>
function process(css, result) {
const traversed = walkDeclsWithUrl(css, result, options.filter);
const paths = uniqWith(
flatten(traversed.map((item) => item.urls)),
(value, other) =>
value.url === other.url && value.needQuotes === other.needQuotes
const traversed = walkDecls(css, result, options.filter);
const paths = collectUniqueUrlsWithNodes(
flatten(traversed.map((item) => item.urls))
);

if (paths.length === 0) {
return;
}

const placeholders = [];
const replacers = new Map();

paths.forEach((path, index) => {
const name = `___CSS_LOADER_URL___${index}___`;
const { url, needQuotes } = path;

placeholders.push({ name, path });
const { url, hash, needQuotes, nodes } = path;
const name = `___CSS_LOADER_URL_IMPORT_${index}___`;

result.messages.push(
{
pluginName,
type: 'import',
value: { type: 'url', url, name, needQuotes },
value: { type: 'url', name, url, needQuotes, hash, index },
},
{
pluginName,
type: 'replacer',
value: { type: 'url', name },
}
);

nodes.forEach((node) => {
replacers.set(node, name);
});
});

traversed.forEach((item) => {
walkUrls(item.parsed, (node, url, needQuotes) => {
const value = placeholders.find(
(placeholder) =>
placeholder.path.url === url &&
placeholder.path.needQuotes === needQuotes
);

if (!value) {
walkUrls(item.parsed, (node) => {
const name = replacers.get(node);

if (!name) {
return;
}

const { name } = value;

// eslint-disable-next-line no-param-reassign
node.type = 'word';
// eslint-disable-next-line no-param-reassign
Expand Down

0 comments on commit 80e9662

Please sign in to comment.