Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

postcss-modules-scope

Package Overview
Dependencies
Maintainers
4
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

postcss-modules-scope - npm Package Compare versions

Comparing version 2.2.0 to 3.0.0-rc.0

16

CHANGELOG.md
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [3.0.0-rc.0] - 2020-09-21
### BREAKING CHANGE
- minimum supported `Node.js` version is `>= 10.13.0 || >= 12.13.0 || >= 14`
- minimum supported `postcss` version is `^8.0.3`
- `postcss` was moved to `peerDependencies`, you need to install `postcss` in your project before use the plugin
## [2.2.0] - 2020-03-19
- added the `exportGlobals` option to export global classes and ids
## [2.1.1] - 2019-03-05
### Fixed
- add additional space after the escape sequence (#17)
## [2.1.0] - 2019-03-05
### Fixed
- handles properly selector with escaping characters (like: `.\31 a2b3c { color: red }`)
### Feature
- `generateExportEntry` option (allow to setup key and value for `:export {}` rule)

40

package.json
{
"name": "postcss-modules-scope",
"version": "2.2.0",
"version": "3.0.0-rc.0",
"description": "A CSS Modules transform to extract export statements from local-scope classes",
"main": "src/index.js",
"engines": {
"node": ">= 6"
"node": ">= 10.13.0 || >= 12.13.0 || >= 14"
},
"scripts": {
"lint": "eslint src test",
"prettier": "prettier -l --ignore-path .gitignore . \"!test/test-cases\"",
"eslint": "eslint --ignore-path .gitignore .",
"lint": "yarn eslint && yarn prettier",
"pretest": "yarn lint",
"test": "mocha",
"autotest": "chokidar src test -c 'yarn test'",
"precover": "yarn lint",
"cover": "nyc mocha",
"travis": "yarn cover",
"prepublish": "yarn run test"
"test": "jest",
"autotest": "jest --watch",
"cover": "jest --coverage --collectCoverageFrom=\"src/**/*\"",
"ci": "yarn pretest && yarn cover",
"prepublishOnly": "yarn test"
},

@@ -37,20 +38,15 @@ "repository": {

"homepage": "https://github.com/css-modules/postcss-modules-scope",
"prettier": {
"semi": true,
"singleQuote": true,
"trailingComma": "es5"
},
"dependencies": {
"postcss": "^7.0.6",
"postcss-selector-parser": "^6.0.0"
"postcss-selector-parser": "^6.0.3"
},
"devDependencies": {
"cssesc": "^3.0.0",
"chokidar-cli": "^1.0.1",
"codecov.io": "^0.1.2",
"coveralls": "^3.0.2",
"eslint": "^5.9.0",
"mocha": "^6.0.2",
"nyc": "^14.1.0"
"eslint": "^7.9.0",
"jest": "^26.4.2",
"postcss": "^8.0.3",
"prettier": "^2.1.2"
},
"peerDependencies": {
"postcss": "^8.0.0"
}
}

@@ -27,4 +27,4 @@ # CSS Modules: Scope Locals & Extend

```js
import styles from './buttons.css'
elem.innerHTML = `<button class="${styles.continueButton}">Continue</button>`
import styles from "./buttons.css";
elem.innerHTML = `<button class="${styles.continueButton}">Continue</button>`;
```

@@ -83,4 +83,4 @@

* Lines: [![Coverage Status](https://coveralls.io/repos/css-modules/postcss-modules-scope/badge.svg?branch=master)](https://coveralls.io/r/css-modules/postcss-modules-scope?branch=master)
* Statements: [![codecov.io](http://codecov.io/github/css-modules/postcss-modules-scope/coverage.svg?branch=master)](http://codecov.io/github/css-modules/postcss-modules-scope?branch=master)
- Lines: [![Coverage Status](https://coveralls.io/repos/css-modules/postcss-modules-scope/badge.svg?branch=master)](https://coveralls.io/r/css-modules/postcss-modules-scope?branch=master)
- Statements: [![codecov.io](http://codecov.io/github/css-modules/postcss-modules-scope/coverage.svg?branch=master)](http://codecov.io/github/css-modules/postcss-modules-scope?branch=master)

@@ -102,2 +102,3 @@ ## Development

---
Glen Maddern, 2015.

@@ -1,5 +0,4 @@

'use strict';
"use strict";
const postcss = require('postcss');
const selectorParser = require('postcss-selector-parser');
const selectorParser = require("postcss-selector-parser");

@@ -9,4 +8,4 @@ const hasOwnProperty = Object.prototype.hasOwnProperty;

function getSingleLocalNamesForComposes(root) {
return root.nodes.map(node => {
if (node.type !== 'selector' || node.nodes.length !== 1) {
return root.nodes.map((node) => {
if (node.type !== "selector" || node.nodes.length !== 1) {
throw new Error(

@@ -20,4 +19,4 @@ `composition is only allowed when selector is single :local class name not in "${root}"`

if (
node.type !== 'pseudo' ||
node.value !== ':local' ||
node.type !== "pseudo" ||
node.value !== ":local" ||
node.nodes.length !== 1

@@ -36,3 +35,3 @@ ) {

if (node.type !== 'selector' || node.length !== 1) {
if (node.type !== "selector" || node.length !== 1) {
throw new Error(

@@ -49,3 +48,3 @@ 'composition is only allowed when selector is single :local class name not in "' +

if (node.type !== 'class') {
if (node.type !== "class") {
// 'id' is not possible, because you can't compose ids

@@ -65,6 +64,6 @@ throw new Error(

const whitespace = '[\\x20\\t\\r\\n\\f]';
const whitespace = "[\\x20\\t\\r\\n\\f]";
const unescapeRegExp = new RegExp(
'\\\\([\\da-f]{1,6}' + whitespace + '?|(' + whitespace + ')|.)',
'ig'
"\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)",
"ig"
);

@@ -74,3 +73,3 @@

return str.replace(unescapeRegExp, (_, escaped, escapedWhitespace) => {
const high = '0x' + escaped - 0x10000;
const high = "0x" + escaped - 0x10000;

@@ -82,230 +81,235 @@ // NaN means non-codepoint

: high < 0
? // BMP codepoint
String.fromCharCode(high + 0x10000)
: // Supplemental Plane codepoint (surrogate pair)
String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
? // BMP codepoint
String.fromCharCode(high + 0x10000)
: // Supplemental Plane codepoint (surrogate pair)
String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
});
}
const processor = postcss.plugin('postcss-modules-scope', function(options) {
return css => {
const generateScopedName =
(options && options.generateScopedName) || processor.generateScopedName;
const generateExportEntry =
(options && options.generateExportEntry) || processor.generateExportEntry;
const exportGlobals = options && options.exportGlobals;
const plugin = (options = {}) => {
const generateScopedName =
(options && options.generateScopedName) || plugin.generateScopedName;
const generateExportEntry =
(options && options.generateExportEntry) || plugin.generateExportEntry;
const exportGlobals = options && options.exportGlobals;
const exports = Object.create(null);
return {
postcssPlugin: "postcss-modules-scope",
RootExit(root, { rule }) {
const exports = Object.create(null);
function exportScopedName(name, rawName) {
const scopedName = generateScopedName(
rawName ? rawName : name,
css.source.input.from,
css.source.input.css
);
const exportEntry = generateExportEntry(
rawName ? rawName : name,
scopedName,
css.source.input.from,
css.source.input.css
);
const { key, value } = exportEntry;
function exportScopedName(name, rawName) {
const scopedName = generateScopedName(
rawName ? rawName : name,
root.source.input.from,
root.source.input.css
);
const exportEntry = generateExportEntry(
rawName ? rawName : name,
scopedName,
root.source.input.from,
root.source.input.css
);
const { key, value } = exportEntry;
exports[key] = exports[key] || [];
exports[key] = exports[key] || [];
if (exports[key].indexOf(value) < 0) {
exports[key].push(value);
if (exports[key].indexOf(value) < 0) {
exports[key].push(value);
}
return scopedName;
}
return scopedName;
}
function localizeNode(node) {
switch (node.type) {
case "selector":
node.nodes = node.map(localizeNode);
return node;
case "class":
return selectorParser.className({
value: exportScopedName(
node.value,
node.raws && node.raws.value ? node.raws.value : null
),
});
case "id": {
return selectorParser.id({
value: exportScopedName(
node.value,
node.raws && node.raws.value ? node.raws.value : null
),
});
}
}
function localizeNode(node) {
switch (node.type) {
case 'selector':
node.nodes = node.map(localizeNode);
return node;
case 'class':
return selectorParser.className({
value: exportScopedName(
node.value,
node.raws && node.raws.value ? node.raws.value : null
),
});
case 'id': {
return selectorParser.id({
value: exportScopedName(
node.value,
node.raws && node.raws.value ? node.raws.value : null
),
});
}
throw new Error(
`${node.type} ("${node}") is not allowed in a :local block`
);
}
throw new Error(
`${node.type} ("${node}") is not allowed in a :local block`
);
}
function traverseNode(node) {
switch (node.type) {
case "pseudo":
if (node.value === ":local") {
if (node.nodes.length !== 1) {
throw new Error('Unexpected comma (",") in :local block');
}
function traverseNode(node) {
switch (node.type) {
case 'pseudo':
if (node.value === ':local') {
if (node.nodes.length !== 1) {
throw new Error('Unexpected comma (",") in :local block');
}
const selector = localizeNode(node.first, node.spaces);
// move the spaces that were around the psuedo selector to the first
// non-container node
selector.first.spaces = node.spaces;
const selector = localizeNode(node.first, node.spaces);
// move the spaces that were around the psuedo selector to the first
// non-container node
selector.first.spaces = node.spaces;
const nextNode = node.next();
const nextNode = node.next();
if (
nextNode &&
nextNode.type === "combinator" &&
nextNode.value === " " &&
/\\[A-F0-9]{1,6}$/.test(selector.last.value)
) {
selector.last.spaces.after = " ";
}
if (
nextNode &&
nextNode.type === 'combinator' &&
nextNode.value === ' ' &&
/\\[A-F0-9]{1,6}$/.test(selector.last.value)
) {
selector.last.spaces.after = ' ';
node.replaceWith(selector);
return;
}
node.replaceWith(selector);
return;
/* falls through */
case "root":
case "selector": {
node.each(traverseNode);
break;
}
/* falls through */
case 'root':
case 'selector': {
node.each(traverseNode);
break;
case "id":
case "class":
if (exportGlobals) {
exports[node.value] = [node.value];
}
break;
}
case 'id':
case 'class':
if (exportGlobals) {
exports[node.value] = [node.value];
}
break;
return node;
}
return node;
}
// Find any :import and remember imported names
const importedNames = {};
// Find any :import and remember imported names
const importedNames = {};
css.walkRules(rule => {
if (/^:import\(.+\)$/.test(rule.selector)) {
rule.walkDecls(decl => {
importedNames[decl.prop] = true;
});
}
});
root.walkRules((rule) => {
if (/^:import\(.+\)$/.test(rule.selector)) {
rule.walkDecls((decl) => {
importedNames[decl.prop] = true;
});
}
});
// Find any :local classes
css.walkRules(rule => {
if (
rule.nodes &&
rule.selector.slice(0, 2) === '--' &&
rule.selector.slice(-1) === ':'
) {
// ignore custom property set
return;
}
// Find any :local classes
root.walkRules((rule) => {
if (
rule.nodes &&
rule.selector.slice(0, 2) === "--" &&
rule.selector.slice(-1) === ":"
) {
// ignore custom property set
return;
}
let parsedSelector = selectorParser().astSync(rule);
let parsedSelector = selectorParser().astSync(rule);
rule.selector = traverseNode(parsedSelector.clone()).toString();
rule.selector = traverseNode(parsedSelector.clone()).toString();
rule.walkDecls(/composes|compose-with/, decl => {
const localNames = getSingleLocalNamesForComposes(parsedSelector);
const classes = decl.value.split(/\s+/);
rule.walkDecls(/composes|compose-with/, (decl) => {
const localNames = getSingleLocalNamesForComposes(parsedSelector);
const classes = decl.value.split(/\s+/);
classes.forEach(className => {
const global = /^global\(([^\)]+)\)$/.exec(className);
classes.forEach((className) => {
const global = /^global\(([^)]+)\)$/.exec(className);
if (global) {
localNames.forEach(exportedName => {
exports[exportedName].push(global[1]);
});
} else if (hasOwnProperty.call(importedNames, className)) {
localNames.forEach(exportedName => {
exports[exportedName].push(className);
});
} else if (hasOwnProperty.call(exports, className)) {
localNames.forEach(exportedName => {
exports[className].forEach(item => {
exports[exportedName].push(item);
if (global) {
localNames.forEach((exportedName) => {
exports[exportedName].push(global[1]);
});
});
} else {
throw decl.error(
`referenced class name "${className}" in ${decl.prop} not found`
);
}
} else if (hasOwnProperty.call(importedNames, className)) {
localNames.forEach((exportedName) => {
exports[exportedName].push(className);
});
} else if (hasOwnProperty.call(exports, className)) {
localNames.forEach((exportedName) => {
exports[className].forEach((item) => {
exports[exportedName].push(item);
});
});
} else {
throw decl.error(
`referenced class name "${className}" in ${decl.prop} not found`
);
}
});
decl.remove();
});
decl.remove();
});
rule.walkDecls((decl) => {
let tokens = decl.value.split(/(,|'[^']*'|"[^"]*")/);
rule.walkDecls(decl => {
let tokens = decl.value.split(/(,|'[^']*'|"[^"]*")/);
tokens = tokens.map((token, idx) => {
if (idx === 0 || tokens[idx - 1] === ",") {
const localMatch = /^(\s*):local\s*\((.+?)\)/.exec(token);
tokens = tokens.map((token, idx) => {
if (idx === 0 || tokens[idx - 1] === ',') {
const localMatch = /^(\s*):local\s*\((.+?)\)/.exec(token);
if (localMatch) {
return (
localMatch[1] +
exportScopedName(localMatch[2]) +
token.substr(localMatch[0].length)
);
if (localMatch) {
return (
localMatch[1] +
exportScopedName(localMatch[2]) +
token.substr(localMatch[0].length)
);
} else {
return token;
}
} else {
return token;
}
} else {
return token;
}
});
decl.value = tokens.join("");
});
decl.value = tokens.join('');
});
});
// Find any :local keyframes
css.walkAtRules(atrule => {
if (/keyframes$/i.test(atrule.name)) {
const localMatch = /^\s*:local\s*\((.+?)\)\s*$/.exec(atrule.params);
// Find any :local keyframes
root.walkAtRules((atrule) => {
if (/keyframes$/i.test(atrule.name)) {
const localMatch = /^\s*:local\s*\((.+?)\)\s*$/.exec(atrule.params);
if (localMatch) {
atrule.params = exportScopedName(localMatch[1]);
if (localMatch) {
atrule.params = exportScopedName(localMatch[1]);
}
}
}
});
});
// If we found any :locals, insert an :export rule
const exportedNames = Object.keys(exports);
// If we found any :locals, insert an :export rule
const exportedNames = Object.keys(exports);
if (exportedNames.length > 0) {
const exportRule = postcss.rule({ selector: ':export' });
if (exportedNames.length > 0) {
const exportRule = rule({ selector: ":export" });
exportedNames.forEach(exportedName =>
exportRule.append({
prop: exportedName,
value: exports[exportedName].join(' '),
raws: { before: '\n ' },
})
);
exportedNames.forEach((exportedName) =>
exportRule.append({
prop: exportedName,
value: exports[exportedName].join(" "),
raws: { before: "\n " },
})
);
css.append(exportRule);
}
root.append(exportRule);
}
},
};
});
};
processor.generateScopedName = function(name, path) {
plugin.postcss = true;
plugin.generateScopedName = function (name, path) {
const sanitisedPath = path
.replace(/\.[^\.\/\\]+$/, '')
.replace(/[\W_]+/g, '_')
.replace(/^_|_$/g, '');
.replace(/\.[^./\\]+$/, "")
.replace(/[\W_]+/g, "_")
.replace(/^_|_$/g, "");

@@ -315,3 +319,3 @@ return `_${sanitisedPath}__${name}`.trim();

processor.generateExportEntry = function(name, scopedName) {
plugin.generateExportEntry = function (name, scopedName) {
return {

@@ -323,2 +327,2 @@ key: unescape(name),

module.exports = processor;
module.exports = plugin;
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc