Skip to content

Commit

Permalink
feat: use template literal when it possible to prevent `Maximum call …
Browse files Browse the repository at this point in the history
…stack size exceeded` (#1525)
  • Loading branch information
alexander-akait committed May 27, 2023
1 parent 0a2a596 commit 6eb5661
Show file tree
Hide file tree
Showing 13 changed files with 1,286 additions and 134 deletions.
3 changes: 2 additions & 1 deletion .cspell.json
Expand Up @@ -36,7 +36,8 @@
"Brotli",
"Contex",
"vspace",
"commitlint"
"commitlint",
"eslintcache"
],

"ignorePaths": [
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -14,6 +14,7 @@ npm-debug.log*
/test/fixtures/modules/composes/composes-absolute.css
/test/fixtures/import/import-file-protocol.css
/test/fixtures/url/url-file-protocol.css
/test/fixtures/url/many-urls.css

.DS_Store
Thumbs.db
Expand Down
29 changes: 27 additions & 2 deletions src/index.js
Expand Up @@ -227,12 +227,36 @@ export default async function loader(content, map, meta) {
}
}

let isTemplateLiteralSupported = false;

if (
// eslint-disable-next-line no-underscore-dangle
this._compilation &&
// eslint-disable-next-line no-underscore-dangle
this._compilation.options &&
// eslint-disable-next-line no-underscore-dangle
this._compilation.options.output &&
// eslint-disable-next-line no-underscore-dangle
this._compilation.options.output.environment &&
// eslint-disable-next-line no-underscore-dangle
this._compilation.options.output.environment.templateLiteral
) {
isTemplateLiteralSupported = true;
}

const importCode = getImportCode(imports, options);

let moduleCode;

