mirror of https://github.com/webpack/webpack.git
				
				
				
			fix: `css/auto` considers a module depending on its filename as `css` (pure CSS) or `css/local`
This commit is contained in:
		
						commit
						94feb93752
					
				| 
						 | 
				
			
			@ -17,7 +17,8 @@ const {
 | 
			
		|||
	ASSET_MODULE_TYPE,
 | 
			
		||||
	CSS_MODULE_TYPE_AUTO,
 | 
			
		||||
	CSS_MODULE_TYPE,
 | 
			
		||||
	CSS_MODULE_TYPE_MODULE
 | 
			
		||||
	CSS_MODULE_TYPE_MODULE,
 | 
			
		||||
	CSS_MODULE_TYPE_GLOBAL
 | 
			
		||||
} = require("../ModuleTypeConstants");
 | 
			
		||||
const Template = require("../Template");
 | 
			
		||||
const { cleverMerge } = require("../util/cleverMerge");
 | 
			
		||||
| 
						 | 
				
			
			@ -633,19 +634,19 @@ const applyModuleDefaults = (
 | 
			
		|||
	F(module.parser, ASSET_MODULE_TYPE, () => ({}));
 | 
			
		||||
	F(
 | 
			
		||||
		/** @type {NonNullable<ParserOptionsByModuleTypeKnown["asset"]>} */
 | 
			
		||||
		(module.parser.asset),
 | 
			
		||||
		(module.parser[ASSET_MODULE_TYPE]),
 | 
			
		||||
		"dataUrlCondition",
 | 
			
		||||
		() => ({})
 | 
			
		||||
	);
 | 
			
		||||
	if (
 | 
			
		||||
		typeof (
 | 
			
		||||
			/** @type {NonNullable<ParserOptionsByModuleTypeKnown["asset"]>} */
 | 
			
		||||
			(module.parser.asset).dataUrlCondition
 | 
			
		||||
			(module.parser[ASSET_MODULE_TYPE]).dataUrlCondition
 | 
			
		||||
		) === "object"
 | 
			
		||||
	) {
 | 
			
		||||
		D(
 | 
			
		||||
			/** @type {NonNullable<ParserOptionsByModuleTypeKnown["asset"]>} */
 | 
			
		||||
			(module.parser.asset).dataUrlCondition,
 | 
			
		||||
			(module.parser[ASSET_MODULE_TYPE]).dataUrlCondition,
 | 
			
		||||
			"maxSize",
 | 
			
		||||
			8096
 | 
			
		||||
		);
 | 
			
		||||
| 
						 | 
				
			
			@ -663,41 +664,41 @@ const applyModuleDefaults = (
 | 
			
		|||
	);
 | 
			
		||||
 | 
			
		||||
	if (css) {
 | 
			
		||||
		F(module.parser, "css", () => ({}));
 | 
			
		||||
		F(module.parser, CSS_MODULE_TYPE, () => ({}));
 | 
			
		||||
 | 
			
		||||
		D(module.parser.css, "namedExports", true);
 | 
			
		||||
		D(module.parser[CSS_MODULE_TYPE], "namedExports", true);
 | 
			
		||||
 | 
			
		||||
		F(module.generator, "css", () => ({}));
 | 
			
		||||
		F(module.generator, CSS_MODULE_TYPE, () => ({}));
 | 
			
		||||
 | 
			
		||||
		applyCssGeneratorOptionsDefaults(
 | 
			
		||||
			/** @type {NonNullable<GeneratorOptionsByModuleTypeKnown["css"]>} */
 | 
			
		||||
			(module.generator.css),
 | 
			
		||||
			(module.generator[CSS_MODULE_TYPE]),
 | 
			
		||||
			{ targetProperties }
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		F(module.generator, "css/auto", () => ({}));
 | 
			
		||||
		F(module.generator, CSS_MODULE_TYPE_AUTO, () => ({}));
 | 
			
		||||
		D(
 | 
			
		||||
			module.generator["css/auto"],
 | 
			
		||||
			module.generator[CSS_MODULE_TYPE_AUTO],
 | 
			
		||||
			"localIdentName",
 | 
			
		||||
			"[uniqueName]-[id]-[local]"
 | 
			
		||||
		);
 | 
			
		||||
		D(module.generator["css/auto"], "exportsConvention", "as-is");
 | 
			
		||||
		D(module.generator[CSS_MODULE_TYPE_AUTO], "exportsConvention", "as-is");
 | 
			
		||||
 | 
			
		||||
		F(module.generator, "css/module", () => ({}));
 | 
			
		||||
		F(module.generator, CSS_MODULE_TYPE_MODULE, () => ({}));
 | 
			
		||||
		D(
 | 
			
		||||
			module.generator["css/module"],
 | 
			
		||||
			module.generator[CSS_MODULE_TYPE_MODULE],
 | 
			
		||||
			"localIdentName",
 | 
			
		||||
			"[uniqueName]-[id]-[local]"
 | 
			
		||||
		);
 | 
			
		||||
		D(module.generator["css/module"], "exportsConvention", "as-is");
 | 
			
		||||
		D(module.generator[CSS_MODULE_TYPE_MODULE], "exportsConvention", "as-is");
 | 
			
		||||
 | 
			
		||||
		F(module.generator, "css/global", () => ({}));
 | 
			
		||||
		F(module.generator, CSS_MODULE_TYPE_GLOBAL, () => ({}));
 | 
			
		||||
		D(
 | 
			
		||||
			module.generator["css/global"],
 | 
			
		||||
			module.generator[CSS_MODULE_TYPE_GLOBAL],
 | 
			
		||||
			"localIdentName",
 | 
			
		||||
			"[uniqueName]-[id]-[local]"
 | 
			
		||||
		);
 | 
			
		||||
		D(module.generator["css/global"], "exportsConvention", "as-is");
 | 
			
		||||
		D(module.generator[CSS_MODULE_TYPE_GLOBAL], "exportsConvention", "as-is");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	A(module, "defaultRules", () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -278,14 +278,13 @@ class CssModulesPlugin {
 | 
			
		|||
							const { namedExports } = parserOptions;
 | 
			
		||||
 | 
			
		||||
							switch (type) {
 | 
			
		||||
								case CSS_MODULE_TYPE_GLOBAL:
 | 
			
		||||
								case CSS_MODULE_TYPE_AUTO:
 | 
			
		||||
								case CSS_MODULE_TYPE:
 | 
			
		||||
									return new CssParser({
 | 
			
		||||
										namedExports
 | 
			
		||||
									});
 | 
			
		||||
								case CSS_MODULE_TYPE:
 | 
			
		||||
								case CSS_MODULE_TYPE_GLOBAL:
 | 
			
		||||
									return new CssParser({
 | 
			
		||||
										allowModeSwitch: false,
 | 
			
		||||
										defaultMode: "global",
 | 
			
		||||
										namedExports
 | 
			
		||||
									});
 | 
			
		||||
								case CSS_MODULE_TYPE_MODULE:
 | 
			
		||||
| 
						 | 
				
			
			@ -293,6 +292,11 @@ class CssModulesPlugin {
 | 
			
		|||
										defaultMode: "local",
 | 
			
		||||
										namedExports
 | 
			
		||||
									});
 | 
			
		||||
								case CSS_MODULE_TYPE_AUTO:
 | 
			
		||||
									return new CssParser({
 | 
			
		||||
										defaultMode: "auto",
 | 
			
		||||
										namedExports
 | 
			
		||||
									});
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
					normalModuleFactory.hooks.createGenerator
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,8 @@ const STRING_MULTILINE = /\\[\n\r\f]/g;
 | 
			
		|||
// https://www.w3.org/TR/css-syntax-3/#whitespace
 | 
			
		||||
const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g;
 | 
			
		||||
const UNESCAPE = /\\([0-9a-fA-F]{1,6}[ \t\n\r\f]?|[\s\S])/g;
 | 
			
		||||
const IMAGE_SET_FUNCTION = /^(-\w+-)?image-set$/i;
 | 
			
		||||
// TODO fix tokens
 | 
			
		||||
const IMAGE_SET_FUNCTION = /^:?(-\w+-)?image-set$/i;
 | 
			
		||||
const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(-\w+-)?keyframes$/;
 | 
			
		||||
const OPTIONALLY_VENDOR_PREFIXED_ANIMATION_PROPERTY =
 | 
			
		||||
	/^(-\w+-)?animation(-name)?$/i;
 | 
			
		||||
| 
						 | 
				
			
			@ -133,13 +134,13 @@ const CSS_MODE_AT_IMPORT_INVALID = 3;
 | 
			
		|||
const CSS_MODE_AT_NAMESPACE_INVALID = 4;
 | 
			
		||||
 | 
			
		||||
class CssParser extends Parser {
 | 
			
		||||
	constructor({
 | 
			
		||||
		allowModeSwitch = true,
 | 
			
		||||
		defaultMode = "global",
 | 
			
		||||
		namedExports = true
 | 
			
		||||
	} = {}) {
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param {object} options options
 | 
			
		||||
	 * @param {("pure" | "global" | "local" | "auto")=} options.defaultMode default mode
 | 
			
		||||
	 * @param {boolean=} options.namedExports is named exports
 | 
			
		||||
	 */
 | 
			
		||||
	constructor({ defaultMode = "pure", namedExports = true } = {}) {
 | 
			
		||||
		super();
 | 
			
		||||
		this.allowModeSwitch = allowModeSwitch;
 | 
			
		||||
		this.defaultMode = defaultMode;
 | 
			
		||||
		this.namedExports = namedExports;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -178,22 +179,22 @@ class CssParser extends Parser {
 | 
			
		|||
			source = source.slice(1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let mode = this.defaultMode;
 | 
			
		||||
 | 
			
		||||
		const module = state.module;
 | 
			
		||||
 | 
			
		||||
		/** @type {string | undefined} */
 | 
			
		||||
		let oldDefaultMode;
 | 
			
		||||
 | 
			
		||||
		if (
 | 
			
		||||
			mode === "auto" &&
 | 
			
		||||
			module.type === CSS_MODULE_TYPE_AUTO &&
 | 
			
		||||
			IS_MODULES.test(
 | 
			
		||||
				parseResource(module.matchResource || module.resource).path
 | 
			
		||||
			)
 | 
			
		||||
		) {
 | 
			
		||||
			oldDefaultMode = this.defaultMode;
 | 
			
		||||
 | 
			
		||||
			this.defaultMode = "local";
 | 
			
		||||
			mode = "local";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const isModules = mode === "global" || mode === "local";
 | 
			
		||||
 | 
			
		||||
		const locConverter = new LocConverter(source);
 | 
			
		||||
		/** @type {Set<string>} */
 | 
			
		||||
		const declaredCssVariables = new Set();
 | 
			
		||||
| 
						 | 
				
			
			@ -239,8 +240,7 @@ class CssParser extends Parser {
 | 
			
		|||
		 * @returns {boolean} true, when in local scope
 | 
			
		||||
		 */
 | 
			
		||||
		const isLocalMode = () =>
 | 
			
		||||
			modeData === "local" ||
 | 
			
		||||
			(this.defaultMode === "local" && modeData === undefined);
 | 
			
		||||
			modeData === "local" || (mode === "local" && modeData === undefined);
 | 
			
		||||
		/**
 | 
			
		||||
		 * @param {string} chars characters
 | 
			
		||||
		 * @returns {(input: string, pos: number) => number} function to eat characters
 | 
			
		||||
| 
						 | 
				
			
			@ -586,7 +586,7 @@ class CssParser extends Parser {
 | 
			
		|||
					scope = CSS_MODE_IN_AT_IMPORT;
 | 
			
		||||
					importData = { start };
 | 
			
		||||
				} else if (
 | 
			
		||||
					this.allowModeSwitch &&
 | 
			
		||||
					isModules &&
 | 
			
		||||
					OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE.test(name)
 | 
			
		||||
				) {
 | 
			
		||||
					let pos = end;
 | 
			
		||||
| 
						 | 
				
			
			@ -614,7 +614,7 @@ class CssParser extends Parser {
 | 
			
		|||
					}
 | 
			
		||||
					pos = newPos;
 | 
			
		||||
					return pos + 1;
 | 
			
		||||
				} else if (this.allowModeSwitch && name === "@property") {
 | 
			
		||||
				} else if (isModules && name === "@property") {
 | 
			
		||||
					let pos = end;
 | 
			
		||||
					pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
 | 
			
		||||
					if (pos === input.length) return pos;
 | 
			
		||||
| 
						 | 
				
			
			@ -661,7 +661,7 @@ class CssParser extends Parser {
 | 
			
		|||
					modeData = isLocalMode() ? "local" : "global";
 | 
			
		||||
					isNextRulePrelude = true;
 | 
			
		||||
					return end;
 | 
			
		||||
				} else if (this.allowModeSwitch) {
 | 
			
		||||
				} else if (isModules) {
 | 
			
		||||
					modeData = "global";
 | 
			
		||||
					isNextRulePrelude = false;
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -765,7 +765,7 @@ class CssParser extends Parser {
 | 
			
		|||
						break;
 | 
			
		||||
					}
 | 
			
		||||
					case CSS_MODE_IN_BLOCK: {
 | 
			
		||||
						if (this.allowModeSwitch) {
 | 
			
		||||
						if (isModules) {
 | 
			
		||||
							processDeclarationValueDone(input);
 | 
			
		||||
							inAnimationProperty = false;
 | 
			
		||||
							isNextRulePrelude = isNextNestedSyntax(input, end);
 | 
			
		||||
| 
						 | 
				
			
			@ -782,7 +782,7 @@ class CssParser extends Parser {
 | 
			
		|||
						scope = CSS_MODE_IN_BLOCK;
 | 
			
		||||
						blockNestingLevel = 1;
 | 
			
		||||
 | 
			
		||||
						if (this.allowModeSwitch) {
 | 
			
		||||
						if (isModules) {
 | 
			
		||||
							isNextRulePrelude = isNextNestedSyntax(input, end);
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -791,7 +791,7 @@ class CssParser extends Parser {
 | 
			
		|||
					case CSS_MODE_IN_BLOCK: {
 | 
			
		||||
						blockNestingLevel++;
 | 
			
		||||
 | 
			
		||||
						if (this.allowModeSwitch) {
 | 
			
		||||
						if (isModules) {
 | 
			
		||||
							isNextRulePrelude = isNextNestedSyntax(input, end);
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
| 
						 | 
				
			
			@ -809,11 +809,11 @@ class CssParser extends Parser {
 | 
			
		|||
						if (--blockNestingLevel === 0) {
 | 
			
		||||
							scope = CSS_MODE_TOP_LEVEL;
 | 
			
		||||
 | 
			
		||||
							if (this.allowModeSwitch) {
 | 
			
		||||
							if (isModules) {
 | 
			
		||||
								isNextRulePrelude = true;
 | 
			
		||||
								modeData = undefined;
 | 
			
		||||
							}
 | 
			
		||||
						} else if (this.allowModeSwitch) {
 | 
			
		||||
						} else if (isModules) {
 | 
			
		||||
							isNextRulePrelude = isNextNestedSyntax(input, end);
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
| 
						 | 
				
			
			@ -919,7 +919,7 @@ class CssParser extends Parser {
 | 
			
		|||
				const popped = balanced.pop();
 | 
			
		||||
 | 
			
		||||
				if (
 | 
			
		||||
					this.allowModeSwitch &&
 | 
			
		||||
					isModules &&
 | 
			
		||||
					popped &&
 | 
			
		||||
					(popped[0] === ":local" || popped[0] === ":global")
 | 
			
		||||
				) {
 | 
			
		||||
| 
						 | 
				
			
			@ -959,7 +959,7 @@ class CssParser extends Parser {
 | 
			
		|||
				return end;
 | 
			
		||||
			},
 | 
			
		||||
			pseudoClass: (input, start, end) => {
 | 
			
		||||
				if (this.allowModeSwitch) {
 | 
			
		||||
				if (isModules) {
 | 
			
		||||
					const name = input.slice(start, end).toLowerCase();
 | 
			
		||||
 | 
			
		||||
					if (name === ":global") {
 | 
			
		||||
| 
						 | 
				
			
			@ -998,7 +998,7 @@ class CssParser extends Parser {
 | 
			
		|||
 | 
			
		||||
				balanced.push([name, start, end]);
 | 
			
		||||
 | 
			
		||||
				if (this.allowModeSwitch) {
 | 
			
		||||
				if (isModules) {
 | 
			
		||||
					name = name.toLowerCase();
 | 
			
		||||
 | 
			
		||||
					if (name === ":global") {
 | 
			
		||||
| 
						 | 
				
			
			@ -1015,7 +1015,7 @@ class CssParser extends Parser {
 | 
			
		|||
				return end;
 | 
			
		||||
			},
 | 
			
		||||
			comma: (input, start, end) => {
 | 
			
		||||
				if (this.allowModeSwitch) {
 | 
			
		||||
				if (isModules) {
 | 
			
		||||
					// Reset stack for `:global .class :local .class-other` selector after
 | 
			
		||||
					modeData = undefined;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1033,10 +1033,6 @@ class CssParser extends Parser {
 | 
			
		|||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		if (oldDefaultMode) {
 | 
			
		||||
			this.defaultMode = oldDefaultMode;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/** @type {BuildInfo} */
 | 
			
		||||
		(module.buildInfo).strict = true;
 | 
			
		||||
		/** @type {BuildMeta} */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
import './style.css';
 | 
			
		||||
import * as style1 from './style1.local.css'
 | 
			
		||||
import * as style2 from './style2.global.css'
 | 
			
		||||
import './style3.auto.css';
 | 
			
		||||
import * as style3 from './style4.modules.css'
 | 
			
		||||
 | 
			
		||||
it("should not parse css modules in type: css", () => {
 | 
			
		||||
    const style = getComputedStyle(document.body);
 | 
			
		||||
| 
						 | 
				
			
			@ -10,14 +12,14 @@ it("should not parse css modules in type: css", () => {
 | 
			
		|||
 | 
			
		||||
	expect(css).toMatch(/\:local\(\.foo\)/);
 | 
			
		||||
    expect(css).toMatch(/\:global\(\.bar\)/);
 | 
			
		||||
})
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it("should compile type: css/module", () => {
 | 
			
		||||
    const element = document.createElement(".class2");
 | 
			
		||||
    const style = getComputedStyle(element);
 | 
			
		||||
    expect(style.getPropertyValue("background")).toBe(" green");
 | 
			
		||||
    expect(style1.class1).toBe('-_style1_local_css-class1');
 | 
			
		||||
})
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it("should compile type: css/global", (done) => {
 | 
			
		||||
    const element = document.createElement(".class3");
 | 
			
		||||
| 
						 | 
				
			
			@ -25,4 +27,20 @@ it("should compile type: css/global", (done) => {
 | 
			
		|||
    expect(style.getPropertyValue("color")).toBe(" red");
 | 
			
		||||
    expect(style2.class4).toBe('-_style2_global_css-class4');
 | 
			
		||||
    done()
 | 
			
		||||
})
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it("should not parse css modules in type: css/auto", () => {
 | 
			
		||||
	const style = getComputedStyle(document.body);
 | 
			
		||||
	expect(style.getPropertyValue("background")).toBe(" red");
 | 
			
		||||
	const links = document.getElementsByTagName("link");
 | 
			
		||||
	const css = links[1].sheet.css;
 | 
			
		||||
	expect(css).toMatch(/\:local\(\.baz\)/);
 | 
			
		||||
	expect(css).toMatch(/\:global\(\.qux\)/);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it("should parse css modules in type: css/auto", () => {
 | 
			
		||||
	const element = document.createElement(".class3");
 | 
			
		||||
	const style = getComputedStyle(element);
 | 
			
		||||
	expect(style.getPropertyValue("color")).toBe(" red");
 | 
			
		||||
	expect(style3.class3).toBe('-_style4_modules_css-class3');
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
body {
 | 
			
		||||
    background: red;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:local(.baz) {
 | 
			
		||||
    color: red;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:global(.qux) {
 | 
			
		||||
    color: green;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
.class3 {
 | 
			
		||||
    color: red;
 | 
			
		||||
}
 | 
			
		||||
:global(.class4) {
 | 
			
		||||
    background: green;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,14 @@ module.exports = {
 | 
			
		|||
			{
 | 
			
		||||
				test: /\.global\.css$/i,
 | 
			
		||||
				type: "css/global"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				test: /\.auto\.css$/i,
 | 
			
		||||
				type: "css/auto"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				test: /\.modules\.css$/i,
 | 
			
		||||
				type: "css/auto"
 | 
			
		||||
			}
 | 
			
		||||
		]
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue