Skip to content

Commit e2d214a

Browse files
authored
Merge pull request #15200 from webpack/feature/css-exports-in-node
allow to generate only exports for css in node
2 parents 1ed8aaf + 181a2f0 commit e2d214a

27 files changed

+870
-129
lines changed

declarations/WebpackOptions.d.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2723,6 +2723,15 @@ export interface AssetResourceGeneratorOptions {
27232723
*/
27242724
publicPath?: RawPublicPath;
27252725
}
2726+
/**
2727+
* Options for css handling.
2728+
*/
2729+
export interface CssExperimentOptions {
2730+
/**
2731+
* Avoid generating and loading a stylesheet and only embed exports from css into output javascript files.
2732+
*/
2733+
exportsOnly?: boolean;
2734+
}
27262735
/**
27272736
* Generator options for css modules.
27282737
*/
@@ -2809,10 +2818,6 @@ export interface ExperimentsCommon {
28092818
* Enable additional in memory caching of modules that are unchanged and reference only unchanged modules.
28102819
*/
28112820
cacheUnaffected?: boolean;
2812-
/**
2813-
* Enable css support.
2814-
*/
2815-
css?: boolean;
28162821
/**
28172822
* Apply defaults of next major version.
28182823
*/
@@ -3453,6 +3458,10 @@ export interface ExperimentsExtra {
34533458
* Build http(s): urls using a lockfile and resource content cache.
34543459
*/
34553460
buildHttp?: HttpUriAllowedUris | HttpUriOptions;
3461+
/**
3462+
* Enable css support.
3463+
*/
3464+
css?: boolean | CssExperimentOptions;
34563465
/**
34573466
* Compile entrypoints and import()s only when they are accessed.
34583467
*/
@@ -3466,6 +3475,10 @@ export interface ExperimentsNormalizedExtra {
34663475
* Build http(s): urls using a lockfile and resource content cache.
34673476
*/
34683477
buildHttp?: HttpUriOptions;
3478+
/**
3479+
* Enable css support.
3480+
*/
3481+
css?: CssExperimentOptions;
34693482
/**
34703483
* Compile entrypoints and import()s only when they are accessed.
34713484
*/

lib/Compiler.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ class Compiler {
165165
/** @type {AsyncSeriesHook<[Compilation]>} */
166166
afterCompile: new AsyncSeriesHook(["compilation"]),
167167

168+
/** @type {AsyncSeriesHook<[]>} */
169+
readRecords: new AsyncSeriesHook([]),
170+
/** @type {AsyncSeriesHook<[]>} */
171+
emitRecords: new AsyncSeriesHook([]),
172+
168173
/** @type {AsyncSeriesHook<[Compiler]>} */
169174
watchRun: new AsyncSeriesHook(["compiler"]),
170175
/** @type {SyncHook<[Error]>} */
@@ -882,8 +887,32 @@ ${other}`);
882887
* @returns {void}
883888
*/
884889
emitRecords(callback) {
885-
if (!this.recordsOutputPath) return callback();
890+
if (this.hooks.emitRecords.isUsed()) {
891+
if (this.recordsOutputPath) {
892+
asyncLib.parallel(
893+
[
894+
cb => this.hooks.emitRecords.callAsync(cb),
895+
this._emitRecords.bind(this)
896+
],
897+
err => callback(err)
898+
);
899+
} else {
900+
this.hooks.emitRecords.callAsync(callback);
901+
}
902+
} else {
903+
if (this.recordsOutputPath) {
904+
this._emitRecords(callback);
905+
} else {
906+
callback();
907+
}
908+
}
909+
}
886910

911+
/**
912+
* @param {Callback<void>} callback signals when the call finishes
913+
* @returns {void}
914+
*/
915+
_emitRecords(callback) {
887916
const writeFile = () => {
888917
this.outputFileSystem.writeFile(
889918
this.recordsOutputPath,
@@ -926,6 +955,31 @@ ${other}`);
926955
* @returns {void}
927956
*/
928957
readRecords(callback) {
958+
if (this.hooks.readRecords.isUsed()) {
959+
if (this.recordsInputPath) {
960+
asyncLib.parallel([
961+
cb => this.hooks.readRecords.callAsync(cb),
962+
this._readRecords.bind(this)
963+
]);
964+
} else {
965+
this.records = {};
966+
this.hooks.readRecords.callAsync(callback);
967+
}
968+
} else {
969+
if (this.recordsInputPath) {
970+
this._readRecords(callback);
971+
} else {
972+
this.records = {};
973+
callback();
974+
}
975+
}
976+
}
977+
978+
/**
979+
* @param {Callback<void>} callback signals when the call finishes
980+
* @returns {void}
981+
*/
982+
_readRecords(callback) {
929983
if (!this.recordsInputPath) {
930984
this.records = {};
931985
return callback();

lib/WebpackOptionsApply.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ class WebpackOptionsApply extends OptionsApply {
291291

292292
if (options.experiments.css) {
293293
const CssModulesPlugin = require("./css/CssModulesPlugin");
294-
new CssModulesPlugin().apply(compiler);
294+
new CssModulesPlugin(options.experiments.css).apply(compiler);
295295
}
296296

297297
if (options.experiments.lazyCompilation) {

lib/config/defaults.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const {
1616
} = require("./target");
1717

1818
/** @typedef {import("../../declarations/WebpackOptions").CacheOptionsNormalized} CacheOptions */
19+
/** @typedef {import("../../declarations/WebpackOptions").CssExperimentOptions} CssExperimentOptions */
1920
/** @typedef {import("../../declarations/WebpackOptions").EntryDescription} EntryDescription */
2021
/** @typedef {import("../../declarations/WebpackOptions").EntryNormalized} Entry */
2122
/** @typedef {import("../../declarations/WebpackOptions").Experiments} Experiments */
@@ -160,7 +161,11 @@ const applyWebpackOptionsDefaults = options => {
160161
D(options, "recordsInputPath", false);
161162
D(options, "recordsOutputPath", false);
162163

163-
applyExperimentsDefaults(options.experiments, { production, development });
164+
applyExperimentsDefaults(options.experiments, {
165+
production,
166+
development,
167+
targetProperties
168+
});
164169

165170
const futureDefaults = options.experiments.futureDefaults;
166171

@@ -265,9 +270,13 @@ const applyWebpackOptionsDefaults = options => {
265270
* @param {Object} options options
266271
* @param {boolean} options.production is production
267272
* @param {boolean} options.development is development mode
273+
* @param {TargetProperties | false} options.targetProperties target properties
268274
* @returns {void}
269275
*/
270-
const applyExperimentsDefaults = (experiments, { production, development }) => {
276+
const applyExperimentsDefaults = (
277+
experiments,
278+
{ production, development, targetProperties }
279+
) => {
271280
D(experiments, "futureDefaults", false);
272281
D(experiments, "backCompat", !experiments.futureDefaults);
273282
D(experiments, "topLevelAwait", experiments.futureDefaults);
@@ -278,12 +287,20 @@ const applyExperimentsDefaults = (experiments, { production, development }) => {
278287
D(experiments, "lazyCompilation", undefined);
279288
D(experiments, "buildHttp", undefined);
280289
D(experiments, "cacheUnaffected", experiments.futureDefaults);
281-
D(experiments, "css", experiments.futureDefaults);
290+
F(experiments, "css", () => (experiments.futureDefaults ? {} : undefined));
282291

283292
if (typeof experiments.buildHttp === "object") {
284293
D(experiments.buildHttp, "frozen", production);
285294
D(experiments.buildHttp, "upgrade", false);
286295
}
296+
297+
if (typeof experiments.css === "object") {
298+
D(
299+
experiments.css,
300+
"exportsOnly",
301+
!targetProperties || !targetProperties.document
302+
);
303+
}
287304
};
288305

289306
/**
@@ -461,7 +478,7 @@ const applyJavascriptParserOptionsDefaults = (
461478
* @param {boolean} options.cache is caching enabled
462479
* @param {boolean} options.syncWebAssembly is syncWebAssembly enabled
463480
* @param {boolean} options.asyncWebAssembly is asyncWebAssembly enabled
464-
* @param {boolean} options.css is css enabled
481+
* @param {CssExperimentOptions} options.css is css enabled
465482
* @param {boolean} options.futureDefaults is future defaults enabled
466483
* @returns {void}
467484
*/
@@ -1083,7 +1100,7 @@ const applyPerformanceDefaults = (performance, { production }) => {
10831100
* @param {Object} options options
10841101
* @param {boolean} options.production is production
10851102
* @param {boolean} options.development is development
1086-
* @param {boolean} options.css is css enabled
1103+
* @param {CssExperimentOptions} options.css is css enabled
10871104
* @param {boolean} options.records using records
10881105
* @returns {void}
10891106
*/

lib/config/normalization.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ const getNormalizedWebpackOptions = config => {
180180
experiments.lazyCompilation,
181181
options =>
182182
options === true ? {} : options === false ? undefined : options
183+
),
184+
css: optionalNestedConfig(experiments.css, options =>
185+
options === true ? {} : options === false ? undefined : options
183186
)
184187
})),
185188
externals: config.externals,

lib/css/CssExportsGenerator.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Sergey Melyukov @smelukov
4+
*/
5+
6+
"use strict";
7+
8+
const { ReplaceSource, RawSource, ConcatSource } = require("webpack-sources");
9+
const { UsageState } = require("../ExportsInfo");
10+
const Generator = require("../Generator");
11+
const RuntimeGlobals = require("../RuntimeGlobals");
12+
const Template = require("../Template");
13+
14+
/** @typedef {import("webpack-sources").Source} Source */
15+
/** @typedef {import("../Dependency")} Dependency */
16+
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
17+
/** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
18+
/** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
19+
/** @typedef {import("../NormalModule")} NormalModule */
20+
/** @typedef {import("../util/Hash")} Hash */
21+
22+
const TYPES = new Set(["javascript"]);
23+
24+
class CssExportsGenerator extends Generator {
25+
constructor() {
26+
super();
27+
}
28+
29+
// TODO add getConcatenationBailoutReason to allow concatenation
30+
// but how to make it have a module id
31+
32+
/**
33+
* @param {NormalModule} module module for which the code should be generated
34+
* @param {GenerateContext} generateContext context for generate
35+
* @returns {Source} generated code
36+
*/
37+
generate(module, generateContext) {
38+
const source = new ReplaceSource(new RawSource(""));
39+
const initFragments = [];
40+
const cssExports = new Map();
41+
42+
generateContext.runtimeRequirements.add(RuntimeGlobals.module);
43+
44+
const runtimeRequirements = new Set();
45+
46+
const templateContext = {
47+
runtimeTemplate: generateContext.runtimeTemplate,
48+
dependencyTemplates: generateContext.dependencyTemplates,
49+
moduleGraph: generateContext.moduleGraph,
50+
chunkGraph: generateContext.chunkGraph,
51+
module,
52+
runtime: generateContext.runtime,
53+
runtimeRequirements: runtimeRequirements,
54+
concatenationScope: generateContext.concatenationScope,
55+
codeGenerationResults: generateContext.codeGenerationResults,
56+
initFragments,
57+
cssExports
58+
};
59+
60+
const handleDependency = dependency => {
61+
const constructor = /** @type {new (...args: any[]) => Dependency} */ (
62+
dependency.constructor
63+
);
64+
const template = generateContext.dependencyTemplates.get(constructor);
65+
if (!template) {
66+
throw new Error(
67+
"No template for dependency: " + dependency.constructor.name
68+
);
69+
}
70+
71+
template.apply(dependency, source, templateContext);
72+
};
73+
module.dependencies.forEach(handleDependency);
74+
75+
if (generateContext.concatenationScope) {
76+
const source = new ConcatSource();
77+
const usedIdentifiers = new Set();
78+
for (const [k, v] of cssExports) {
79+
let identifier = Template.toIdentifier(k);
80+
let i = 0;
81+
while (usedIdentifiers.has(identifier)) {
82+
identifier = Template.toIdentifier(k + i);
83+
}
84+
usedIdentifiers.add(identifier);
85+
generateContext.concatenationScope.registerExport(k, identifier);
86+
source.add(
87+
`${
88+
generateContext.runtimeTemplate.supportsConst ? "const" : "var"
89+
} ${identifier} = ${JSON.stringify(v)};\n`
90+
);
91+
}
92+
return source;
93+
} else {
94+
const otherUsed =
95+
generateContext.moduleGraph
96+
.getExportsInfo(module)
97+
.otherExportsInfo.getUsed(generateContext.runtime) !==
98+
UsageState.Unused;
99+
if (otherUsed) {
100+
generateContext.runtimeRequirements.add(
101+
RuntimeGlobals.makeNamespaceObject
102+
);
103+
}
104+
return new RawSource(
105+
`${otherUsed ? `${RuntimeGlobals.makeNamespaceObject}(` : ""}${
106+
module.moduleArgument
107+
}.exports = {\n${Array.from(
108+
cssExports,
109+
([k, v]) => `\t${JSON.stringify(k)}: ${JSON.stringify(v)}`
110+
).join(",\n")}\n}${otherUsed ? ")" : ""};`
111+
);
112+
}
113+
}
114+
115+
/**
116+
* @param {NormalModule} module fresh module
117+
* @returns {Set<string>} available types (do not mutate)
118+
*/
119+
getTypes(module) {
120+
return TYPES;
121+
}
122+
123+
/**
124+
* @param {NormalModule} module the module
125+
* @param {string=} type source type
126+
* @returns {number} estimate size of the module
127+
*/
128+
getSize(module, type) {
129+
return 42;
130+
}
131+
132+
/**
133+
* @param {Hash} hash hash that will be modified
134+
* @param {UpdateHashContext} updateHashContext context for updating hash
135+
*/
136+
updateHash(hash, { module }) {}
137+
}
138+
139+
module.exports = CssExportsGenerator;

lib/css/CssLoadingRuntimeModule.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,7 @@ class CssLoadingRuntimeModule extends RuntimeModule {
192192
"exports, module",
193193
`module.exports = exports;`
194194
)}).bind(null, exports); ${
195-
withHmr
196-
? "moduleIds.push(token); target[token].cssExports = exports; "
197-
: ""
195+
withHmr ? "moduleIds.push(token); " : ""
198196
}token = ""; exports = {}; exportsWithId.length = 0; }`,
199197
`else if(cc == ${cc("\\")}) { token += data[++i] }`,
200198
`else { token += data[i]; }`

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy