From 5517cf79c1a5dbf7c10d17be01cf4ac4470116f9 Mon Sep 17 00:00:00 2001 From: Anton Evzhakov Date: Wed, 1 Dec 2021 12:18:57 +0200 Subject: [PATCH] fix(webpack): replace file system cache with in-memory cache (fixes #878) (#879) * fix(webpack): prevent double rebuild for each change (fixes #878) --- docs/BUNDLERS_INTEGRATION.md | 5 -- packages/webpack4-loader/package.json | 7 +-- packages/webpack4-loader/src/index.ts | 54 +++---------------- .../webpack4-loader/src/outputCssLoader.ts | 9 ++++ packages/webpack5-loader/package.json | 7 +-- packages/webpack5-loader/src/index.ts | 52 +++--------------- .../webpack5-loader/src/outputCssLoader.ts | 9 ++++ yarn.lock | 10 +--- 8 files changed, 36 insertions(+), 117 deletions(-) create mode 100644 packages/webpack4-loader/src/outputCssLoader.ts create mode 100644 packages/webpack5-loader/src/outputCssLoader.ts diff --git a/docs/BUNDLERS_INTEGRATION.md b/docs/BUNDLERS_INTEGRATION.md index 58158e94b..a2eb686a5 100644 --- a/docs/BUNDLERS_INTEGRATION.md +++ b/docs/BUNDLERS_INTEGRATION.md @@ -177,10 +177,6 @@ The loader accepts the following options: Setting this option to `true` will include source maps for the generated CSS so that you can see where source of the class name in devtools. We recommend to enable this only in development mode because the sourcemap is inlined into the CSS files. -- `cacheDirectory: string` (default: `'.linaria-cache'`): - - Path to the directory where the loader will output the intermediate CSS files. You can pass a relative or absolute directory path. Make sure the directory is inside the working directory for things to work properly. **You should add this directory to `.gitignore` so you don't accidentally commit them.** - - `extension: string` (default: `'.linaria.css'`): An extension of the intermediate CSS files. @@ -218,7 +214,6 @@ You can pass options to the loader like so: loader: '@linaria/webpack-loader', options: { sourceMap: false, - cacheDirectory: '.linaria-cache', }, } ``` diff --git a/packages/webpack4-loader/package.json b/packages/webpack4-loader/package.json index 3ed2ce2dd..9c190d379 100644 --- a/packages/webpack4-loader/package.json +++ b/packages/webpack4-loader/package.json @@ -38,22 +38,17 @@ "watch": "yarn build --watch" }, "devDependencies": { - "@types/cosmiconfig": "^5.0.3", "@types/enhanced-resolve": "^3.0.6", "@types/loader-utils": "^1.1.3", "@types/mkdirp": "^0.5.2", - "@types/normalize-path": "^3.0.0", "source-map": "^0.7.3" }, "dependencies": { "@linaria/babel-preset": "^3.0.0-beta.15", "@linaria/logger": "^3.0.0-beta.15", - "cosmiconfig": "^5.1.0", "enhanced-resolve": "^4.1.0", - "find-yarn-workspace-root": "^1.2.1", "loader-utils": "^1.2.3", - "mkdirp": "^0.5.1", - "normalize-path": "^3.0.0" + "mkdirp": "^0.5.1" }, "peerDependencies": { "@babel/core": ">=7" diff --git a/packages/webpack4-loader/src/index.ts b/packages/webpack4-loader/src/index.ts index 3a4848e26..b190c7ed0 100644 --- a/packages/webpack4-loader/src/index.ts +++ b/packages/webpack4-loader/src/index.ts @@ -4,24 +4,13 @@ * returns transformed code without template literals and attaches generated source maps */ -import fs from 'fs'; import path from 'path'; -import mkdirp from 'mkdirp'; -import normalize from 'normalize-path'; import loaderUtils from 'loader-utils'; import enhancedResolve from 'enhanced-resolve'; -import findYarnWorkspaceRoot from 'find-yarn-workspace-root'; import type { RawSourceMap } from 'source-map'; -import cosmiconfig from 'cosmiconfig'; import { EvalCache, Module, transform } from '@linaria/babel-preset'; import { debug, notify } from '@linaria/logger'; - -const workspaceRoot = findYarnWorkspaceRoot(); -const lernaConfig = cosmiconfig('lerna', { - searchPlaces: ['lerna.json'], -}).searchSync(); -const lernaRoot = - lernaConfig !== null ? path.dirname(lernaConfig.filepath) : null; +import { addFile } from './outputCssLoader'; type LoaderContext = Parameters[0]; @@ -35,6 +24,8 @@ const castSourceMap = ( } : undefined; +const outputCssLoader = require.resolve('./outputCssLoader'); + export default function webpack4Loader( this: LoaderContext, content: string, @@ -51,27 +42,13 @@ export default function webpack4Loader( const { sourceMap = undefined, - cacheDirectory = '.linaria-cache', preprocessor = undefined, extension = '.linaria.css', resolveOptions = {}, ...rest } = loaderUtils.getOptions(this) || {}; - const root = workspaceRoot || lernaRoot || process.cwd(); - - const baseOutputFileName = this.resourcePath.replace(/\.[^.]+$/, extension); - - const outputFilename = normalize( - path.join( - path.isAbsolute(cacheDirectory) - ? cacheDirectory - : path.join(process.cwd(), cacheDirectory), - this.resourcePath.includes(root) - ? path.relative(root, baseOutputFileName) - : baseOutputFileName - ) - ); + const outputFileName = this.resourcePath.replace(/\.[^.]+$/, extension); // this._compilation is a deprecated API // However there seems to be no other way to access webpack's resolver @@ -130,7 +107,6 @@ export default function webpack4Loader( result = transform(content, { filename: path.relative(process.cwd(), this.resourcePath), inputSourceMap: inputSourceMap ?? undefined, - outputFilename, pluginOptions: rest, preprocessor, }); @@ -161,28 +137,14 @@ export default function webpack4Loader( }); } - // Read the file first to compare the content - // Write the new content only if it's changed - // This will prevent unnecessary WDS reloads - let currentCssText; + addFile(this.resourcePath, cssText); - try { - currentCssText = fs.readFileSync(outputFilename, 'utf-8'); - } catch (e) { - // Ignore error - } - - if (currentCssText !== cssText) { - mkdirp.sync(path.dirname(outputFilename)); - fs.writeFileSync(outputFilename, cssText); - } + const request = `${outputFileName}!=!${outputCssLoader}!${this.resourcePath}`; + const stringifiedRequest = loaderUtils.stringifyRequest(this, request); this.callback( null, - `${result.code}\n\nrequire(${loaderUtils.stringifyRequest( - this, - outputFilename - )});`, + `${result.code}\n\nrequire(${stringifiedRequest});`, castSourceMap(result.sourceMap) ); return; diff --git a/packages/webpack4-loader/src/outputCssLoader.ts b/packages/webpack4-loader/src/outputCssLoader.ts new file mode 100644 index 000000000..27916a76f --- /dev/null +++ b/packages/webpack4-loader/src/outputCssLoader.ts @@ -0,0 +1,9 @@ +const cssLookup = new Map(); + +export const addFile = (id: string, content: string) => { + cssLookup.set(id, content); +}; + +export default function outputCssLoader(this: { resourcePath: string }) { + return cssLookup.get(this.resourcePath) ?? ''; +} diff --git a/packages/webpack5-loader/package.json b/packages/webpack5-loader/package.json index 12480c199..58a78d1fa 100644 --- a/packages/webpack5-loader/package.json +++ b/packages/webpack5-loader/package.json @@ -38,23 +38,18 @@ "watch": "yarn build --watch" }, "devDependencies": { - "@types/cosmiconfig": "^5.0.3", "@types/enhanced-resolve": "^3.0.6", "@types/loader-utils": "^1.1.3", "@types/mkdirp": "^0.5.2", - "@types/normalize-path": "^3.0.0", "source-map": "^0.7.3", "webpack": "^5.6.0" }, "dependencies": { "@linaria/babel-preset": "^3.0.0-beta.15", "@linaria/logger": "^3.0.0-beta.15", - "cosmiconfig": "^5.1.0", "enhanced-resolve": "^5.3.1", - "find-yarn-workspace-root": "^1.2.1", "loader-utils": "^2.0.0", - "mkdirp": "^0.5.1", - "normalize-path": "^3.0.0" + "mkdirp": "^0.5.1" }, "peerDependencies": { "@babel/core": ">=7", diff --git a/packages/webpack5-loader/src/index.ts b/packages/webpack5-loader/src/index.ts index 7d100aede..18a2f1147 100644 --- a/packages/webpack5-loader/src/index.ts +++ b/packages/webpack5-loader/src/index.ts @@ -4,24 +4,15 @@ * returns transformed code without template literals and attaches generated source maps */ -import fs from 'fs'; import path from 'path'; import loaderUtils from 'loader-utils'; -import mkdirp from 'mkdirp'; -import normalize from 'normalize-path'; import enhancedResolve from 'enhanced-resolve'; -import findYarnWorkspaceRoot from 'find-yarn-workspace-root'; import type { RawSourceMap } from 'source-map'; -import cosmiconfig from 'cosmiconfig'; import { EvalCache, Module, transform } from '@linaria/babel-preset'; import { debug, notify } from '@linaria/logger'; +import { addFile } from './outputCssLoader'; -const workspaceRoot = findYarnWorkspaceRoot(); -const lernaConfig = cosmiconfig('lerna', { - searchPlaces: ['lerna.json'], -}).searchSync(); -const lernaRoot = - lernaConfig !== null ? path.dirname(lernaConfig.filepath) : null; +const outputCssLoader = require.resolve('./outputCssLoader'); export default function webpack5Loader( this: any, @@ -39,27 +30,13 @@ export default function webpack5Loader( const { sourceMap = undefined, - cacheDirectory = '.linaria-cache', preprocessor = undefined, extension = '.linaria.css', resolveOptions = {}, ...rest } = this.getOptions() || {}; - const root = workspaceRoot || lernaRoot || process.cwd(); - - const baseOutputFileName = this.resourcePath.replace(/\.[^.]+$/, extension); - - const outputFilename = normalize( - path.join( - path.isAbsolute(cacheDirectory) - ? cacheDirectory - : path.join(process.cwd(), cacheDirectory), - this.resourcePath.includes(root) - ? path.relative(root, baseOutputFileName) - : baseOutputFileName - ) - ); + const outputFileName = this.resourcePath.replace(/\.[^.]+$/, extension); // this._compilation is a deprecated API // However there seems to be no other way to access webpack's resolver @@ -123,7 +100,6 @@ export default function webpack5Loader( result = transform(content, { filename: path.relative(process.cwd(), this.resourcePath), inputSourceMap: inputSourceMap ?? undefined, - outputFilename, pluginOptions: rest, preprocessor, }); @@ -154,28 +130,14 @@ export default function webpack5Loader( }); } - // Read the file first to compare the content - // Write the new content only if it's changed - // This will prevent unnecessary WDS reloads - let currentCssText; + addFile(this.resourcePath, cssText); - try { - currentCssText = fs.readFileSync(outputFilename, 'utf-8'); - } catch (e) { - // Ignore error - } - - if (currentCssText !== cssText) { - mkdirp.sync(path.dirname(outputFilename)); - fs.writeFileSync(outputFilename, cssText); - } + const request = `${outputFileName}!=!${outputCssLoader}!${this.resourcePath}`; + const stringifiedRequest = loaderUtils.stringifyRequest(this, request); this.callback( null, - `${result.code}\n\nrequire(${loaderUtils.stringifyRequest( - this, - outputFilename - )});`, + `${result.code}\n\nrequire(${stringifiedRequest});`, result.sourceMap ?? undefined ); return; diff --git a/packages/webpack5-loader/src/outputCssLoader.ts b/packages/webpack5-loader/src/outputCssLoader.ts new file mode 100644 index 000000000..27916a76f --- /dev/null +++ b/packages/webpack5-loader/src/outputCssLoader.ts @@ -0,0 +1,9 @@ +const cssLookup = new Map(); + +export const addFile = (id: string, content: string) => { + cssLookup.set(id, content); +}; + +export default function outputCssLoader(this: { resourcePath: string }) { + return cssLookup.get(this.resourcePath) ?? ''; +} diff --git a/yarn.lock b/yarn.lock index 111620285..eda9d3133 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6760,14 +6760,6 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-yarn-workspace-root@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" - integrity sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q== - dependencies: - fs-extra "^4.0.3" - micromatch "^3.1.4" - findup-sync@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" @@ -6878,7 +6870,7 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^4.0.2, fs-extra@^4.0.3: +fs-extra@^4.0.2: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==