stylelint-media-use-custom-media
Advanced tools
Comparing version
156
index.js
@@ -5,27 +5,31 @@ 'use strict'; | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var stylelint = require('stylelint'); | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var postcss = require('postcss'); | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
function _interopNamespace(e) { | ||
if (e && e.__esModule) { return e; } else { | ||
var n = {}; | ||
if (e) { | ||
Object.keys(e).forEach(function (k) { | ||
if (e && e.__esModule) return e; | ||
var n = Object.create(null); | ||
if (e) { | ||
Object.keys(e).forEach(function (k) { | ||
if (k !== 'default') { | ||
var d = Object.getOwnPropertyDescriptor(e, k); | ||
Object.defineProperty(n, k, d.get ? d : { | ||
enumerable: true, | ||
get: function () { | ||
return e[k]; | ||
} | ||
get: function () { return e[k]; } | ||
}); | ||
}); | ||
} | ||
n['default'] = e; | ||
return n; | ||
} | ||
}); | ||
} | ||
n["default"] = e; | ||
return Object.freeze(n); | ||
} | ||
var stylelint = _interopDefault(require('stylelint')); | ||
var fs = _interopDefault(require('fs')); | ||
var path = _interopDefault(require('path')); | ||
var postcss = _interopDefault(require('postcss')); | ||
var stylelint__default = /*#__PURE__*/_interopDefaultLegacy(stylelint); | ||
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); | ||
var path__default = /*#__PURE__*/_interopDefaultLegacy(path); | ||
var postcss__default = /*#__PURE__*/_interopDefaultLegacy(postcss); | ||
@@ -38,6 +42,4 @@ function parse(string, splitByAnd) { | ||
let i = -1; | ||
while (++i < string.length) { | ||
const char = string[i]; | ||
if (char === '(') { | ||
@@ -56,3 +58,2 @@ func += 1; | ||
} | ||
if (split) { | ||
@@ -66,10 +67,7 @@ array.push(splitByAnd ? new MediaExpression(buffer + char) : new MediaQuery(buffer)); | ||
} | ||
if (buffer !== '') { | ||
array.push(splitByAnd ? new MediaExpression(buffer) : new MediaQuery(buffer)); | ||
} | ||
return array; | ||
} | ||
class MediaQueryList { | ||
@@ -80,3 +78,2 @@ constructor(string) { | ||
} | ||
invert() { | ||
@@ -88,13 +85,9 @@ this.nodes.forEach(node => { | ||
} | ||
clone() { | ||
return new MediaQueryList(String(this)); | ||
} | ||
toString() { | ||
return this.nodes.join(','); | ||
} | ||
} | ||
class MediaQuery { | ||
@@ -122,3 +115,2 @@ constructor(string) { | ||
} | ||
clone(overrides) { | ||
@@ -129,3 +121,2 @@ const instance = new MediaQuery(String(this)); | ||
} | ||
invert() { | ||
@@ -135,3 +126,2 @@ this.modifier = this.modifier ? '' : this.raws.originalModifier; | ||
} | ||
toString() { | ||
@@ -143,5 +133,3 @@ const { | ||
} | ||
} | ||
class MediaExpression { | ||
@@ -161,3 +149,2 @@ constructor(string) { | ||
} | ||
clone(overrides) { | ||
@@ -168,3 +155,2 @@ const instance = new MediaExpression(String(this)); | ||
} | ||
toString() { | ||
@@ -176,5 +162,3 @@ const { | ||
} | ||
} | ||
const modifierRE = '(not|only)'; | ||
@@ -195,20 +179,26 @@ const typeRE = '(all|print|screen|speech)'; | ||
// initialize custom selectors | ||
const customMedias = {}; // for each @custom-media at-rule | ||
const customMedias = {}; | ||
// for each @custom-media at-rule | ||
root.nodes.forEach(node => { | ||
if (isCustomMedia(node)) { | ||
// extract the name and selectors from the params of the custom selector | ||
const [, name, selectors] = node.params.match(customMediaParamsRegExp); // write the parsed selectors to the custom selector | ||
const [, name, selectors] = node.params.match(customMediaParamsRegExp); | ||
// write the parsed selectors to the custom selector | ||
customMedias[name] = selectors; | ||
} | ||
}); // return all custom medias | ||
}); | ||
// return all custom medias | ||
return customMedias; | ||
}); // match the custom selector name | ||
}); | ||
const customMediaNameRegExp = /^custom-media$/i; // match the custom selector params | ||
// match the custom selector name | ||
const customMediaNameRegExp = /^custom-media$/i; | ||
const customMediaParamsRegExp = /^(--[A-z][\w-]*)\s+([\W\w]+)\s*$/; // whether the atrule is a custom selector | ||
// match the custom selector params | ||
const customMediaParamsRegExp = /^(--[A-z][\w-]*)\s+([\W\w]+)\s*$/; | ||
// whether the atrule is a custom selector | ||
const isCustomMedia = node => node.type === 'atrule' && customMediaNameRegExp.test(node.name) && customMediaParamsRegExp.test(node.params); | ||
@@ -221,3 +211,3 @@ | ||
const css = await readFile(from); | ||
const root = postcss.parse(css, { | ||
const root = postcss__default["default"].parse(css, { | ||
from | ||
@@ -227,6 +217,6 @@ }); | ||
} | ||
/* Get Custom Media from Object | ||
/* ========================================================================== */ | ||
function getCustomMediaFromObject(object) { | ||
@@ -236,6 +226,6 @@ const customMedia = Object.assign({}, Object(object).customMedia, Object(object)['custom-media']); | ||
} | ||
/* Get Custom Media from JSON file | ||
/* ========================================================================== */ | ||
async function getCustomMediaFromJSONFile(from) { | ||
@@ -245,14 +235,14 @@ const object = await readJSON(from); | ||
} | ||
/* Get Custom Media from JS file | ||
/* ========================================================================== */ | ||
async function getCustomMediaFromJSFile(from) { | ||
const object = await Promise.resolve().then(function () { return _interopNamespace(require(from)); }); | ||
const object = await (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })(from); | ||
return getCustomMediaFromObject(object); | ||
} | ||
/* Get Custom Media from Imports | ||
/* ========================================================================== */ | ||
function getCustomMediaFromImports(sources) { | ||
@@ -264,17 +254,19 @@ return sources.map(source => { | ||
return source(); | ||
} // read the source as an object | ||
} | ||
// read the source as an object | ||
const opts = source === Object(source) ? source : { | ||
from: String(source) | ||
}; // skip objects with Custom Media | ||
}; | ||
// skip objects with Custom Media | ||
if (opts.customMedia || opts['custom-media']) { | ||
return opts; | ||
} // source pathname | ||
} | ||
// source pathname | ||
const from = path__default["default"].resolve(String(opts.from || '')); | ||
const from = path.resolve(String(opts.from || '')); // type of file being read from | ||
const type = (opts.type || path.extname(from).slice(1)).toLowerCase(); | ||
// type of file being read from | ||
const type = (opts.type || path__default["default"].extname(from).slice(1)).toLowerCase(); | ||
return { | ||
@@ -289,18 +281,15 @@ type, | ||
} = await source; | ||
if (type === 'css') { | ||
return Object.assign(await customMedia, await getCustomMediaFromCSSFile(from)); | ||
} | ||
if (type === 'js') { | ||
return Object.assign(await customMedia, await getCustomMediaFromJSFile(from)); | ||
} | ||
if (type === 'json') { | ||
return Object.assign(await customMedia, await getCustomMediaFromJSONFile(from)); | ||
} | ||
return Object.assign(await customMedia, await getCustomMediaFromObject(await source)); | ||
}, {}); | ||
} | ||
/* Promise-ified utilities | ||
@@ -310,3 +299,3 @@ /* ========================================================================== */ | ||
const readFile = from => new Promise((resolve, reject) => { | ||
fs.readFile(from, 'utf8', (error, result) => { | ||
fs__default["default"].readFile(from, 'utf8', (error, result) => { | ||
if (error) { | ||
@@ -319,47 +308,50 @@ reject(error); | ||
}); | ||
const readJSON = async from => JSON.parse(await readFile(from)); | ||
const ruleName = 'csstools/media-use-custom-media'; | ||
var index = stylelint.createPlugin(ruleName, (method, opts) => { | ||
var index = stylelint__default["default"].createPlugin(ruleName, (method, opts) => { | ||
// sources to import custom selectors from | ||
const importFrom = [].concat(Object(opts).importFrom || []); // conditionally promise any custom selectors are imported | ||
const importFrom = [].concat(Object(opts).importFrom || []); | ||
// conditionally promise any custom selectors are imported | ||
const customMediaPromise = isMethodAlwaysKnown(method) || isMethodKnown(method) ? getCustomMediaFromImports(importFrom) : {}; | ||
return async (root, result) => { | ||
// valid methods are: "always" || "always-known" || "never" || "known" || true || false || null | ||
const validOptions = stylelint.utils.validateOptions(result, ruleName, { | ||
const validOptions = stylelint__default["default"].utils.validateOptions(result, ruleName, { | ||
actual: method, | ||
possible() { | ||
return isMethodAlways(method) || isMethodAlwaysKnown(method) || isMethodIndifferent(method) || isMethodKnown(method) || isMethodNever(method); | ||
} | ||
}); | ||
}); // conditionally enforce the use of custom media | ||
// conditionally enforce the use of custom media | ||
if (validOptions && !isMethodIndifferent(method)) { | ||
// all custom properties from the file and imports | ||
const customMedia = isMethodAlwaysKnown(method) || isMethodKnown(method) ? Object.assign(await customMediaPromise, getCustomMediaFromRoot(root)) : {}; // check every @media at-rule | ||
const customMedia = isMethodAlwaysKnown(method) || isMethodKnown(method) ? Object.assign(await customMediaPromise, getCustomMediaFromRoot(root)) : {}; | ||
// check every @media at-rule | ||
root.walkAtRules(mediaAtRuleNameRegExp, atrule => { | ||
const mediaAST = parseMedia(atrule.params); | ||
let word = `@${atrule.name}`; // check whether media queries are using custom media references | ||
let word = `@${atrule.name}`; | ||
// check whether media queries are using custom media references | ||
const isCorrectlyUsingMedia = mediaAST.nodes.every(node => node.nodes.every(child => { | ||
// whether the expression is like @media (--foo) | ||
const isCustomExpression = checkCustomExpression(child); | ||
const returnValue = isCustomExpression ? isMethodKnown(method) || isMethodAlwaysKnown(method) // @media (--foo) && ("always-known" || "known") && @custom-media --foo bar; | ||
? child.value.slice(1, -1) in customMedia // @media (--foo) && "always" | ||
: isMethodAlways(method) // !@media (--foo) && ("known" || "never") | ||
const returnValue = isCustomExpression ? isMethodKnown(method) || isMethodAlwaysKnown(method) | ||
// @media (--foo) && ("always-known" || "known") && @custom-media --foo bar; | ||
? child.value.slice(1, -1) in customMedia | ||
// @media (--foo) && "always" | ||
: isMethodAlways(method) | ||
// !@media (--foo) && ("known" || "never") | ||
: isMethodKnown(method) || isMethodNever(method); | ||
if (!returnValue) { | ||
word = String(child); | ||
} | ||
return returnValue; | ||
})); // conditionally report media queries not using custom media references | ||
})); | ||
// conditionally report media queries not using custom media references | ||
if (!isCorrectlyUsingMedia) { | ||
stylelint.utils.report({ | ||
stylelint__default["default"].utils.report({ | ||
message: isMethodNever(method) ? messages.unexpected(atrule.params) : messages.expected(atrule.params), | ||
@@ -376,30 +368,22 @@ node: atrule, | ||
}); | ||
const messages = stylelint.utils.ruleMessages(ruleName, { | ||
const messages = stylelint__default["default"].utils.ruleMessages(ruleName, { | ||
expected(expression) { | ||
return `Expected a custom media query instead of "${expression}".`; | ||
}, | ||
unexpected(expression) { | ||
return `Expected no custom media query instead of "${expression}".`; | ||
} | ||
}); | ||
const mediaAtRuleNameRegExp = /^media$/i; | ||
const customMediaExpressionRegExp = /\(--[\w-]+\)/i; | ||
const checkCustomExpression = node => node.nodeType === 'expression' && customMediaExpressionRegExp.test(node.value); | ||
const isMethodIndifferent = method => method === 'ignore' || method === null; | ||
const isMethodAlways = method => method === 'always' || method === true; | ||
const isMethodAlwaysKnown = method => method === 'always-known'; | ||
const isMethodKnown = method => method === 'known'; | ||
const isMethodNever = method => method === 'never' || method === false; | ||
exports.default = index; | ||
exports["default"] = index; | ||
exports.messages = messages; | ||
exports.ruleName = ruleName; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "stylelint-media-use-custom-media", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"description": "Enforce usage of custom media queries in CSS", | ||
@@ -19,3 +19,2 @@ "author": "Jonathan Neal <jonathantneal@hotmail.com>", | ||
"scripts": { | ||
"prepublishOnly": "npm test", | ||
"pretest": "rollup -c .rollup.js --silent", | ||
@@ -40,3 +39,3 @@ "test": "npm run test:js && npm run test:tape", | ||
"rollup-plugin-babel": "^4.4.0", | ||
"stylelint": "^10.0.1", | ||
"stylelint": "^14.0.1", | ||
"stylelint-test-rule-tape": "^0.2.0", | ||
@@ -46,3 +45,3 @@ "tap-spec": "^5.0.0" | ||
"peerDependencies": { | ||
"stylelint": "10 - 13" | ||
"stylelint": "10 - 14" | ||
}, | ||
@@ -49,0 +48,0 @@ "eslintConfig": { |
@@ -102,3 +102,3 @@ # Media Use Custom Media [<img src="https://jonathantneal.github.io/stylelint-logo.svg" alt="stylelint" width="90" height="90" align="right">][stylelint] | ||
If the first option is `"known"`, then [Media Use Custom Media] requires all | ||
If the first option is `"always-known"`, then [Media Use Custom Media] requires all | ||
`@media` queries to use known Custom Media from either `@custom-media` | ||
@@ -105,0 +105,0 @@ declarations in the file or imported using the second option. Then the |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
84380
2.76%634
6.38%0
-100%7
-12.5%