From 0d2ea98b7104a3fe34186f6407cfea1163f4abba Mon Sep 17 00:00:00 2001 From: Anton Evzhakov Date: Tue, 30 Nov 2021 19:48:20 +0200 Subject: [PATCH 1/3] fix(webpack): prevent double rebuild for each change (fixes #878) --- docs/BUNDLERS_INTEGRATION.md | 9 --- packages/webpack4-loader/package.json | 7 +-- packages/webpack4-loader/src/index.ts | 55 +++---------------- .../webpack4-loader/src/outputCssLoader.ts | 9 +++ packages/webpack5-loader/package.json | 7 +-- packages/webpack5-loader/src/index.ts | 53 ++---------------- .../webpack5-loader/src/outputCssLoader.ts | 9 +++ 7 files changed, 33 insertions(+), 116 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..905fc4133 100644 --- a/docs/BUNDLERS_INTEGRATION.md +++ b/docs/BUNDLERS_INTEGRATION.md @@ -177,14 +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. - - `preprocessor: 'none' | 'stylis' | Function` (default: `'stylis'`) You can override the pre-processor if you want to override how the loader processes the CSS. @@ -218,7 +210,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..7edfb061f 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,28 +42,11 @@ 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 - ) - ); - // this._compilation is a deprecated API // However there seems to be no other way to access webpack's resolver // There is this.resolve, but it's asynchronous @@ -130,7 +104,6 @@ export default function webpack4Loader( result = transform(content, { filename: path.relative(process.cwd(), this.resourcePath), inputSourceMap: inputSourceMap ?? undefined, - outputFilename, pluginOptions: rest, preprocessor, }); @@ -161,28 +134,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; - - try { - currentCssText = fs.readFileSync(outputFilename, 'utf-8'); - } catch (e) { - // Ignore error - } + addFile(this.resourcePath, cssText); - if (currentCssText !== cssText) { - mkdirp.sync(path.dirname(outputFilename)); - fs.writeFileSync(outputFilename, cssText); - } + const request = `linaria.css!=!${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..39df29f7c --- /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 async 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..257138575 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,28 +30,11 @@ 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 - ) - ); - // this._compilation is a deprecated API // However there seems to be no other way to access webpack's resolver // There is this.resolve, but it's asynchronous @@ -123,7 +97,6 @@ export default function webpack5Loader( result = transform(content, { filename: path.relative(process.cwd(), this.resourcePath), inputSourceMap: inputSourceMap ?? undefined, - outputFilename, pluginOptions: rest, preprocessor, }); @@ -154,28 +127,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 = `linaria.css!=!${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..39df29f7c --- /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 async function outputCssLoader(this: { resourcePath: string }) { + return cssLookup.get(this.resourcePath) ?? ''; +} From 1d0cc1f7f5884ec36e3fed7098d58f956edc3056 Mon Sep 17 00:00:00 2001 From: Anton Evzhakov Date: Tue, 30 Nov 2021 20:25:00 +0200 Subject: [PATCH 2/3] fix(webpack): cleanup #878 --- yarn.lock | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) 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== From 557611f8032f5bf9f2896aeccd2a7dd3cd7adb2d Mon Sep 17 00:00:00 2001 From: Anton Evzhakov Date: Wed, 1 Dec 2021 12:05:36 +0200 Subject: [PATCH 3/3] fix(webpack): requested changes #879 --- docs/BUNDLERS_INTEGRATION.md | 4 ++++ packages/webpack4-loader/src/index.ts | 5 ++++- packages/webpack4-loader/src/outputCssLoader.ts | 2 +- packages/webpack5-loader/src/index.ts | 5 ++++- packages/webpack5-loader/src/outputCssLoader.ts | 2 +- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/BUNDLERS_INTEGRATION.md b/docs/BUNDLERS_INTEGRATION.md index 905fc4133..a2eb686a5 100644 --- a/docs/BUNDLERS_INTEGRATION.md +++ b/docs/BUNDLERS_INTEGRATION.md @@ -177,6 +177,10 @@ 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. +- `extension: string` (default: `'.linaria.css'`): + + An extension of the intermediate CSS files. + - `preprocessor: 'none' | 'stylis' | Function` (default: `'stylis'`) You can override the pre-processor if you want to override how the loader processes the CSS. diff --git a/packages/webpack4-loader/src/index.ts b/packages/webpack4-loader/src/index.ts index 7edfb061f..b190c7ed0 100644 --- a/packages/webpack4-loader/src/index.ts +++ b/packages/webpack4-loader/src/index.ts @@ -43,10 +43,13 @@ export default function webpack4Loader( const { sourceMap = undefined, preprocessor = undefined, + extension = '.linaria.css', resolveOptions = {}, ...rest } = loaderUtils.getOptions(this) || {}; + 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 // There is this.resolve, but it's asynchronous @@ -136,7 +139,7 @@ export default function webpack4Loader( addFile(this.resourcePath, cssText); - const request = `linaria.css!=!${outputCssLoader}!${this.resourcePath}`; + const request = `${outputFileName}!=!${outputCssLoader}!${this.resourcePath}`; const stringifiedRequest = loaderUtils.stringifyRequest(this, request); this.callback( diff --git a/packages/webpack4-loader/src/outputCssLoader.ts b/packages/webpack4-loader/src/outputCssLoader.ts index 39df29f7c..27916a76f 100644 --- a/packages/webpack4-loader/src/outputCssLoader.ts +++ b/packages/webpack4-loader/src/outputCssLoader.ts @@ -4,6 +4,6 @@ export const addFile = (id: string, content: string) => { cssLookup.set(id, content); }; -export default async function outputCssLoader(this: { resourcePath: string }) { +export default function outputCssLoader(this: { resourcePath: string }) { return cssLookup.get(this.resourcePath) ?? ''; } diff --git a/packages/webpack5-loader/src/index.ts b/packages/webpack5-loader/src/index.ts index 257138575..18a2f1147 100644 --- a/packages/webpack5-loader/src/index.ts +++ b/packages/webpack5-loader/src/index.ts @@ -31,10 +31,13 @@ export default function webpack5Loader( const { sourceMap = undefined, preprocessor = undefined, + extension = '.linaria.css', resolveOptions = {}, ...rest } = this.getOptions() || {}; + 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 // There is this.resolve, but it's asynchronous @@ -129,7 +132,7 @@ export default function webpack5Loader( addFile(this.resourcePath, cssText); - const request = `linaria.css!=!${outputCssLoader}!${this.resourcePath}`; + const request = `${outputFileName}!=!${outputCssLoader}!${this.resourcePath}`; const stringifiedRequest = loaderUtils.stringifyRequest(this, request); this.callback( diff --git a/packages/webpack5-loader/src/outputCssLoader.ts b/packages/webpack5-loader/src/outputCssLoader.ts index 39df29f7c..27916a76f 100644 --- a/packages/webpack5-loader/src/outputCssLoader.ts +++ b/packages/webpack5-loader/src/outputCssLoader.ts @@ -4,6 +4,6 @@ export const addFile = (id: string, content: string) => { cssLookup.set(id, content); }; -export default async function outputCssLoader(this: { resourcePath: string }) { +export default function outputCssLoader(this: { resourcePath: string }) { return cssLookup.get(this.resourcePath) ?? ''; }