Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/config/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ module.exports = {
'tailwindcss/classnames-order': 'warn',
'tailwindcss/enforces-negative-arbitrary-values': 'warn',
'tailwindcss/enforces-shorthand': 'warn',
'tailwindcss/migration-from-tailwind-2': 'warn',
'tailwindcss/migration-from-tailwind-2': 'off',
'tailwindcss/no-arbitrary-value': 'off',
'tailwindcss/no-custom-classname': 'warn',
'tailwindcss/no-contradicting-classname': 'error',
'tailwindcss/no-contradicting-classname': 'off',
'tailwindcss/no-unnecessary-arbitrary-value': 'warn',
};
16 changes: 2 additions & 14 deletions lib/rules/classnames-order.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
'use strict';

const docsUrl = require('../util/docsUrl');
const customConfig = require('../util/customConfig');
const { getSortedClassNames } = require('../util/tailwindAPI');
const astUtil = require('../util/ast');
const removeDuplicatesFromClassnamesAndWhitespaces = require('../util/removeDuplicatesFromClassnamesAndWhitespaces');
const getOption = require('../util/settings');
const parserUtil = require('../util/parser');
const order = require('../util/prettier/order');
const createContextFallback = require('tailwindcss/lib/lib/setupContextUtils').createContext;

//------------------------------------------------------------------------------
// Rule Definition
Expand All @@ -21,8 +19,6 @@ const createContextFallback = require('tailwindcss/lib/lib/setupContextUtils').c
// messageId will still be usable in tests.
const INVALID_CLASSNAMES_ORDER_MSG = 'Invalid Tailwind CSS classnames order';

const contextFallbackCache = new WeakMap();