try {
moduleCode = getModuleCode(result, api, replacements, options, this);
moduleCode = getModuleCode(
result,
api,
replacements,
options,
isTemplateLiteralSupported,
this
);
} catch (error) {
callback(error);

Expand All @@ -243,7 +267,8 @@ export default async function loader(content, map, meta) {
exports,
replacements,
needToUseIcssPlugin,
options
options,
isTemplateLiteralSupported
);

callback(null, `${importCode}${moduleCode}${exportCode}`);
Expand Down
114 changes: 89 additions & 25 deletions src/utils.js
Expand Up @@ -1018,7 +1018,14 @@ function printParams(media, dedupe, supports, layer) {
return result;
}

function getModuleCode(result, api, replacements, options, loaderContext) {
function getModuleCode(
result,
api,
replacements,
options,
isTemplateLiteralSupported,
loaderContext
) {
if (options.modules.exportOnlyLocals === true) {
return "";
}
Expand All @@ -1034,7 +1041,9 @@ function getModuleCode(result, api, replacements, options, loaderContext) {
)}`;
}

let code = JSON.stringify(result.css);
let code = isTemplateLiteralSupported
? convertToTemplateLiteral(result.css)
: JSON.stringify(result.css);

let beforeCode = `var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(${
options.sourceMap
Expand Down Expand Up @@ -1067,12 +1076,21 @@ function getModuleCode(result, api, replacements, options, loaderContext) {
if (localName) {
code = code.replace(new RegExp(replacementName, "g"), () =>
options.modules.namedExport
? `" + ${importName}_NAMED___[${JSON.stringify(
getValidLocalName(
localName,
options.modules.exportLocalsConvention
)
)}] + "`
? isTemplateLiteralSupported
? `\${ ${importName}_NAMED___[${JSON.stringify(
getValidLocalName(
localName,
options.modules.exportLocalsConvention
)
)}] }`
: `" + ${importName}_NAMED___[${JSON.stringify(
getValidLocalName(
localName,
options.modules.exportLocalsConvention
)
)}] + "`
: isTemplateLiteralSupported
? `\${${importName}.locals[${JSON.stringify(localName)}]}`
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
);
} else {
Expand All @@ -1084,9 +1102,10 @@ function getModuleCode(result, api, replacements, options, loaderContext) {
getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(", ")} }` : "";

beforeCode += `var ${replacementName} = ___CSS_LOADER_GET_URL_IMPORT___(${importName}${preparedOptions});\n`;
code = code.replace(
new RegExp(replacementName, "g"),
() => `" + ${replacementName} + "`
code = code.replace(new RegExp(replacementName, "g"), () =>
isTemplateLiteralSupported
? `\${${replacementName}}`
: `" + ${replacementName} + "`
);
}
}
Expand All @@ -1101,13 +1120,38 @@ function getModuleCode(result, api, replacements, options, loaderContext) {
return `${beforeCode}// Module\n___CSS_LOADER_EXPORT___.push([module.id, ${code}, ""${sourceMapValue}]);\n`;
}

const SLASH = "\\".charCodeAt(0);
const BACKTICK = "`".charCodeAt(0);
const DOLLAR = "$".charCodeAt(0);

function convertToTemplateLiteral(str) {
let escapedString = "";

for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);

escapedString +=
code === SLASH || code === BACKTICK || code === DOLLAR
? `\\${str[i]}`
: str[i];
}

return `\`${escapedString}\``;
}

function dashesCamelCase(str) {
return str.replace(/-+(\w)/g, (match, firstLetter) =>
firstLetter.toUpperCase()
);
}

function getExportCode(exports, replacements, icssPluginUsed, options) {
function getExportCode(
exports,
replacements,
icssPluginUsed,
options,
isTemplateLiteralSupported
) {
let code = "// Exports\n";

if (icssPluginUsed) {
Expand All @@ -1120,13 +1164,21 @@ function getExportCode(exports, replacements, icssPluginUsed, options) {

for (const name of normalizedNames) {
if (options.modules.namedExport) {
localsCode += `export var ${name} = ${JSON.stringify(value)};\n`;
localsCode += `export var ${name} = ${
isTemplateLiteralSupported
? convertToTemplateLiteral(value)
: JSON.stringify(value)
};\n`;
} else {
if (localsCode) {
localsCode += `,\n`;
}

localsCode += `\t${JSON.stringify(name)}: ${JSON.stringify(value)}`;
localsCode += `\t${JSON.stringify(name)}: ${
isTemplateLiteralSupported
? convertToTemplateLiteral(value)
: JSON.stringify(value)
}`;
}
}
};
Expand All @@ -1148,23 +1200,35 @@ function getExportCode(exports, replacements, icssPluginUsed, options) {
new RegExp(replacementName, "g"),
() => {
if (options.modules.namedExport) {
return `" + ${importName}_NAMED___[${JSON.stringify(
getValidLocalName(
localName,
options.modules.exportLocalsConvention
)
)}] + "`;
return isTemplateLiteralSupported
? `\${${importName}_NAMED___[${JSON.stringify(
getValidLocalName(
localName,
options.modules.exportLocalsConvention
)
)}]}`
: `" + ${importName}_NAMED___[${JSON.stringify(
getValidLocalName(
localName,
options.modules.exportLocalsConvention
)
)}] + "`;
} else if (options.modules.exportOnlyLocals) {
return `" + ${importName}[${JSON.stringify(localName)}] + "`;
return isTemplateLiteralSupported
? `\${${importName}[${JSON.stringify(localName)}]}`
: `" + ${importName}[${JSON.stringify(localName)}] + "`;
}

return `" + ${importName}.locals[${JSON.stringify(localName)}] + "`;
return isTemplateLiteralSupported
? `\${${importName}.locals[${JSON.stringify(localName)}]}`
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`;
}
);
} else {
localsCode = localsCode.replace(
new RegExp(replacementName, "g"),
() => `" + ${replacementName} + "`
localsCode = localsCode.replace(new RegExp(replacementName, "g"), () =>
isTemplateLiteralSupported
? `\${${replacementName}}`
: `" + ${replacementName} + "`
);
}
}
Expand Down

0 comments on commit 6eb5661

Please sign in to comment.