From e21173314dcf708fe9551a7ff3b52a24c3cbff41 Mon Sep 17 00:00:00 2001 From: kimulaco Date: Wed, 21 Sep 2022 00:17:41 +0900 Subject: [PATCH 01/12] feat: Change function to calculate hash --- lib/__tests__/standalone-cache.test.js | 36 +++++++++++++++++++++----- lib/lintSource.js | 20 ++++++++++++++ lib/standalone.js | 24 ++++++----------- lib/utils/FileCache.js | 7 +++++ types/stylelint/index.d.ts | 3 +++ 5 files changed, 68 insertions(+), 22 deletions(-) diff --git a/lib/__tests__/standalone-cache.test.js b/lib/__tests__/standalone-cache.test.js index 156519e61e..0af12365d7 100644 --- a/lib/__tests__/standalone-cache.test.js +++ b/lib/__tests__/standalone-cache.test.js @@ -74,8 +74,16 @@ describe('standalone cache', () => { const { results } = await standalone(getConfig()); // Ensure only changed files are linted - expect(results.some((file) => file.source === validFile)).toBe(false); - expect(results.some((file) => file.source === newFileDest)).toBe(true); + expect( + results.some((file) => { + return file.source === validFile && !file.ignored; + }), + ).toBe(false); + expect( + results.some((file) => { + return file.source === newFileDest && !file.ignored; + }), + ).toBe(true); const { cache } = fCache.createFromFile(expectedCacheFilePath); @@ -98,8 +106,16 @@ describe('standalone cache', () => { ); // Ensure all files are re-linted - expect(results.some((file) => file.source === validFile)).toBe(true); - expect(results.some((file) => file.source === newFileDest)).toBe(true); + expect( + results.some((file) => { + return file.source === validFile && !file.ignored; + }), + ).toBe(true); + expect( + results.some((file) => { + return file.source === newFileDest && !file.ignored; + }), + ).toBe(true); }); it('invalid files are not cached', async () => { @@ -111,8 +127,16 @@ describe('standalone cache', () => { expect(errored).toBe(true); // Ensure only changed files are linted - expect(results.some((file) => file.source === validFile)).toBe(false); - expect(results.some((file) => file.source === newFileDest)).toBe(true); + expect( + results.some((file) => { + return file.source === validFile && !file.ignored; + }), + ).toBe(false); + expect( + results.some((file) => { + return file.source === newFileDest && !file.ignored; + }), + ).toBe(true); const { cache } = fCache.createFromFile(expectedCacheFilePath); diff --git a/lib/lintSource.js b/lib/lintSource.js index d45d71e20a..943e6d7dc1 100644 --- a/lib/lintSource.js +++ b/lib/lintSource.js @@ -2,6 +2,8 @@ const isPathNotFoundError = require('./utils/isPathNotFoundError'); const lintPostcssResult = require('./lintPostcssResult'); +const pkg = require('../package.json'); +const hash = require('./utils/hash'); const path = require('path'); /** @typedef {import('stylelint').InternalApi} StylelintInternalApi */ @@ -65,6 +67,22 @@ module.exports = async function lintSource(stylelint, options = {}) { const config = configForFile.config; const existingPostcssResult = options.existingPostcssResult; + const fileCache = options.fileCache; + + if (options.cache && fileCache) { + const stylelintVersion = pkg.version; + const hashOfConfig = hash(`${stylelintVersion}_${JSON.stringify(config || {})}`); + + fileCache.hashOfConfig = hashOfConfig; + + if (!fileCache.hasFileChanged(options.filePath)) { + return options.existingPostcssResult + ? Object.assign(options.existingPostcssResult, { + stylelint: createEmptyStylelintPostcssResult(), + }) + : createEmptyPostcssResult(inputFilePath); + } + } /** @type {StylelintPostcssResult} */ const stylelintResult = { @@ -72,6 +90,7 @@ module.exports = async function lintSource(stylelint, options = {}) { customMessages: {}, ruleMetadata: {}, disabledRanges: {}, + fileCache, }; const postcssResult = @@ -104,6 +123,7 @@ function createEmptyStylelintPostcssResult() { disabledRanges: {}, ignored: true, stylelintError: false, + fileCache: undefined, }; } diff --git a/lib/standalone.js b/lib/standalone.js index 6d4e45067f..bfb2f8e867 100644 --- a/lib/standalone.js +++ b/lib/standalone.js @@ -14,11 +14,9 @@ const filterFilePaths = require('./utils/filterFilePaths'); const formatters = require('./formatters'); const getFileIgnorer = require('./utils/getFileIgnorer'); const getFormatterOptionsText = require('./utils/getFormatterOptionsText'); -const hash = require('./utils/hash'); const NoFilesFoundError = require('./utils/noFilesFoundError'); const AllFilesIgnoredError = require('./utils/allFilesIgnoredError'); const { assert } = require('./utils/validateTypes'); -const pkg = require('../package.json'); const prepareReturnValue = require('./prepareReturnValue'); const ALWAYS_IGNORED_GLOBS = ['**/node_modules/**']; @@ -62,7 +60,7 @@ async function standalone({ syntax, }) { /** @type {FileCache} */ - let fileCache; + let fileCache = new FileCache(cacheLocation, cwd); const startTime = Date.now(); const isValidCode = typeof code === 'string'; @@ -175,15 +173,7 @@ async function standalone({ fileList = fileList.concat(ALWAYS_IGNORED_GLOBS.map((glob) => `!${glob}`)); } - if (useCache) { - const stylelintVersion = pkg.version; - const hashOfConfig = hash(`${stylelintVersion}_${JSON.stringify(config || {})}`); - - fileCache = new FileCache(cacheLocation, cwd, hashOfConfig); - } else { - // No need to calculate hash here, we just want to delete cache file. - fileCache = new FileCache(cacheLocation, cwd); - // Remove cache file if cache option is disabled + if (!useCache) { fileCache.destroy(); } @@ -217,18 +207,20 @@ async function standalone({ return absoluteFilepath; }); - if (useCache) { - absoluteFilePaths = absoluteFilePaths.filter(fileCache.hasFileChanged.bind(fileCache)); - } - const getStylelintResults = absoluteFilePaths.map(async (absoluteFilepath) => { debug(`Processing ${absoluteFilepath}`); try { const postcssResult = await stylelint._lintSource({ filePath: absoluteFilepath, + cache: useCache, + fileCache, }); + if (postcssResult.stylelint.fileCache) { + fileCache = postcssResult.stylelint.fileCache; + } + if (postcssResult.stylelint.stylelintError && useCache) { debug(`${absoluteFilepath} contains linting errors and will not be cached.`); fileCache.removeEntry(absoluteFilepath); diff --git a/lib/utils/FileCache.js b/lib/utils/FileCache.js index 698b3105f1..d0e44c7a69 100644 --- a/lib/utils/FileCache.js +++ b/lib/utils/FileCache.js @@ -28,6 +28,13 @@ class FileCache { this._hashOfConfig = hashOfConfig; } + /** + * @param {string} hashOfConfig + */ + set hashOfConfig(hashOfConfig) { + this._hashOfConfig = hashOfConfig; + } + /** * @param {string} absoluteFilepath * @return {boolean} diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index f70fd7afa8..5464229976 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -99,6 +99,7 @@ declare module 'stylelint' { disableWritingFix?: boolean; config?: Config; ruleDisableFix?: boolean; + fileCache?: FileCache; }; type EmptyResult = { @@ -205,6 +206,8 @@ declare module 'stylelint' { export type GetLintSourceOptions = GetPostcssOptions & { existingPostcssResult?: PostCSS.Result; + cache?: boolean; + fileCache?: FileCache; }; export type LinterOptions = { From 98d488ed7f0110201a9a6d8e8f018a739817920c Mon Sep 17 00:00:00 2001 From: kimulaco Date: Wed, 21 Sep 2022 01:25:27 +0900 Subject: [PATCH 02/12] test: Add cli-cache test --- lib/__tests__/cli-cache.test.js | 75 ++++++++++++++++++++++++++ lib/__tests__/standalone-cache.test.js | 40 ++++---------- 2 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 lib/__tests__/cli-cache.test.js diff --git a/lib/__tests__/cli-cache.test.js b/lib/__tests__/cli-cache.test.js new file mode 100644 index 0000000000..6a88031bda --- /dev/null +++ b/lib/__tests__/cli-cache.test.js @@ -0,0 +1,75 @@ +'use strict'; + +const path = require('path'); +const { promises: fs, existsSync } = require('fs'); + +const cli = require('../cli'); +const replaceBackslashes = require('../testUtils/replaceBackslashes'); + +const fixturesPath = (...elems) => replaceBackslashes(path.join(__dirname, 'fixtures', ...elems)); + +const removeFile = async (filePath) => { + if (existsSync(filePath)) { + await fs.unlink(filePath); + } +}; + +jest.mock('../utils/getStdin', () => () => Promise.resolve('')); + +describe('buildCLI', () => { + beforeAll(() => { + jest.spyOn(process, 'exit').mockImplementation(() => {}); + jest.spyOn(process.stdout, 'write').mockImplementation(() => {}); + jest.spyOn(process.stderr, 'write').mockImplementation(() => {}); + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + afterEach(async () => { + await removeFile(fixturesPath('.stylelintcache')); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('--cache with same warning', async () => { + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('default-severity-warning.json'), + fixturesPath('empty-block.css'), + ]); + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('default-severity-warning.json'), + fixturesPath('empty-block.css'), + ]); + expect(process.stdout.write).toHaveBeenCalledTimes(1); + }); + + it('--cache with different warning', async () => { + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('default-severity-warning.json'), + fixturesPath('empty-block.css'), + ]); + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('config-block-no-empty.json'), + fixturesPath('empty-block.css'), + ]); + expect(process.stdout.write).toHaveBeenCalledTimes(2); + }); +}); diff --git a/lib/__tests__/standalone-cache.test.js b/lib/__tests__/standalone-cache.test.js index 0af12365d7..8d65b84f7f 100644 --- a/lib/__tests__/standalone-cache.test.js +++ b/lib/__tests__/standalone-cache.test.js @@ -16,6 +16,10 @@ const removeFile = async (filePath) => { } }; +const isChanged = (file, targetFilePath) => { + return file.source === targetFilePath && !file.ignored; +}; + const cwd = process.cwd(); const fixturesPath = path.join(__dirname, 'fixtures'); const invalidFile = path.join(fixturesPath, 'empty-block.css'); @@ -74,16 +78,8 @@ describe('standalone cache', () => { const { results } = await standalone(getConfig()); // Ensure only changed files are linted - expect( - results.some((file) => { - return file.source === validFile && !file.ignored; - }), - ).toBe(false); - expect( - results.some((file) => { - return file.source === newFileDest && !file.ignored; - }), - ).toBe(true); + expect(results.some((file) => isChanged(file, validFile))).toBe(false); + expect(results.some((file) => isChanged(file, newFileDest))).toBe(true); const { cache } = fCache.createFromFile(expectedCacheFilePath); @@ -106,16 +102,8 @@ describe('standalone cache', () => { ); // Ensure all files are re-linted - expect( - results.some((file) => { - return file.source === validFile && !file.ignored; - }), - ).toBe(true); - expect( - results.some((file) => { - return file.source === newFileDest && !file.ignored; - }), - ).toBe(true); + expect(results.some((file) => isChanged(file, validFile))).toBe(true); + expect(results.some((file) => isChanged(file, newFileDest))).toBe(true); }); it('invalid files are not cached', async () => { @@ -127,16 +115,8 @@ describe('standalone cache', () => { expect(errored).toBe(true); // Ensure only changed files are linted - expect( - results.some((file) => { - return file.source === validFile && !file.ignored; - }), - ).toBe(false); - expect( - results.some((file) => { - return file.source === newFileDest && !file.ignored; - }), - ).toBe(true); + expect(results.some((file) => isChanged(file, validFile))).toBe(false); + expect(results.some((file) => isChanged(file, newFileDest))).toBe(true); const { cache } = fCache.createFromFile(expectedCacheFilePath); From 148b9bfaf4f8ccd5ac7cfb102a38821cf45567e2 Mon Sep 17 00:00:00 2001 From: kimulaco Date: Wed, 21 Sep 2022 07:35:11 +0900 Subject: [PATCH 03/12] test: merge cli test files --- lib/__tests__/cli-cache.test.js | 75 --------------------------------- lib/__tests__/cli.test.js | 73 ++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 75 deletions(-) delete mode 100644 lib/__tests__/cli-cache.test.js diff --git a/lib/__tests__/cli-cache.test.js b/lib/__tests__/cli-cache.test.js deleted file mode 100644 index 6a88031bda..0000000000 --- a/lib/__tests__/cli-cache.test.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict'; - -const path = require('path'); -const { promises: fs, existsSync } = require('fs'); - -const cli = require('../cli'); -const replaceBackslashes = require('../testUtils/replaceBackslashes'); - -const fixturesPath = (...elems) => replaceBackslashes(path.join(__dirname, 'fixtures', ...elems)); - -const removeFile = async (filePath) => { - if (existsSync(filePath)) { - await fs.unlink(filePath); - } -}; - -jest.mock('../utils/getStdin', () => () => Promise.resolve('')); - -describe('buildCLI', () => { - beforeAll(() => { - jest.spyOn(process, 'exit').mockImplementation(() => {}); - jest.spyOn(process.stdout, 'write').mockImplementation(() => {}); - jest.spyOn(process.stderr, 'write').mockImplementation(() => {}); - jest.spyOn(console, 'log').mockImplementation(() => {}); - jest.spyOn(console, 'error').mockImplementation(() => {}); - }); - - afterEach(async () => { - await removeFile(fixturesPath('.stylelintcache')); - }); - - afterAll(() => { - jest.restoreAllMocks(); - }); - - it('--cache with same warning', async () => { - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('default-severity-warning.json'), - fixturesPath('empty-block.css'), - ]); - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('default-severity-warning.json'), - fixturesPath('empty-block.css'), - ]); - expect(process.stdout.write).toHaveBeenCalledTimes(1); - }); - - it('--cache with different warning', async () => { - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('default-severity-warning.json'), - fixturesPath('empty-block.css'), - ]); - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('config-block-no-empty.json'), - fixturesPath('empty-block.css'), - ]); - expect(process.stdout.write).toHaveBeenCalledTimes(2); - }); -}); diff --git a/lib/__tests__/cli.test.js b/lib/__tests__/cli.test.js index 373489384c..3c27a58601 100644 --- a/lib/__tests__/cli.test.js +++ b/lib/__tests__/cli.test.js @@ -3,6 +3,7 @@ 'use strict'; const path = require('path'); +const { promises: fs, existsSync } = require('fs'); const stripAnsi = require('strip-ansi'); const cli = require('../cli'); @@ -12,6 +13,12 @@ const replaceBackslashes = require('../testUtils/replaceBackslashes'); const fixturesPath = (...elems) => replaceBackslashes(path.join(__dirname, 'fixtures', ...elems)); const { buildCLI } = cli; +const removeFile = async (filePath) => { + if (existsSync(filePath)) { + await fs.unlink(filePath); + } +}; + jest.mock('../utils/getStdin', () => () => Promise.resolve('')); describe('buildCLI', () => { @@ -369,4 +376,70 @@ describe('CLI', () => { expect(output).toBe('Custom formatter reports 1 warning(s).'); }); + + it('--cache same warning config', async () => { + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('default-severity-warning.json'), + fixturesPath('empty-block.css'), + ]); + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('default-severity-warning.json'), + fixturesPath('empty-block.css'), + ]); + expect(process.stdout.write).toHaveBeenCalledTimes(1); + + await removeFile(fixturesPath('.stylelintcache')); + }); + + it('--cache different error config', async () => { + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('default-severity-warning.json'), + fixturesPath('empty-block.css'), + ]); + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('config-block-no-empty.json'), + fixturesPath('empty-block.css'), + ]); + expect(process.stdout.write).toHaveBeenCalledTimes(2); + + await removeFile(fixturesPath('.stylelintcache')); + }); + + it('--cache same error config', async () => { + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('config-block-no-empty.json'), + fixturesPath('empty-block.css'), + ]); + await cli([ + '--cache', + '--cache-location', + fixturesPath('.stylelintcache'), + '--config', + fixturesPath('config-block-no-empty.json'), + fixturesPath('empty-block.css'), + ]); + expect(process.stdout.write).toHaveBeenCalledTimes(2); + + await removeFile(fixturesPath('.stylelintcache')); + }); }); From 3efe984faea2527eb46fde96f3e7ba1678e3af29 Mon Sep 17 00:00:00 2001 From: kimulaco Date: Thu, 22 Sep 2022 07:24:38 +0900 Subject: [PATCH 04/12] fix: Fix type fileCache --- types/stylelint/index.d.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index 5464229976..c11a061cb7 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -2,6 +2,7 @@ declare module 'stylelint' { import type * as PostCSS from 'postcss'; import type { GlobbyOptions } from 'globby'; import type { cosmiconfig } from 'cosmiconfig'; + import * as FileCache from '../../lib/utils/FileCache'; namespace stylelint { export type Severity = 'warning' | 'error'; @@ -99,7 +100,7 @@ declare module 'stylelint' { disableWritingFix?: boolean; config?: Config; ruleDisableFix?: boolean; - fileCache?: FileCache; + fileCache?: InstanceType; }; type EmptyResult = { @@ -207,7 +208,7 @@ declare module 'stylelint' { export type GetLintSourceOptions = GetPostcssOptions & { existingPostcssResult?: PostCSS.Result; cache?: boolean; - fileCache?: FileCache; + fileCache?: InstanceType; }; export type LinterOptions = { From 2485e8e5e337fb0d27269b633a29dcc7c5fdec73 Mon Sep 17 00:00:00 2001 From: kimulaco Date: Thu, 22 Sep 2022 07:37:52 +0900 Subject: [PATCH 05/12] Revert "fix: Fix type fileCache" This reverts commit 3efe984faea2527eb46fde96f3e7ba1678e3af29. --- types/stylelint/index.d.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index c11a061cb7..5464229976 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -2,7 +2,6 @@ declare module 'stylelint' { import type * as PostCSS from 'postcss'; import type { GlobbyOptions } from 'globby'; import type { cosmiconfig } from 'cosmiconfig'; - import * as FileCache from '../../lib/utils/FileCache'; namespace stylelint { export type Severity = 'warning' | 'error'; @@ -100,7 +99,7 @@ declare module 'stylelint' { disableWritingFix?: boolean; config?: Config; ruleDisableFix?: boolean; - fileCache?: InstanceType; + fileCache?: FileCache; }; type EmptyResult = { @@ -208,7 +207,7 @@ declare module 'stylelint' { export type GetLintSourceOptions = GetPostcssOptions & { existingPostcssResult?: PostCSS.Result; cache?: boolean; - fileCache?: InstanceType; + fileCache?: FileCache; }; export type LinterOptions = { From d2035ef460c3727e3d9285fcc9878521498139dd Mon Sep 17 00:00:00 2001 From: kimulaco Date: Thu, 22 Sep 2022 07:50:37 +0900 Subject: [PATCH 06/12] fix: Add type FileCache --- lib/lintSource.js | 2 +- types/stylelint/index.d.ts | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/lintSource.js b/lib/lintSource.js index 943e6d7dc1..8963d55c7e 100644 --- a/lib/lintSource.js +++ b/lib/lintSource.js @@ -75,7 +75,7 @@ module.exports = async function lintSource(stylelint, options = {}) { fileCache.hashOfConfig = hashOfConfig; - if (!fileCache.hasFileChanged(options.filePath)) { + if (options.filePath && !fileCache.hasFileChanged(options.filePath)) { return options.existingPostcssResult ? Object.assign(options.existingPostcssResult, { stylelint: createEmptyStylelintPostcssResult(), diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index 82a3725256..24b4e74180 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -2,6 +2,7 @@ declare module 'stylelint' { import type * as PostCSS from 'postcss'; import type { GlobbyOptions } from 'globby'; import type { cosmiconfig } from 'cosmiconfig'; + import type * as fileEntryCache from 'file-entry-cache'; namespace stylelint { export type Severity = 'warning' | 'error'; @@ -87,6 +88,16 @@ declare module 'stylelint' { export type DisabledWarning = { line: number; rule: string }; + type FileCache = { + _fileCache: fileEntryCache.FileEntryCache; + _hashOfConfig: string; + hashOfConfig: string; + hasFileChanged: (absoluteFilepath: string) => boolean; + reconcile: () => void; + destroy: () => void; + removeEntry: (absoluteFilepath: string) => void; + }; + export type StylelintPostcssResult = { ruleSeverities: { [ruleName: string]: Severity }; customMessages: { [ruleName: string]: RuleMessage }; From fb73a9ba305893a73f7acbce2b79b77db0405a2e Mon Sep 17 00:00:00 2001 From: Richard Hallows Date: Thu, 22 Sep 2022 13:54:25 +0100 Subject: [PATCH 07/12] Create happy-oranges-invite.md --- .changeset/happy-oranges-invite.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/happy-oranges-invite.md diff --git a/.changeset/happy-oranges-invite.md b/.changeset/happy-oranges-invite.md new file mode 100644 index 0000000000..15b6fc6b66 --- /dev/null +++ b/.changeset/happy-oranges-invite.md @@ -0,0 +1,5 @@ +--- +"stylelint": patch +--- + +Fixed: cache refresh when config is changed From ca2fb534ba93e2dba40092945e6f56758c99a97e Mon Sep 17 00:00:00 2001 From: kimulaco Date: Fri, 23 Sep 2022 07:10:01 +0900 Subject: [PATCH 08/12] fix: Move fileCache to InternalApi --- lib/createStylelint.js | 2 ++ lib/lintSource.js | 16 +++++----------- lib/standalone.js | 17 +++++------------ lib/utils/FileCache.js | 20 ++++++++++---------- types/stylelint/index.d.ts | 5 ++--- 5 files changed, 24 insertions(+), 36 deletions(-) diff --git a/lib/createStylelint.js b/lib/createStylelint.js index ffe0a04582..e63a5aa6f8 100644 --- a/lib/createStylelint.js +++ b/lib/createStylelint.js @@ -6,6 +6,7 @@ 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'; @@ -35,6 +36,7 @@ function createStylelint(options = {}) { stylelint._specifiedConfigCache = new Map(); stylelint._postcssResultCache = new Map(); + stylelint._fileCache = new FileCache(stylelint._options.cacheLocation, stylelint._options.cwd); stylelint._createStylelintResult = createStylelintResult.bind(null, stylelint); stylelint._getPostcssResult = getPostcssResult.bind(null, stylelint); stylelint._lintSource = lintSource.bind(null, stylelint); diff --git a/lib/lintSource.js b/lib/lintSource.js index 8963d55c7e..09e09061e7 100644 --- a/lib/lintSource.js +++ b/lib/lintSource.js @@ -2,8 +2,6 @@ const isPathNotFoundError = require('./utils/isPathNotFoundError'); const lintPostcssResult = require('./lintPostcssResult'); -const pkg = require('../package.json'); -const hash = require('./utils/hash'); const path = require('path'); /** @typedef {import('stylelint').InternalApi} StylelintInternalApi */ @@ -67,15 +65,13 @@ module.exports = async function lintSource(stylelint, options = {}) { const config = configForFile.config; const existingPostcssResult = options.existingPostcssResult; - const fileCache = options.fileCache; - if (options.cache && fileCache) { - const stylelintVersion = pkg.version; - const hashOfConfig = hash(`${stylelintVersion}_${JSON.stringify(config || {})}`); - - fileCache.hashOfConfig = hashOfConfig; + if (options.cache) { + if (!stylelint._fileCache._hashOfConfig) { + stylelint._fileCache.calcHashOfConfig(config); + } - if (options.filePath && !fileCache.hasFileChanged(options.filePath)) { + if (options.filePath && !stylelint._fileCache.hasFileChanged(options.filePath)) { return options.existingPostcssResult ? Object.assign(options.existingPostcssResult, { stylelint: createEmptyStylelintPostcssResult(), @@ -90,7 +86,6 @@ module.exports = async function lintSource(stylelint, options = {}) { customMessages: {}, ruleMetadata: {}, disabledRanges: {}, - fileCache, }; const postcssResult = @@ -123,7 +118,6 @@ function createEmptyStylelintPostcssResult() { disabledRanges: {}, ignored: true, stylelintError: false, - fileCache: undefined, }; } diff --git a/lib/standalone.js b/lib/standalone.js index bfb2f8e867..b544d0fc2e 100644 --- a/lib/standalone.js +++ b/lib/standalone.js @@ -9,7 +9,6 @@ const path = require('path'); const createStylelint = require('./createStylelint'); const createStylelintResult = require('./createStylelintResult'); -const FileCache = require('./utils/FileCache'); const filterFilePaths = require('./utils/filterFilePaths'); const formatters = require('./formatters'); const getFileIgnorer = require('./utils/getFileIgnorer'); @@ -59,8 +58,6 @@ async function standalone({ reportNeedlessDisables, syntax, }) { - /** @type {FileCache} */ - let fileCache = new FileCache(cacheLocation, cwd); const startTime = Date.now(); const isValidCode = typeof code === 'string'; @@ -93,6 +90,7 @@ async function standalone({ } const stylelint = createStylelint({ + cacheLocation, config, configFile, configBasedir, @@ -174,7 +172,7 @@ async function standalone({ } if (!useCache) { - fileCache.destroy(); + stylelint._fileCache.destroy(); } const effectiveGlobbyOptions = { @@ -214,16 +212,11 @@ async function standalone({ const postcssResult = await stylelint._lintSource({ filePath: absoluteFilepath, cache: useCache, - fileCache, }); - if (postcssResult.stylelint.fileCache) { - fileCache = postcssResult.stylelint.fileCache; - } - if (postcssResult.stylelint.stylelintError && useCache) { debug(`${absoluteFilepath} contains linting errors and will not be cached.`); - fileCache.removeEntry(absoluteFilepath); + stylelint._fileCache.removeEntry(absoluteFilepath); } /** @@ -250,7 +243,7 @@ async function standalone({ return stylelint._createStylelintResult(postcssResult, absoluteFilepath); } catch (error) { // On any error, we should not cache the lint result - fileCache.removeEntry(absoluteFilepath); + stylelint._fileCache.removeEntry(absoluteFilepath); return handleError(stylelint, error, absoluteFilepath); } @@ -267,7 +260,7 @@ async function standalone({ } if (useCache) { - fileCache.reconcile(); + stylelint._fileCache.reconcile(); } const result = prepareReturnValue(stylelintResults, maxWarnings, formatterFunction, cwd); diff --git a/lib/utils/FileCache.js b/lib/utils/FileCache.js index d0e44c7a69..c1b53766d9 100644 --- a/lib/utils/FileCache.js +++ b/lib/utils/FileCache.js @@ -3,10 +3,11 @@ const debug = require('debug')('stylelint:file-cache'); const fileEntryCache = require('file-entry-cache'); const getCacheFile = require('./getCacheFile'); +const hash = require('./hash'); +const pkg = require('../../package.json'); const path = require('path'); const DEFAULT_CACHE_LOCATION = './.stylelintcache'; -const DEFAULT_HASH = ''; /** @typedef {import('file-entry-cache').FileDescriptor["meta"] & { hashOfConfig?: string }} CacheMetadata */ @@ -16,23 +17,22 @@ const DEFAULT_HASH = ''; * @constructor */ class FileCache { - constructor( - cacheLocation = DEFAULT_CACHE_LOCATION, - cwd = process.cwd(), - hashOfConfig = DEFAULT_HASH, - ) { + constructor(cacheLocation = DEFAULT_CACHE_LOCATION, cwd = process.cwd()) { const cacheFile = path.resolve(getCacheFile(cacheLocation, cwd)); debug(`Cache file is created at ${cacheFile}`); this._fileCache = fileEntryCache.create(cacheFile); - this._hashOfConfig = hashOfConfig; + this._hashOfConfig = ''; } /** - * @param {string} hashOfConfig + * @param {import('stylelint').Config} config */ - set hashOfConfig(hashOfConfig) { - this._hashOfConfig = hashOfConfig; + calcHashOfConfig(config) { + const stylelintVersion = pkg.version; + const configString = JSON.stringify(config || {}); + + this._hashOfConfig = hash(`${stylelintVersion}_${configString}`); } /** diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index 24b4e74180..bfd45e2bb8 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -91,7 +91,7 @@ declare module 'stylelint' { type FileCache = { _fileCache: fileEntryCache.FileEntryCache; _hashOfConfig: string; - hashOfConfig: string; + calcHashOfConfig: (config: Config) => void; hasFileChanged: (absoluteFilepath: string) => boolean; reconcile: () => void; destroy: () => void; @@ -110,7 +110,6 @@ declare module 'stylelint' { disableWritingFix?: boolean; config?: Config; ruleDisableFix?: boolean; - fileCache?: FileCache; }; type EmptyResult = { @@ -218,7 +217,6 @@ declare module 'stylelint' { export type GetLintSourceOptions = GetPostcssOptions & { existingPostcssResult?: PostCSS.Result; cache?: boolean; - fileCache?: FileCache; }; export type LinterOptions = { @@ -531,6 +529,7 @@ declare module 'stylelint' { _extendExplorer: ReturnType; _specifiedConfigCache: Map>; _postcssResultCache: Map; + _fileCache: FileCache; _getPostcssResult: (options?: GetPostcssOptions) => Promise; _lintSource: (options: GetLintSourceOptions) => Promise; From b9c8a00f7cf4abf58dafd3b0d0660703aa6c913d Mon Sep 17 00:00:00 2001 From: kimulaco Date: Sat, 24 Sep 2022 23:45:51 +0900 Subject: [PATCH 09/12] feat: Update FileCache.calcHashOfConfig() --- lib/lintSource.js | 4 +--- lib/utils/FileCache.js | 13 +++++++------ types/stylelint/index.d.ts | 2 -- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/lintSource.js b/lib/lintSource.js index 09e09061e7..52c9d80b4d 100644 --- a/lib/lintSource.js +++ b/lib/lintSource.js @@ -67,9 +67,7 @@ module.exports = async function lintSource(stylelint, options = {}) { const existingPostcssResult = options.existingPostcssResult; if (options.cache) { - if (!stylelint._fileCache._hashOfConfig) { - stylelint._fileCache.calcHashOfConfig(config); - } + stylelint._fileCache.calcHashOfConfig(config); if (options.filePath && !stylelint._fileCache.hasFileChanged(options.filePath)) { return options.existingPostcssResult diff --git a/lib/utils/FileCache.js b/lib/utils/FileCache.js index c1b53766d9..67b9f227ec 100644 --- a/lib/utils/FileCache.js +++ b/lib/utils/FileCache.js @@ -11,13 +11,12 @@ const DEFAULT_CACHE_LOCATION = './.stylelintcache'; /** @typedef {import('file-entry-cache').FileDescriptor["meta"] & { hashOfConfig?: string }} CacheMetadata */ -/** - * @param {string} [cacheLocation] - * @param {string} [hashOfConfig] - * @constructor - */ class FileCache { - constructor(cacheLocation = DEFAULT_CACHE_LOCATION, cwd = process.cwd()) { + /** + * @param {string | undefined} cacheLocation + * @param {string} cwd + */ + constructor(cacheLocation = DEFAULT_CACHE_LOCATION, cwd) { const cacheFile = path.resolve(getCacheFile(cacheLocation, cwd)); debug(`Cache file is created at ${cacheFile}`); @@ -29,6 +28,8 @@ class FileCache { * @param {import('stylelint').Config} config */ calcHashOfConfig(config) { + if (this._hashOfConfig) return; + const stylelintVersion = pkg.version; const configString = JSON.stringify(config || {}); diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index bfd45e2bb8..1b363f4bef 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -89,8 +89,6 @@ declare module 'stylelint' { export type DisabledWarning = { line: number; rule: string }; type FileCache = { - _fileCache: fileEntryCache.FileEntryCache; - _hashOfConfig: string; calcHashOfConfig: (config: Config) => void; hasFileChanged: (absoluteFilepath: string) => boolean; reconcile: () => void; From 1bf36073eb112b6819a42828695a1ed58576d1f2 Mon Sep 17 00:00:00 2001 From: kimulaco Date: Sat, 24 Sep 2022 23:50:10 +0900 Subject: [PATCH 10/12] chore: Refactor lintSource --- lib/lintSource.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/lintSource.js b/lib/lintSource.js index 52c9d80b4d..bd101d4a61 100644 --- a/lib/lintSource.js +++ b/lib/lintSource.js @@ -70,8 +70,8 @@ module.exports = async function lintSource(stylelint, options = {}) { stylelint._fileCache.calcHashOfConfig(config); if (options.filePath && !stylelint._fileCache.hasFileChanged(options.filePath)) { - return options.existingPostcssResult - ? Object.assign(options.existingPostcssResult, { + return existingPostcssResult + ? Object.assign(existingPostcssResult, { stylelint: createEmptyStylelintPostcssResult(), }) : createEmptyPostcssResult(inputFilePath); From fe3f4411257c48b14539480b4699f12a3f4a6a40 Mon Sep 17 00:00:00 2001 From: kimulaco Date: Sun, 25 Sep 2022 10:09:45 +0900 Subject: [PATCH 11/12] fix: Remove cach test from `cli.test.js` --- lib/__tests__/cli.test.js | 73 --------------------------------------- 1 file changed, 73 deletions(-) diff --git a/lib/__tests__/cli.test.js b/lib/__tests__/cli.test.js index 3c27a58601..373489384c 100644 --- a/lib/__tests__/cli.test.js +++ b/lib/__tests__/cli.test.js @@ -3,7 +3,6 @@ 'use strict'; const path = require('path'); -const { promises: fs, existsSync } = require('fs'); const stripAnsi = require('strip-ansi'); const cli = require('../cli'); @@ -13,12 +12,6 @@ const replaceBackslashes = require('../testUtils/replaceBackslashes'); const fixturesPath = (...elems) => replaceBackslashes(path.join(__dirname, 'fixtures', ...elems)); const { buildCLI } = cli; -const removeFile = async (filePath) => { - if (existsSync(filePath)) { - await fs.unlink(filePath); - } -}; - jest.mock('../utils/getStdin', () => () => Promise.resolve('')); describe('buildCLI', () => { @@ -376,70 +369,4 @@ describe('CLI', () => { expect(output).toBe('Custom formatter reports 1 warning(s).'); }); - - it('--cache same warning config', async () => { - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('default-severity-warning.json'), - fixturesPath('empty-block.css'), - ]); - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('default-severity-warning.json'), - fixturesPath('empty-block.css'), - ]); - expect(process.stdout.write).toHaveBeenCalledTimes(1); - - await removeFile(fixturesPath('.stylelintcache')); - }); - - it('--cache different error config', async () => { - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('default-severity-warning.json'), - fixturesPath('empty-block.css'), - ]); - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('config-block-no-empty.json'), - fixturesPath('empty-block.css'), - ]); - expect(process.stdout.write).toHaveBeenCalledTimes(2); - - await removeFile(fixturesPath('.stylelintcache')); - }); - - it('--cache same error config', async () => { - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('config-block-no-empty.json'), - fixturesPath('empty-block.css'), - ]); - await cli([ - '--cache', - '--cache-location', - fixturesPath('.stylelintcache'), - '--config', - fixturesPath('config-block-no-empty.json'), - fixturesPath('empty-block.css'), - ]); - expect(process.stdout.write).toHaveBeenCalledTimes(2); - - await removeFile(fixturesPath('.stylelintcache')); - }); }); From a3bff965057239859b87e9e9f400b43e975f22cd Mon Sep 17 00:00:00 2001 From: kimulaco Date: Tue, 27 Sep 2022 00:08:11 +0900 Subject: [PATCH 12/12] test: Add test to discarded cache --- lib/__tests__/standalone-cache.test.js | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/__tests__/standalone-cache.test.js b/lib/__tests__/standalone-cache.test.js index 7f0373590a..ecc80f1f45 100644 --- a/lib/__tests__/standalone-cache.test.js +++ b/lib/__tests__/standalone-cache.test.js @@ -198,3 +198,47 @@ describe('standalone cache uses cacheLocation', () => { expect(cache.getKey(validFile)).toBeTruthy(); }); }); + +describe('standalone cache uses a config file', () => { + const cwd = path.join(__dirname, 'tmp', 'standalone-cache-use-config-file'); + + safeChdir(cwd); + + const configFile = path.join(cwd, '.stylelintrc.json'); + const lintedFile = path.join(cwd, 'a.css'); + + beforeEach(async () => { + await fs.writeFile(lintedFile, 'a {}'); + }); + + afterEach(async () => { + await removeFile(configFile); + await removeFile(lintedFile); + }); + + it('cache is discarded when a config file is changed', async () => { + const config = { files: [lintedFile], config: undefined, cache: true }; + + // No warnings when a rule is disabled. + await fs.writeFile( + configFile, + JSON.stringify({ + rules: { 'block-no-empty': null }, + }), + ); + const { results } = await standalone(config); + + expect(results[0].warnings).toHaveLength(0); + + // Some warnings when a rule becomes enabled by changing the config. + await fs.writeFile( + configFile, + JSON.stringify({ + rules: { 'block-no-empty': true }, + }), + ); + const { results: resultsNew } = await standalone(config); + + expect(resultsNew[0].warnings).toHaveLength(1); + }); +});