module.exports = {
meta: {
docs: {
Expand Down Expand Up @@ -75,14 +71,6 @@ module.exports = {
const classRegex = getOption(context, 'classRegex');
const removeDuplicates = getOption(context, 'removeDuplicates');

const mergedConfig = customConfig.resolve(twConfig);
const contextFallback = // Set the created contextFallback in the cache if it does not exist yet.
(
contextFallbackCache.has(mergedConfig)
? contextFallbackCache
: contextFallbackCache.set(mergedConfig, createContextFallback(mergedConfig))
).get(mergedConfig);

//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
Expand Down Expand Up @@ -175,7 +163,7 @@ module.exports = {
return;
}

let orderedClassNames = order(classNames, contextFallback).split(' ');
let orderedClassNames = getSortedClassNames(twConfig, classNames);

if (removeDuplicates) {
removeDuplicatesFromClassnamesAndWhitespaces(orderedClassNames, whitespaces, headSpace, tailSpace);
Expand Down
4 changes: 2 additions & 2 deletions lib/rules/enforces-negative-arbitrary-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'use strict';

const docsUrl = require('../util/docsUrl');
const customConfig = require('../util/customConfig');
const { getTailwindConfig } = require('../util/tailwindAPI');
const astUtil = require('../util/ast');
const groupUtil = require('../util/groupMethods');
const getOption = require('../util/settings');
Expand Down Expand Up @@ -66,7 +66,7 @@ module.exports = {
const twConfig = getOption(context, 'config');
const classRegex = getOption(context, 'classRegex');

const mergedConfig = customConfig.resolve(twConfig);
const mergedConfig = getTailwindConfig(twConfig);

//----------------------------------------------------------------------
// Helpers
Expand Down
14 changes: 11 additions & 3 deletions lib/rules/enforces-shorthand.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

const docsUrl = require('../util/docsUrl');
const defaultGroups = require('../config/groups').groups;
const customConfig = require('../util/customConfig');
const { getTailwindConfig } = require('../util/tailwindAPI');
const astUtil = require('../util/ast');
const groupUtil = require('../util/groupMethods');
const getOption = require('../util/settings');
Expand Down Expand Up @@ -67,7 +67,7 @@ module.exports = {
const twConfig = getOption(context, 'config');
const classRegex = getOption(context, 'classRegex');

const mergedConfig = customConfig.resolve(twConfig);
const mergedConfig = getTailwindConfig(twConfig);

//----------------------------------------------------------------------
// Helpers
Expand Down Expand Up @@ -270,7 +270,15 @@ module.exports = {
const bodyMatch = inputSet.some(
(inputClassPattern) => `${mergedConfig.prefix}${inputClassPattern}` === remainingClass.body
);
if ([undefined, null].includes(mergedConfig.theme.size)) {
if (
!mergedConfig.theme ||
!mergedConfig.theme.size ||
!mergedConfig.theme.size[remainingClass.value] ||
!mergedConfig.theme.width ||
!mergedConfig.theme.width[remainingClass.value] ||
!mergedConfig.theme.height ||
!mergedConfig.theme.height[remainingClass.value]
) {
return false;
}
// w-screen + h-screen ≠ size-screen (Issue #307)
Expand Down
4 changes: 2 additions & 2 deletions lib/rules/migration-from-tailwind-2.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'use strict';

const docsUrl = require('../util/docsUrl');
const customConfig = require('../util/customConfig');
const { getTailwindConfig } = require('../util/tailwindAPI');
const astUtil = require('../util/ast');
const groupUtil = require('../util/groupMethods');
const getOption = require('../util/settings');
Expand Down Expand Up @@ -72,7 +72,7 @@ module.exports = {
const twConfig = getOption(context, 'config');
const classRegex = getOption(context, 'classRegex');

const mergedConfig = customConfig.resolve(twConfig);
const mergedConfig = getTailwindConfig(twConfig);

//----------------------------------------------------------------------
// Helpers
Expand Down
4 changes: 2 additions & 2 deletions lib/rules/no-arbitrary-value.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'use strict';

const docsUrl = require('../util/docsUrl');
const customConfig = require('../util/customConfig');
const { getTailwindConfig } = require('../util/tailwindAPI');
const astUtil = require('../util/ast');
const groupUtil = require('../util/groupMethods');
const getOption = require('../util/settings');
Expand Down Expand Up @@ -66,7 +66,7 @@ module.exports = {
const twConfig = getOption(context, 'config');
const classRegex = getOption(context, 'classRegex');

const mergedConfig = customConfig.resolve(twConfig);
const mergedConfig = getTailwindConfig(twConfig);

//----------------------------------------------------------------------
// Helpers
Expand Down
4 changes: 2 additions & 2 deletions lib/rules/no-contradicting-classname.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

const docsUrl = require('../util/docsUrl');
const defaultGroups = require('../config/groups').groups;
const customConfig = require('../util/customConfig');
const { getTailwindConfig } = require('../util/tailwindAPI');
const astUtil = require('../util/ast');
const groupUtil = require('../util/groupMethods');
const getOption = require('../util/settings');
Expand Down Expand Up @@ -68,7 +68,7 @@ module.exports = {
const twConfig = getOption(context, 'config');
const classRegex = getOption(context, 'classRegex');

const mergedConfig = customConfig.resolve(twConfig);
const mergedConfig = getTailwindConfig(twConfig);

//----------------------------------------------------------------------
// Helpers
Expand Down
15 changes: 3 additions & 12 deletions lib/rules/no-custom-classname.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@

const docsUrl = require('../util/docsUrl');
const defaultGroups = require('../config/groups').groups;
const customConfig = require('../util/customConfig');
const { getTailwindConfig, isValidClassName } = require('../util/tailwindAPI');
const astUtil = require('../util/ast');
const groupUtil = require('../util/groupMethods');
const getOption = require('../util/settings');
const parserUtil = require('../util/parser');
const getClassnamesFromCSS = require('../util/cssFiles');
const createContextFallback = require('tailwindcss/lib/lib/setupContextUtils').createContext;
const generated = require('../util/generated');
const escapeRegex = require('../util/regex').escapeRegex;

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -97,13 +95,7 @@ module.exports = {
const whitelist = getOption(context, 'whitelist');
const classRegex = getOption(context, 'classRegex');

const mergedConfig = customConfig.resolve(twConfig);
const contextFallback = // Set the created contextFallback in the cache if it does not exist yet.
(
contextFallbackCache.has(mergedConfig)
? contextFallbackCache
: contextFallbackCache.set(mergedConfig, createContextFallback(mergedConfig))
).get(mergedConfig);
const mergedConfig = getTailwindConfig(twConfig);

//----------------------------------------------------------------------
// Helpers
Expand All @@ -121,8 +113,7 @@ module.exports = {
*/
const parseForCustomClassNames = (classNames, node) => {
classNames.forEach((className) => {
const gen = generated(className, contextFallback);
if (gen.length) {
if (isValidClassName(twConfig, className)) {
return; // Lazier is faster... processing next className!
}
const idx = groupUtil.getGroupIndex(className, groups, mergedConfig.separator);
Expand Down
6 changes: 3 additions & 3 deletions lib/rules/no-unnecessary-arbitrary-value.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'use strict';

const docsUrl = require('../util/docsUrl');
const customConfig = require('../util/customConfig');
const { getTailwindConfig } = require('../util/tailwindAPI');
const astUtil = require('../util/ast');
const groupUtil = require('../util/groupMethods');
const getOption = require('../util/settings');
Expand Down Expand Up @@ -72,7 +72,7 @@ module.exports = {
const twConfig = getOption(context, 'config');
const classRegex = getOption(context, 'classRegex');

const mergedConfig = customConfig.resolve(twConfig);
const mergedConfig = getTailwindConfig(twConfig);
const groups = groupUtil.getGroups(defaultGroups, mergedConfig);
const configKeys = groupUtil.getGroupConfigKeys(defaultGroups);
let parentTemplateLiteral = null;
Expand Down Expand Up @@ -193,7 +193,7 @@ module.exports = {
const isNegativeClass = parsed.body.indexOf('-') === 0;
const isNegativeValue = arbitraryValue.indexOf('-') === 0;
const configurationKey = configKeys[groupIdx];
const configuration = mergedConfig.theme[configurationKey];
const configuration = mergedConfig.theme?.[configurationKey];
if ([undefined, null].includes(configuration)) {
return false;
}
Expand Down
99 changes: 13 additions & 86 deletions lib/util/customConfig.js
Original file line number Diff line number Diff line change
@@ -1,100 +1,27 @@
'use strict';

const fs = require('fs');
const path = require('path');
const resolveConfig = require('tailwindcss/resolveConfig');
let twLoadConfig;
const { TailwindUtils } = require('tailwind-api-utils');

try {
twLoadConfig = require('tailwindcss/lib/lib/load-config');
} catch (err) {
twLoadConfig = null;
}

const CHECK_REFRESH_RATE = 1_000;
let previousConfig = null;
let lastCheck = null;
let mergedConfig = null;
let lastModifiedDate = null;

/**
* @see https://stackoverflow.com/questions/9210542/node-js-require-cache-possible-to-invalidate
* @param {string} module The path to the module
* @returns the module's export
*/
function requireUncached(module) {
delete require.cache[require.resolve(module)];
if (twLoadConfig === null) {
// Using native loading
return require(module);
} else {
// Using Tailwind CSS's loadConfig utility
return twLoadConfig.loadConfig(module);
}
}
// for nativewind preset
process.env.TAILWIND_MODE = 'build';

const CHECK_REFRESH_RATE = 10_000;
let lastCheck = new Map();
/**
* Load the config from a path string or parsed from an object
* @param {string|Object} config
* @returns `null` when unchanged, `{}` when not found
* @type {Map<string, TailwindUtils}>}
*/
function loadConfig(config) {
let loadedConfig = null;
if (typeof config === 'string') {
const resolvedPath = path.isAbsolute(config) ? config : path.join(path.resolve(), config);
try {
const stats = fs.statSync(resolvedPath);
const mtime = `${stats.mtime || ''}`;
if (stats === null) {
// Default to no config
loadedConfig = {};
} else if (lastModifiedDate !== mtime) {
// Load the config based on path
lastModifiedDate = mtime;
loadedConfig = requireUncached(resolvedPath);
} else {
// Unchanged config
loadedConfig = null;
}
} catch (err) {
// Default to no config
loadedConfig = {};
} finally {
return loadedConfig;
}
} else {
if (typeof config === 'object' && config !== null) {
return config;
}
return {};
}
}

function convertConfigToString(config) {
switch (typeof config) {
case 'string':
return config;
case 'object':
return JSON.stringify(config);
default:
return config.toString();
}
}
let mergedConfig = new Map();

function resolve(twConfig) {
const newConfig = convertConfigToString(twConfig) !== convertConfigToString(previousConfig);
const newConfig = mergedConfig.get(twConfig) === undefined;
const now = Date.now();
const expired = now - lastCheck > CHECK_REFRESH_RATE;
const expired = now - lastCheck.get(twConfig) > CHECK_REFRESH_RATE;
if (newConfig || expired) {
previousConfig = twConfig;
lastCheck = now;
const userConfig = loadConfig(twConfig);
// userConfig is null when config file was not modified
if (userConfig !== null) {
mergedConfig = resolveConfig(userConfig);
}
lastCheck.set(twConfig, now);
const tailwindUtils = new TailwindUtils();
mergedConfig.set(twConfig, tailwindUtils);
}
return mergedConfig;
return mergedConfig.get(twConfig);
}

module.exports = {
Expand Down
10 changes: 10 additions & 0 deletions lib/util/getSortedClassNamesWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { runAsWorker } = require('synckit');
const { resolve } = require('./customConfig');

runAsWorker(async (twConfig, classNames) => {
const tailwindUtils = resolve(twConfig);
if (!tailwindUtils.context) {
await tailwindUtils.loadConfig(twConfig);
}
return tailwindUtils.getSortedClassNames(classNames);
});
10 changes: 10 additions & 0 deletions lib/util/getTailwindConfigWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { runAsWorker } = require('synckit');
const { resolve } = require('./customConfig');

runAsWorker(async (twConfig) => {
const tailwindUtils = resolve(twConfig);
if (!tailwindUtils.context) {
await tailwindUtils.loadConfig(twConfig);
}
return tailwindUtils.context.tailwindConfig;
});
5 changes: 4 additions & 1 deletion lib/util/groupMethods.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function escapeSpecialChars(str) {
* @returns {String} The suffix or an empty string
*/
function generateOptionalOpacitySuffix(config) {
const opacityKeys = !config.theme['opacity'] ? [] : Object.keys(config.theme['opacity']);
const opacityKeys = !config.theme?.['opacity'] ? [] : Object.keys(config.theme['opacity']);
opacityKeys.push('\\[(\\d*\\.?\\d*)%?\\]');
return `(\\/(${opacityKeys.join('|')}))?`;
}
Expand Down Expand Up @@ -97,6 +97,9 @@ function generateOptions(propName, keys, config, isNegative = false) {
// https://tailwindcss.com/docs/customizing-colors#color-object-syntax
const options = [];
keys.forEach((k) => {
if (!config.theme?.[propName]?.[k] && !config.theme?.colors?.[k]) {
return;
}
const color = config.theme[propName][k] || config.theme.colors[k];
if (typeof color === 'string') {
options.push(escapeSpecialChars(k) + opacitySuffixes);
Expand Down
Loading