Skip to content

Commit

Permalink
Refactor the internal API (#6415)
Browse files Browse the repository at this point in the history
This refactoring aims to make the codebase around the internal API more consistent.

Our codebase is mostly FP style, so the OOP-like code using `.bind()` seems inconsistent to me.

Also, the dependency between the internal modules seems implicit through `.bind()`.
  • Loading branch information
ybiquitous committed Oct 17, 2022
1 parent d402889 commit cb425cb
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 110 deletions.
41 changes: 0 additions & 41 deletions lib/__tests__/createLinter.test.js

This file was deleted.

26 changes: 26 additions & 0 deletions lib/__tests__/getConfigForFile.test.js
@@ -0,0 +1,26 @@
'use strict';

const path = require('path');

const createStylelint = require('../createStylelint');
const getConfigForFile = require('../getConfigForFile');
const pluginWarnAboutFoo = require('./fixtures/plugin-warn-about-foo');

test('augmented config loads', async () => {
const stylelint = createStylelint();
const filepath = path.join(__dirname, 'fixtures/getConfigForFile/a/b/foo.css');

await expect(getConfigForFile(stylelint, filepath)).resolves.toEqual({
config: {
plugins: [path.join(__dirname, '/fixtures/plugin-warn-about-foo.js')],
rules: {
'block-no-empty': [true],
'plugin/warn-about-foo': ['always'],
},
pluginFunctions: {
'plugin/warn-about-foo': pluginWarnAboutFoo.rule,
},
},
filepath: path.join(__dirname, 'fixtures/getConfigForFile/a/.stylelintrc'),
});
});
21 changes: 21 additions & 0 deletions lib/__tests__/isPathIgnored.test.js
@@ -0,0 +1,21 @@
'use strict';

const createStylelint = require('../createStylelint');
const isPathIgnored = require('../isPathIgnored');

test('isPathIgnored()', async () => {
const config = {
ignoreFiles: ['**/*.css', '!**/invalid-hex.css'],
rules: { 'block-no-empty': true },
};
const stylelint = createStylelint({ config });

await expect(
Promise.all([
isPathIgnored(stylelint, 'a.css'),
isPathIgnored(stylelint, 'foo/bar/baz.css'),
isPathIgnored(stylelint, 'foo/bar/baz.scss'),
isPathIgnored(stylelint, 'foo/invalid-hex.css'),
]),
).resolves.toEqual([true, true, false, false]);
});
53 changes: 17 additions & 36 deletions lib/createStylelint.js
@@ -1,13 +1,9 @@
'use strict';

const { cosmiconfig } = require('cosmiconfig');

const augmentConfig = require('./augmentConfig');
const createStylelintResult = require('./createStylelintResult');
const getConfigForFile = require('./getConfigForFile');
const getPostcssResult = require('./getPostcssResult');
const isPathIgnored = require('./isPathIgnored');
const lintSource = require('./lintSource');
const FileCache = require('./utils/FileCache');
const { cosmiconfig } = require('cosmiconfig');

const IS_TEST = process.env.NODE_ENV === 'test';
const STOP_DIR = IS_TEST ? process.cwd() : undefined;
Expand All @@ -19,36 +15,21 @@ const STOP_DIR = IS_TEST ? process.cwd() : undefined;
* so that methods on a stylelint instance can invoke
* each other while sharing options and caches.
*
* @param {import('stylelint').LinterOptions} options
* @returns {StylelintInternalApi}
* @type {import('stylelint')['createLinter']}
*/
function createStylelint(options = {}) {
module.exports = function createStylelint(options = {}) {
const cwd = options.cwd || process.cwd();

/** @type {StylelintInternalApi} */
// @ts-expect-error -- TS2740: Type '{ _options: LinterOptions; }' is missing the following properties from type 'InternalApi'
const stylelint = { _options: { ...options, cwd } };

stylelint._extendExplorer = cosmiconfig('', {
transform: augmentConfig.augmentConfigExtended(cwd),
stopDir: STOP_DIR,
});

stylelint._specifiedConfigCache = new Map();
stylelint._postcssResultCache = new Map();
stylelint._fileCache = new FileCache(
stylelint._options.cacheLocation,
stylelint._options.cacheStrategy,
stylelint._options.cwd,
);
stylelint._createStylelintResult = createStylelintResult.bind(null, stylelint);
stylelint._getPostcssResult = getPostcssResult.bind(null, stylelint);
stylelint._lintSource = lintSource.bind(null, stylelint);

stylelint.getConfigForFile = getConfigForFile.bind(null, stylelint);
stylelint.isPathIgnored = isPathIgnored.bind(null, stylelint);

return stylelint;
}

module.exports = /** @type {typeof import('stylelint').createLinter} */ (createStylelint);
return {
_options: { ...options, cwd },

_extendExplorer: cosmiconfig('', {
transform: augmentConfig.augmentConfigExtended(cwd),
stopDir: STOP_DIR,
}),

_specifiedConfigCache: new Map(),
_postcssResultCache: new Map(),
_fileCache: new FileCache(options.cacheLocation, options.cacheStrategy, cwd),
};
};
3 changes: 2 additions & 1 deletion lib/createStylelintResult.js
@@ -1,6 +1,7 @@
'use strict';

const createPartialStylelintResult = require('./createPartialStylelintResult');
const getConfigForFile = require('./getConfigForFile');

/** @typedef {import('stylelint').PostcssResult} PostcssResult */
/** @typedef {import('stylelint').LintResult} StylelintResult */
Expand All @@ -20,7 +21,7 @@ module.exports = async function createStylelintResult(
) {
let stylelintResult = createPartialStylelintResult(postcssResult, cssSyntaxError);

const configForFile = await stylelint.getConfigForFile(filePath, filePath);
const configForFile = await getConfigForFile(stylelint, filePath, filePath);

const config = configForFile === null ? {} : configForFile.config;
const file = stylelintResult.source || (cssSyntaxError && cssSyntaxError.file);
Expand Down
12 changes: 6 additions & 6 deletions lib/isPathIgnored.js
@@ -1,11 +1,13 @@
'use strict';

const filterFilePaths = require('./utils/filterFilePaths');
const getFileIgnorer = require('./utils/getFileIgnorer');
const micromatch = require('micromatch');
const normalizePath = require('normalize-path');
const path = require('path');

const filterFilePaths = require('./utils/filterFilePaths');
const getConfigForFile = require('./getConfigForFile');
const getFileIgnorer = require('./utils/getFileIgnorer');

/**
* To find out if a path is ignored, we need to load the config,
* which may have an ignoreFiles property. We then check the path
Expand All @@ -22,16 +24,14 @@ module.exports = async function isPathIgnored(stylelint, filePath) {
const cwd = stylelint._options.cwd;
const ignorer = getFileIgnorer(stylelint._options);

const result = await stylelint.getConfigForFile(filePath, filePath);
const result = await getConfigForFile(stylelint, filePath, filePath);

if (!result) {
return true;
}

// Glob patterns for micromatch should be in POSIX-style
const ignoreFiles = /** @type {Array<string>} */ (result.config.ignoreFiles || []).map((s) =>
normalizePath(s),
);
const ignoreFiles = [result.config.ignoreFiles || []].flat().map((s) => normalizePath(s));

const absoluteFilePath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);

Expand Down
24 changes: 16 additions & 8 deletions lib/lintSource.js
@@ -1,8 +1,12 @@
'use strict';

const path = require('path');

const getConfigForFile = require('./getConfigForFile');
const getPostcssResult = require('./getPostcssResult');
const isPathIgnored = require('./isPathIgnored');
const isPathNotFoundError = require('./utils/isPathNotFoundError');
const lintPostcssResult = require('./lintPostcssResult');
const path = require('path');

/** @typedef {import('stylelint').InternalApi} StylelintInternalApi */
/** @typedef {import('stylelint').GetLintSourceOptions} Options */
Expand Down Expand Up @@ -34,7 +38,7 @@ module.exports = async function lintSource(stylelint, options = {}) {
return Promise.reject(new Error('filePath must be an absolute path'));
}

const isIgnored = await stylelint.isPathIgnored(inputFilePath).catch((err) => {
const isIgnored = await isPathIgnored(stylelint, inputFilePath).catch((err) => {
if (isCodeNotFile && isPathNotFoundError(err)) return false;

throw err;
Expand All @@ -51,13 +55,17 @@ module.exports = async function lintSource(stylelint, options = {}) {
const configSearchPath = stylelint._options.configFile || inputFilePath;
const cwd = stylelint._options.cwd;

const configForFile = await stylelint
.getConfigForFile(configSearchPath, inputFilePath)
.catch((err) => {
if (isCodeNotFile && isPathNotFoundError(err)) return stylelint.getConfigForFile(cwd);
let configForFile;

try {
configForFile = await getConfigForFile(stylelint, configSearchPath, inputFilePath);
} catch (err) {
if (isCodeNotFile && isPathNotFoundError(err)) {
configForFile = await getConfigForFile(stylelint, cwd);
} else {
throw err;
});
}
}

if (!configForFile) {
return Promise.reject(new Error('Config file not found'));
Expand Down Expand Up @@ -88,7 +96,7 @@ module.exports = async function lintSource(stylelint, options = {}) {

const postcssResult =
existingPostcssResult ||
(await stylelint._getPostcssResult({
(await getPostcssResult(stylelint, {
code: options.code,
codeFilename: options.codeFilename,
filePath: inputFilePath,
Expand Down
6 changes: 4 additions & 2 deletions lib/postcssPlugin.js
@@ -1,8 +1,10 @@
'use strict';

const createStylelint = require('./createStylelint');
const path = require('path');

const createStylelint = require('./createStylelint');
const lintSource = require('./lintSource');

/** @typedef {import('stylelint').PostcssPluginOptions} PostcssPluginOptions */
/** @typedef {import('stylelint').Config} StylelintConfig */

Expand All @@ -24,7 +26,7 @@ module.exports = (options = {}) => {
filePath = path.join(cwd, filePath);
}

return stylelint._lintSource({
return lintSource(stylelint, {
filePath,
existingPostcssResult: result,
});
Expand Down
6 changes: 4 additions & 2 deletions lib/resolveConfig.js
@@ -1,8 +1,10 @@
'use strict';

const createStylelint = require('./createStylelint');
const path = require('path');

const createStylelint = require('./createStylelint');
const getConfigForFile = require('./getConfigForFile');

/**
* Resolves the effective configuration for a given file. Resolves to `undefined`
* if no config is found.
Expand Down Expand Up @@ -37,7 +39,7 @@ module.exports = async function resolveConfig(

const configSearchPath = stylelint._options.configFile || absoluteFilePath;

const resolved = await stylelint.getConfigForFile(configSearchPath, absoluteFilePath);
const resolved = await getConfigForFile(stylelint, configSearchPath, absoluteFilePath);

if (!resolved) {
return undefined;
Expand Down
9 changes: 5 additions & 4 deletions lib/standalone.js
Expand Up @@ -13,6 +13,7 @@ const filterFilePaths = require('./utils/filterFilePaths');
const formatters = require('./formatters');
const getFileIgnorer = require('./utils/getFileIgnorer');
const getFormatterOptionsText = require('./utils/getFormatterOptionsText');
const lintSource = require('./lintSource');
const NoFilesFoundError = require('./utils/noFilesFoundError');
const AllFilesIgnoredError = require('./utils/allFilesIgnoredError');
const { assert } = require('./utils/validateTypes');
Expand Down Expand Up @@ -125,12 +126,12 @@ async function standalone({
let stylelintResult;

try {
const postcssResult = await stylelint._lintSource({
const postcssResult = await lintSource(stylelint, {
code,
codeFilename: absoluteCodeFilename,
});

stylelintResult = await stylelint._createStylelintResult(postcssResult, absoluteCodeFilename);
stylelintResult = await createStylelintResult(stylelint, postcssResult, absoluteCodeFilename);
} catch (error) {
stylelintResult = await handleError(stylelint, error);
}
Expand Down Expand Up @@ -211,7 +212,7 @@ async function standalone({
debug(`Processing ${absoluteFilepath}`);

try {
const postcssResult = await stylelint._lintSource({
const postcssResult = await lintSource(stylelint, {
filePath: absoluteFilepath,
cache: useCache,
});
Expand Down Expand Up @@ -245,7 +246,7 @@ async function standalone({
}
}

return stylelint._createStylelintResult(postcssResult, absoluteFilepath);
return createStylelintResult(stylelint, postcssResult, absoluteFilepath);
} catch (error) {
// On any error, we should not cache the lint result
stylelint._fileCache.removeEntry(absoluteFilepath);
Expand Down
10 changes: 0 additions & 10 deletions types/stylelint/index.d.ts
Expand Up @@ -529,16 +529,6 @@ declare module 'stylelint' {
_specifiedConfigCache: Map<Config, Promise<CosmiconfigResult>>;
_postcssResultCache: Map<string, PostCSS.Result>;
_fileCache: FileCache;

_getPostcssResult: (options?: GetPostcssOptions) => Promise<PostCSS.Result>;
_lintSource: (options: GetLintSourceOptions) => Promise<PostcssResult>;
_createStylelintResult: (
postcssResult: PostcssResult,
filePath?: string,
) => Promise<LintResult>;

getConfigForFile: (searchPath?: string, filePath?: string) => Promise<CosmiconfigResult>;
isPathIgnored: (s?: string) => Promise<boolean>;
};

export type DisableOptionsReport = DisableReportEntry[];
Expand Down

0 comments on commit cb425cb

Please sign in to comment.