From 1e382d88dc08e5b3759e1a0fad764d2dfe10464f Mon Sep 17 00:00:00 2001 From: Anton Evzhakov Date: Sun, 28 Nov 2021 14:30:41 +0200 Subject: [PATCH 1/2] fix(webpack): fallback to default resolver if async plugin is detected --- packages/logger/package.json | 3 +- packages/logger/src/index.ts | 13 ++++++- packages/webpack4-loader/src/index.ts | 52 ++++++++++++++++++++------- packages/webpack5-loader/src/index.ts | 52 ++++++++++++++++++++------- yarn.lock | 5 +++ 5 files changed, 99 insertions(+), 26 deletions(-) diff --git a/packages/logger/package.json b/packages/logger/package.json index 0e9e3ac54..cec285edd 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -39,6 +39,7 @@ "@types/debug": "^4.1.5" }, "dependencies": { - "debug": "^4.1.1" + "debug": "^4.1.1", + "picocolors": "^1.0.0" } } diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 45765bbdf..e69e68528 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,10 +1,11 @@ import genericDebug from 'debug'; import type { Debugger } from 'debug'; +import pc from 'picocolors'; type LogLevel = 'error' | 'warn' | 'info' | 'debug'; const levels = ['error', 'warn', 'info', 'debug']; -const currentLevel = levels.indexOf(process.env.LINARIA_LOG || 'error'); +const currentLevel = levels.indexOf(process.env.LINARIA_LOG || 'warn'); const linariaLogger = genericDebug('linaria'); @@ -61,3 +62,13 @@ export const debug = log.bind(null, 'debug'); export const info = log.bind(null, 'info'); export const warn = log.bind(null, 'warn'); export const error = log.bind(null, 'error'); + +export const notify = (message: string) => { + console.log( + pc.red( + message.replace(/(`.*?`)/g, (s) => + pc.italic(s.substring(1, s.length - 1)) + ) + ) + ); +}; diff --git a/packages/webpack4-loader/src/index.ts b/packages/webpack4-loader/src/index.ts index 93744b29d..cf11dd875 100644 --- a/packages/webpack4-loader/src/index.ts +++ b/packages/webpack4-loader/src/index.ts @@ -14,7 +14,7 @@ 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 } from '@linaria/logger'; +import { debug, notify } from '@linaria/logger'; const workspaceRoot = findYarnWorkspaceRoot(); const lernaConfig = cosmiconfig('lerna', { @@ -44,6 +44,11 @@ export default function webpack4Loader( EvalCache.clearForFile(this.resourcePath); + const resolveOptionsDefaults = { + conditionNames: ['require'], + extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], + }; + const { sourceMap = undefined, cacheDirectory = '.linaria-cache', @@ -68,18 +73,41 @@ export default function webpack4Loader( ) ); - const resolveSync = enhancedResolve.create.sync( - // 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 - // Another option is to read the webpack.config.js, but it won't work for programmatic usage - // This API is used by many loaders/plugins, so hope we're safe for a while - { - ...(this._compilation?.options.resolve ?? {}), + // 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 + // Another option is to read the webpack.config.js, but it won't work for programmatic usage + // This API is used by many loaders/plugins, so hope we're safe for a while + const webpackResolveOptions = this._compilation?.options.resolve; + + // Let's try to create a resolver with the webpack config + let resolveSync = enhancedResolve.create.sync({ + ...resolveOptionsDefaults, + ...(this._compilation?.options.resolve ?? {}), + ...resolveOptions, + mainFields: ['main'], + }); + + try { + // Try to resolve the current file + resolveSync(__dirname, __filename); + } catch (e) { + // Looks like one of the webpack plugins is async and the whole resolver became async + notify( + 'The default webpack configuration cannot be used because some of the plugins are asynchronous. All plugins have been ignored. Please override `resolveOptions.plugins` in the Linaria configuration.' + ); + + // Fallback to synchronous resolve + resolveSync = enhancedResolve.create.sync({ + ...resolveOptionsDefaults, + ...((webpackResolveOptions && { + alias: webpackResolveOptions.alias, + modules: webpackResolveOptions.modules, + }) || + {}), ...resolveOptions, - mainFields: ['main'], - } - ); + }); + } let result; diff --git a/packages/webpack5-loader/src/index.ts b/packages/webpack5-loader/src/index.ts index 905c2e4cd..a5484d65f 100644 --- a/packages/webpack5-loader/src/index.ts +++ b/packages/webpack5-loader/src/index.ts @@ -14,7 +14,7 @@ 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 } from '@linaria/logger'; +import { debug, notify } from '@linaria/logger'; const workspaceRoot = findYarnWorkspaceRoot(); const lernaConfig = cosmiconfig('lerna', { @@ -32,6 +32,11 @@ export default function webpack5Loader( EvalCache.clearForFile(this.resourcePath); + const resolveOptionsDefaults = { + conditionNames: ['require'], + extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], + }; + const { sourceMap = undefined, cacheDirectory = '.linaria-cache', @@ -56,18 +61,41 @@ export default function webpack5Loader( ) ); - const resolveSync = enhancedResolve.create.sync( - // 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 - // Another option is to read the webpack.config.js, but it won't work for programmatic usage - // This API is used by many loaders/plugins, so hope we're safe for a while - { - ...(this._compilation?.options.resolve ?? {}), + // 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 + // Another option is to read the webpack.config.js, but it won't work for programmatic usage + // This API is used by many loaders/plugins, so hope we're safe for a while + const webpackResolveOptions = this._compilation?.options.resolve; + + // Let's try to create a resolver with the webpack config + let resolveSync = enhancedResolve.create.sync({ + ...resolveOptionsDefaults, + ...(webpackResolveOptions ?? {}), + ...resolveOptions, + mainFields: ['main'], + }); + + try { + // Try to resolve the current file + resolveSync(__dirname, __filename); + } catch (e) { + // Looks like one of the webpack plugins is async and the whole resolver became async + notify( + 'The default webpack configuration cannot be used because some of the plugins are asynchronous. All plugins have been ignored. Please override `resolveOptions.plugins` in the Linaria configuration.' + ); + + // Fallback to synchronous resolve + resolveSync = enhancedResolve.create.sync({ + ...resolveOptionsDefaults, + ...((webpackResolveOptions && { + alias: webpackResolveOptions.alias, + modules: webpackResolveOptions.modules, + }) || + {}), ...resolveOptions, - mainFields: ['main'], - } - ); + }); + } let result; diff --git a/yarn.lock b/yarn.lock index 2038ff9b9..4107f0622 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11251,6 +11251,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" From 94e6486b3727e2b58875ea523241a6b4b2c4e363 Mon Sep 17 00:00:00 2001 From: Anton Evzhakov Date: Sun, 28 Nov 2021 14:39:43 +0200 Subject: [PATCH 2/2] fix(webpack): don't use default extensions if it's empty (fixes #855) --- packages/webpack4-loader/src/index.ts | 6 ++++++ packages/webpack5-loader/src/index.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/packages/webpack4-loader/src/index.ts b/packages/webpack4-loader/src/index.ts index cf11dd875..3a4848e26 100644 --- a/packages/webpack4-loader/src/index.ts +++ b/packages/webpack4-loader/src/index.ts @@ -80,6 +80,12 @@ export default function webpack4Loader( // This API is used by many loaders/plugins, so hope we're safe for a while const webpackResolveOptions = this._compilation?.options.resolve; + // Resolved configuration contains empty list of extensions as a default value + // https://github.com/callstack/linaria/issues/855 + if (webpackResolveOptions.extensions?.length === 0) { + delete webpackResolveOptions.extensions; + } + // Let's try to create a resolver with the webpack config let resolveSync = enhancedResolve.create.sync({ ...resolveOptionsDefaults, diff --git a/packages/webpack5-loader/src/index.ts b/packages/webpack5-loader/src/index.ts index a5484d65f..7d100aede 100644 --- a/packages/webpack5-loader/src/index.ts +++ b/packages/webpack5-loader/src/index.ts @@ -68,6 +68,12 @@ export default function webpack5Loader( // This API is used by many loaders/plugins, so hope we're safe for a while const webpackResolveOptions = this._compilation?.options.resolve; + // Resolved configuration contains empty list of extensions as a default value + // https://github.com/callstack/linaria/issues/855 + if (webpackResolveOptions.extensions?.length === 0) { + delete webpackResolveOptions.extensions; + } + // Let's try to create a resolver with the webpack config let resolveSync = enhancedResolve.create.sync({ ...resolveOptionsDefaults,