Skip to content

Commit

Permalink
Fix customSyntax resolution with configBasedir (#6536)
Browse files Browse the repository at this point in the history
  • Loading branch information
ybiquitous committed Dec 24, 2022
1 parent cae5880 commit c0db3fd
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/fifty-donuts-arrive.md
@@ -0,0 +1,5 @@
---
"stylelint": patch
---

Fixed: `customSyntax` resolution with `configBasedir`
2 changes: 1 addition & 1 deletion docs/user-guide/usage/cli.md
Expand Up @@ -46,7 +46,7 @@ Force enabling/disabling of color.

### `--config-basedir`

Absolute path to the directory that relative paths defining "extends" and "plugins" are _relative to_. Only necessary if these values are relative paths. [More info](options.md#configbasedir).
Absolute path to the directory that relative paths defining "extends", "plugins", and "customSyntax" are _relative to_. Only necessary if these values are relative paths. [More info](options.md#configbasedir).

### `--config`

Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/usage/options.md
Expand Up @@ -28,7 +28,7 @@ The path should be either absolute or relative to the directory that your proces

CLI flag: `--config-basedir`

Absolute path to the directory that relative paths defining "extends" and "plugins" are _relative to_. Only necessary if these values are relative paths.
Absolute path to the directory that relative paths defining "extends", "plugins", and "customSyntax" are _relative to_. Only necessary if these values are relative paths.

## `fix`

Expand Down
6 changes: 3 additions & 3 deletions lib/__tests__/__snapshots__/cli.test.js.snap
Expand Up @@ -29,9 +29,9 @@ exports[`CLI --help 1`] = `
--config-basedir
An absolute path to the directory that relative paths defining \\"extends\\"
and \\"plugins\\" are *relative to*. Only necessary if these values are
relative paths.
An absolute path to the directory that relative paths defining \\"extends\\",
\\"plugins\\", and \\"customSyntax\\" are *relative to*. Only necessary if these
values are relative paths.
--print-config
Expand Down
30 changes: 30 additions & 0 deletions lib/__tests__/cli.test.js
Expand Up @@ -402,4 +402,34 @@ describe('CLI', () => {
expect(process.stdout.write).toHaveBeenCalledTimes(1);
expect(process.stdout.write).toHaveBeenCalledWith(expect.stringMatching(/block-no-empty/));
});

it('--custom-syntax', async () => {
await cli([
'--custom-syntax=postcss-scss',
'--config',
fixturesPath('config-color-no-invalid-hex.json'),
fixturesPath('invalid-hex.scss'),
]);

expect(process.stdout.write).toHaveBeenCalledTimes(1);
expect(process.stdout.write).toHaveBeenCalledWith(
expect.stringMatching(/color-no-invalid-hex/),
);
});

it('--custom-syntax and --config-basedir', async () => {
await cli([
'--custom-syntax=./custom-syntax',
'--config-basedir',
fixturesPath(),
'--config',
fixturesPath('config-color-no-invalid-hex.json'),
fixturesPath('invalid-hex.scss'),
]);

expect(process.stdout.write).toHaveBeenCalledTimes(1);
expect(process.stdout.write).toHaveBeenCalledWith(
expect.stringMatching(/color-no-invalid-hex/),
);
});
});
3 changes: 3 additions & 0 deletions lib/__tests__/fixtures/invalid-hex.scss
@@ -0,0 +1,3 @@
a {
color: #zzzzzz; // SCSS comment
}
48 changes: 48 additions & 0 deletions lib/__tests__/standalone-syntax.test.js
Expand Up @@ -267,6 +267,28 @@ describe('customSyntax set in the config', () => {
expect(results[0].warnings[0].rule).toBe('block-no-empty');
});

it('standalone with path to custom syntax relative from "configBasedir"', async () => {
const config = {
customSyntax: './custom-syntax',
rules: {
'block-no-empty': true,
},
};

const { results } = await standalone({
config,
configBasedir: fixturesPath,
code: '$foo: bar; // foo;\nb {}',
formatter: stringFormatter,
});

expect(results).toHaveLength(1);
expect(results[0].warnings).toHaveLength(1);
expect(results[0].warnings[0].line).toBe(2);
expect(results[0].warnings[0].column).toBe(3);
expect(results[0].warnings[0].rule).toBe('block-no-empty');
});

it('rejects on unknown custom syntax option', async () => {
await expect(
standalone({
Expand All @@ -280,4 +302,30 @@ describe('customSyntax set in the config', () => {
'Cannot resolve custom syntax module "unknown-module". Check that module "unknown-module" is available and spelled correctly.',
);
});

it('rejects on invalid custom syntax object', async () => {
await expect(
standalone({
code: '',
config: {
customSyntax: {},
rules: { 'block-no-empty': 'wahoo' },
},
}),
).rejects.toThrow(
'An object provided to the "customSyntax" option must have a "parse" property. Ensure the "parse" property exists and its value is a function.',
);
});

it('rejects on invalid custom syntax type', async () => {
await expect(
standalone({
code: '',
config: {
customSyntax: true,
rules: { 'block-no-empty': 'wahoo' },
},
}),
).rejects.toThrow('Custom syntax must be a string or a Syntax object');
});
});
9 changes: 4 additions & 5 deletions lib/cli.js
Expand Up @@ -8,7 +8,6 @@ const { isPlainObject } = require('./utils/validateTypes');

const checkInvalidCLIOptions = require('./utils/checkInvalidCLIOptions');
const getFormatterOptionsText = require('./utils/getFormatterOptionsText');
const getModulePath = require('./utils/getModulePath');
const getStdin = require('./utils/getStdin');
const printConfig = require('./printConfig');
const resolveFrom = require('resolve-from');
Expand Down Expand Up @@ -119,9 +118,9 @@ const meowOptions = {
--config-basedir
An absolute path to the directory that relative paths defining "extends"
and "plugins" are *relative to*. Only necessary if these values are
relative paths.
An absolute path to the directory that relative paths defining "extends",
"plugins", and "customSyntax" are *relative to*. Only necessary if these
values are relative paths.
--print-config
Expand Down Expand Up @@ -387,7 +386,7 @@ module.exports = async (argv) => {
}

if (cli.flags.customSyntax) {
optionsBase.customSyntax = getModulePath(process.cwd(), cli.flags.customSyntax);
optionsBase.customSyntax = cli.flags.customSyntax;
}

if (cli.flags.config) {
Expand Down
32 changes: 18 additions & 14 deletions lib/getPostcssResult.js
Expand Up @@ -5,6 +5,8 @@ const path = require('path');
const { default: postcss } = require('postcss');
const { promises: fs } = require('fs');

const getModulePath = require('./utils/getModulePath');

/** @typedef {import('postcss').Result} Result */
/** @typedef {import('postcss').Syntax} Syntax */
/** @typedef {import('stylelint').CustomSyntax} CustomSyntax */
Expand Down Expand Up @@ -38,7 +40,7 @@ module.exports = async function getPostcssResult(stylelint, options = {}) {
}

const syntax = options.customSyntax
? getCustomSyntax(options.customSyntax)
? getCustomSyntax(options.customSyntax, stylelint._options.configBasedir)
: cssSyntax(stylelint, options.filePath);

const postcssOptions = {
Expand Down Expand Up @@ -85,21 +87,25 @@ module.exports = async function getPostcssResult(stylelint, options = {}) {

/**
* @param {CustomSyntax} customSyntax
* @param {string | undefined} basedir
* @returns {Syntax}
*/
function getCustomSyntax(customSyntax) {
let resolved;

function getCustomSyntax(customSyntax, basedir) {
if (typeof customSyntax === 'string') {
const customSyntaxLookup = basedir ? getModulePath(basedir, customSyntax) : customSyntax;

let resolved;

try {
resolved = require(customSyntax);
resolved = require(customSyntaxLookup);
} catch (error) {
if (
error &&
typeof error === 'object' &&
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
'code' in error &&
error.code === 'MODULE_NOT_FOUND' &&
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
'message' in error &&
typeof error.message === 'string' &&
error.message.includes(customSyntax)
) {
throw new Error(
Expand All @@ -126,17 +132,15 @@ function getCustomSyntax(customSyntax) {

if (typeof customSyntax === 'object') {
if (typeof customSyntax.parse === 'function') {
resolved = { ...customSyntax };
} else {
throw new TypeError(
`An object provided to the "customSyntax" option must have a "parse" property. Ensure the "parse" property exists and its value is a function.`,
);
return { ...customSyntax };
}

return resolved;
throw new TypeError(
'An object provided to the "customSyntax" option must have a "parse" property. Ensure the "parse" property exists and its value is a function.',
);
}

throw new Error(`Custom syntax must be a string or a Syntax object`);
throw new Error('Custom syntax must be a string or a Syntax object');
}

/** @type {{ [key: string]: string }} */
Expand Down
4 changes: 3 additions & 1 deletion lib/utils/getModulePath.js
Expand Up @@ -25,7 +25,9 @@ module.exports = function getModulePath(basedir, lookup, cwd = process.cwd()) {
}

if (!path) {
throw configurationError(`Could not find "${lookup}". Do you need a \`configBasedir\`?`);
throw configurationError(
`Could not find "${lookup}". Do you need the "configBasedir" or "--config-basedir" option?`,
);
}

return path;
Expand Down

0 comments on commit c0db3fd

Please sign in to comment.