Skip to content

Commit

Permalink
Add quiet option to Node.js API (#5542)
Browse files Browse the repository at this point in the history
* Use async-await

* Add name to unnamed functions
  • Loading branch information
hudochenkov committed Sep 17, 2021
1 parent 9608544 commit c0383e9
Show file tree
Hide file tree
Showing 23 changed files with 469 additions and 508 deletions.
6 changes: 6 additions & 0 deletions docs/user-guide/usage/options.md
Expand Up @@ -187,3 +187,9 @@ CLI flag: `--stdin-filename`
A filename to assign the input.

If using `code` or `stdin` to pass a source string directly, you can use `codeFilename` to associate that code with a particular filename.

## `quiet`

CLI flag: `--quiet`

Only register violations for rules with an "error"-level severity (ignore "warning"-level).
13 changes: 13 additions & 0 deletions lib/__tests__/cli.test.js
Expand Up @@ -320,4 +320,17 @@ describe('CLI', () => {
'Max warnings exceeded: 1 found. 0 allowed',
);
});

it('--quiet', async () => {
await cli([
'--quiet',
'--config',
fixturesPath('default-severity-warning.json'),
fixturesPath('empty-block.css'),
]);

expect(process.exitCode).toEqual(2);

expect(process.stdout.write).toHaveBeenCalledTimes(0);
});
});
2 changes: 1 addition & 1 deletion lib/__tests__/plugins.test.js
Expand Up @@ -195,7 +195,7 @@ it('slashless plugin causes configuration error', async () => {

await expect(
postcss().use(stylelint(config)).process('.foo {}', { from: undefined }),
).rejects.toThrow(/^stylelint v7\+ requires plugin rules to be namespaced/);
).rejects.toThrow(/^stylelint requires plugin rules to be namespaced/);
});

it('plugin with primary option array', async () => {
Expand Down
18 changes: 17 additions & 1 deletion lib/__tests__/standalone-quiet.test.js
Expand Up @@ -2,7 +2,7 @@

const standalone = require('../standalone');

it('standalone with input css and quiet mode', () => {
it('standalone with input css and quiet mode (in config)', () => {
const config = {
quiet: true,
rules: {
Expand All @@ -14,3 +14,19 @@ it('standalone with input css and quiet mode', () => {
expect(linted.results[0].warnings).toEqual([]);
});
});

it('standalone with input css and quiet mode (in option)', () => {
const config = {
rules: {
'block-no-empty': [true, { severity: 'warning' }],
},
};

return standalone({
code: 'a {}',
config,
quiet: true,
}).then((linted) => {
expect(linted.results[0].warnings).toEqual([]);
});
});
2 changes: 1 addition & 1 deletion lib/__tests__/standalone.test.js
Expand Up @@ -114,7 +114,7 @@ it('standalone without input css and file(s) should throw error', () => {
'You must pass stylelint a `files` glob or a `code` string, though not both',
);

expect(() => standalone({ config: configBlockNoEmpty })).toThrow(expectedError);
return expect(() => standalone({ config: configBlockNoEmpty })).rejects.toThrow(expectedError);
});

it('standalone with non-existent-file throws an error', async () => {
Expand Down
4 changes: 2 additions & 2 deletions lib/assignDisabledRanges.js
Expand Up @@ -36,12 +36,12 @@ function createDisableRange(comment, start, strictStart, description, end, stric
}

/**
* Run it like a plugin ...
* Run it like a PostCSS plugin
* @param {PostcssRoot} root
* @param {PostcssResult} result
* @returns {PostcssResult}
*/
module.exports = function (root, result) {
module.exports = function assignDisabledRanges(root, result) {
result.stylelint = result.stylelint || {
disabledRanges: {},
ruleSeverities: {},
Expand Down
154 changes: 71 additions & 83 deletions lib/augmentConfig.js
Expand Up @@ -28,26 +28,20 @@ const path = require('path');
* @param {string} [filePath]
* @returns {Promise<StylelintConfig>}
*/
function augmentConfigBasic(stylelint, config, configDir, allowOverrides, filePath) {
return Promise.resolve()
.then(() => {
if (!allowOverrides) return config;

return addOptions(stylelint, config);
})
.then((augmentedConfig) => {
return extendConfig(stylelint, augmentedConfig, configDir);
})
.then((augmentedConfig) => {
if (filePath) {
return applyOverrides(augmentedConfig, configDir, filePath);
}
async function augmentConfigBasic(stylelint, config, configDir, allowOverrides, filePath) {
let augmentedConfig = config;

return augmentedConfig;
})
.then((augmentedConfig) => {
return absolutizePaths(augmentedConfig, configDir);
});
if (allowOverrides) {
augmentedConfig = addOptions(stylelint, augmentedConfig);
}

augmentedConfig = await extendConfig(stylelint, augmentedConfig, configDir);

if (filePath) {
augmentedConfig = applyOverrides(augmentedConfig, configDir, filePath);
}

return absolutizePaths(augmentedConfig, configDir);
}

/**
Expand All @@ -58,18 +52,20 @@ function augmentConfigBasic(stylelint, config, configDir, allowOverrides, filePa
* @param {CosmiconfigResult} [cosmiconfigResult]
* @returns {Promise<CosmiconfigResult | null>}
*/
function augmentConfigExtended(stylelint, cosmiconfigResult) {
if (!cosmiconfigResult) return Promise.resolve(null);
async function augmentConfigExtended(stylelint, cosmiconfigResult) {
if (!cosmiconfigResult) {
return null;
}

const configDir = path.dirname(cosmiconfigResult.filepath || '');
const { ignoreFiles, ...cleanedConfig } = cosmiconfigResult.config;

return augmentConfigBasic(stylelint, cleanedConfig, configDir).then((augmentedConfig) => {
return {
config: augmentedConfig,
filepath: cosmiconfigResult.filepath,
};
});
const augmentedConfig = await augmentConfigBasic(stylelint, cleanedConfig, configDir);

return {
config: augmentedConfig,
filepath: cosmiconfigResult.filepath,
};
}

/**
Expand All @@ -78,36 +74,33 @@ function augmentConfigExtended(stylelint, cosmiconfigResult) {
* @param {CosmiconfigResult} [cosmiconfigResult]
* @returns {Promise<CosmiconfigResult | null>}
*/
function augmentConfigFull(stylelint, filePath, cosmiconfigResult) {
if (!cosmiconfigResult) return Promise.resolve(null);
async function augmentConfigFull(stylelint, filePath, cosmiconfigResult) {
if (!cosmiconfigResult) {
return null;
}

const config = cosmiconfigResult.config;
const filepath = cosmiconfigResult.filepath;

const configDir = stylelint._options.configBasedir || path.dirname(filepath || '');

return augmentConfigBasic(stylelint, config, configDir, true, filePath)
.then((augmentedConfig) => {
return addPluginFunctions(augmentedConfig);
})
.then((augmentedConfig) => {
return addProcessorFunctions(augmentedConfig);
})
.then((augmentedConfig) => {
if (!augmentedConfig.rules) {
throw configurationError(
'No rules found within configuration. Have you provided a "rules" property?',
);
}
let augmentedConfig = await augmentConfigBasic(stylelint, config, configDir, true, filePath);

return normalizeAllRuleSettings(augmentedConfig);
})
.then((augmentedConfig) => {
return {
config: augmentedConfig,
filepath: cosmiconfigResult.filepath,
};
});
augmentedConfig = addPluginFunctions(augmentedConfig);
augmentedConfig = addProcessorFunctions(augmentedConfig);

if (!augmentedConfig.rules) {
throw configurationError(
'No rules found within configuration. Have you provided a "rules" property?',
);
}

augmentedConfig = normalizeAllRuleSettings(augmentedConfig);

return {
config: augmentedConfig,
filepath: cosmiconfigResult.filepath,
};
}

/**
Expand Down Expand Up @@ -167,37 +160,34 @@ function absolutizeProcessors(processors, configDir) {
* @param {string} configDir
* @return {Promise<StylelintConfig>}
*/
function extendConfig(stylelint, config, configDir) {
if (config.extends === undefined) return Promise.resolve(config);
async function extendConfig(stylelint, config, configDir) {
if (config.extends === undefined) {
return config;
}

const normalizedExtends = Array.isArray(config.extends) ? config.extends : [config.extends];
const { extends: configExtends, ...originalWithoutExtends } = config;
const normalizedExtends = [configExtends].flat();

const loadExtends = normalizedExtends.reduce((resultPromise, extendLookup) => {
return resultPromise.then((resultConfig) => {
return loadExtendedConfig(stylelint, resultConfig, configDir, extendLookup).then(
(extendResult) => {
if (!extendResult) return resultConfig;
let resultConfig = originalWithoutExtends;

return mergeConfigs(resultConfig, extendResult.config);
},
);
});
}, Promise.resolve(originalWithoutExtends));
for (const extendLookup of normalizedExtends) {
const extendResult = await loadExtendedConfig(stylelint, configDir, extendLookup);

return loadExtends.then((resultConfig) => {
return mergeConfigs(resultConfig, originalWithoutExtends);
});
if (extendResult) {
resultConfig = mergeConfigs(resultConfig, extendResult.config);
}
}

return mergeConfigs(resultConfig, originalWithoutExtends);
}

/**
* @param {StylelintInternalApi} stylelint
* @param {StylelintConfig} config
* @param {string} configDir
* @param {string} extendLookup
* @return {Promise<CosmiconfigResult | null>}
*/
function loadExtendedConfig(stylelint, config, configDir, extendLookup) {
function loadExtendedConfig(stylelint, configDir, extendLookup) {
const extendPath = getModulePath(configDir, extendLookup);

return stylelint._extendExplorer.load(extendPath);
Expand Down Expand Up @@ -283,43 +273,41 @@ function mergeConfigs(a, b) {
* @returns {StylelintConfig}
*/
function addPluginFunctions(config) {
if (!config.plugins) return config;
if (!config.plugins) {
return config;
}

const normalizedPlugins = [config.plugins].flat();

const normalizedPlugins = Array.isArray(config.plugins) ? config.plugins : [config.plugins];
/** @type {{[k: string]: Function}} */
const pluginFunctions = {};

const pluginFunctions = normalizedPlugins.reduce((result, pluginLookup) => {
for (const pluginLookup of normalizedPlugins) {
let pluginImport = require(pluginLookup);

// Handle either ES6 or CommonJS modules
pluginImport = pluginImport.default || pluginImport;

// A plugin can export either a single rule definition
// or an array of them
const normalizedPluginImport = Array.isArray(pluginImport) ? pluginImport : [pluginImport];
const normalizedPluginImport = [pluginImport].flat();

normalizedPluginImport.forEach((pluginRuleDefinition) => {
if (!pluginRuleDefinition.ruleName) {
throw configurationError(
'stylelint v3+ requires plugins to expose a ruleName. ' +
`The plugin "${pluginLookup}" is not doing this, so will not work ` +
'with stylelint v3+. Please file an issue with the plugin.',
`stylelint requires plugins to expose a ruleName. The plugin "${pluginLookup}" is not doing this, so will not work with stylelint. Please file an issue with the plugin.`,
);
}

if (!pluginRuleDefinition.ruleName.includes('/')) {
throw configurationError(
'stylelint v7+ requires plugin rules to be namespaced, ' +
'i.e. only `plugin-namespace/plugin-rule-name` plugin rule names are supported. ' +
`The plugin rule "${pluginRuleDefinition.ruleName}" does not do this, so will not work. ` +
'Please file an issue with the plugin.',
`stylelint requires plugin rules to be namespaced, i.e. only \`plugin-namespace/plugin-rule-name\` plugin rule names are supported. The plugin rule "${pluginRuleDefinition.ruleName}" does not do this, so will not work. Please file an issue with the plugin.`,
);
}

result[pluginRuleDefinition.ruleName] = pluginRuleDefinition.rule;
pluginFunctions[pluginRuleDefinition.ruleName] = pluginRuleDefinition.rule;
});

return result;
}, /** @type {{[k: string]: Function}} */ ({}));
}

config.pluginFunctions = pluginFunctions;

Expand Down

0 comments on commit c0383e9

Please sign in to comment.