Skip to content

Commit

Permalink
allow to generate only exports for css in node
Browse files Browse the repository at this point in the history
add more options to the DeterministicModuleIdsPlugin to allow to opt-in into a fixed mode

optimize module iterations for module id assignment
  • Loading branch information
sokra committed Jan 19, 2022
1 parent e550b2c commit 1489b91
Show file tree
Hide file tree
Showing 24 changed files with 617 additions and 126 deletions.
21 changes: 17 additions & 4 deletions declarations/WebpackOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2723,6 +2723,15 @@ export interface AssetResourceGeneratorOptions {
*/
publicPath?: RawPublicPath;
}
/**
* Options for css handling.
*/
export interface CssExperimentOptions {
/**
* Avoid generating and loading a stylesheet and only embed exports from css into output javascript files.
*/
exportsOnly?: boolean;
}
/**
* Generator options for css modules.
*/
Expand Down Expand Up @@ -2809,10 +2818,6 @@ export interface ExperimentsCommon {
* Enable additional in memory caching of modules that are unchanged and reference only unchanged modules.
*/
cacheUnaffected?: boolean;
/**
* Enable css support.
*/
css?: boolean;
/**
* Apply defaults of next major version.
*/
Expand Down Expand Up @@ -3453,6 +3458,10 @@ export interface ExperimentsExtra {
* Build http(s): urls using a lockfile and resource content cache.
*/
buildHttp?: HttpUriAllowedUris | HttpUriOptions;
/**
* Enable css support.
*/
css?: boolean | CssExperimentOptions;
/**
* Compile entrypoints and import()s only when they are accessed.
*/
Expand All @@ -3466,6 +3475,10 @@ export interface ExperimentsNormalizedExtra {
* Build http(s): urls using a lockfile and resource content cache.
*/
buildHttp?: HttpUriOptions;
/**
* Enable css support.
*/
css?: CssExperimentOptions;
/**
* Compile entrypoints and import()s only when they are accessed.
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/WebpackOptionsApply.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class WebpackOptionsApply extends OptionsApply {

if (options.experiments.css) {
const CssModulesPlugin = require("./css/CssModulesPlugin");
new CssModulesPlugin().apply(compiler);
new CssModulesPlugin(options.experiments.css).apply(compiler);
}

if (options.experiments.lazyCompilation) {
Expand Down
27 changes: 22 additions & 5 deletions lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const {
} = require("./target");

/** @typedef {import("../../declarations/WebpackOptions").CacheOptionsNormalized} CacheOptions */
/** @typedef {import("../../declarations/WebpackOptions").CssExperimentOptions} CssExperimentOptions */
/** @typedef {import("../../declarations/WebpackOptions").EntryDescription} EntryDescription */
/** @typedef {import("../../declarations/WebpackOptions").EntryNormalized} Entry */
/** @typedef {import("../../declarations/WebpackOptions").Experiments} Experiments */
Expand Down Expand Up @@ -160,7 +161,11 @@ const applyWebpackOptionsDefaults = options => {
D(options, "recordsInputPath", false);
D(options, "recordsOutputPath", false);

applyExperimentsDefaults(options.experiments, { production, development });
applyExperimentsDefaults(options.experiments, {
production,
development,
targetProperties
});

const futureDefaults = options.experiments.futureDefaults;

Expand Down Expand Up @@ -265,9 +270,13 @@ const applyWebpackOptionsDefaults = options => {
* @param {Object} options options
* @param {boolean} options.production is production
* @param {boolean} options.development is development mode
* @param {TargetProperties | false} options.targetProperties target properties
* @returns {void}
*/
const applyExperimentsDefaults = (experiments, { production, development }) => {
const applyExperimentsDefaults = (
experiments,
{ production, development, targetProperties }
) => {
D(experiments, "futureDefaults", false);
D(experiments, "backCompat", !experiments.futureDefaults);
D(experiments, "topLevelAwait", experiments.futureDefaults);
Expand All @@ -278,12 +287,20 @@ const applyExperimentsDefaults = (experiments, { production, development }) => {
D(experiments, "lazyCompilation", undefined);
D(experiments, "buildHttp", undefined);
D(experiments, "cacheUnaffected", experiments.futureDefaults);
D(experiments, "css", experiments.futureDefaults);
F(experiments, "css", () => (experiments.futureDefaults ? {} : undefined));

if (typeof experiments.buildHttp === "object") {
D(experiments.buildHttp, "frozen", production);
D(experiments.buildHttp, "upgrade", false);
}

if (typeof experiments.css === "object") {
D(
experiments.css,
"exportsOnly",
!targetProperties || !targetProperties.document
);
}
};

/**
Expand Down Expand Up @@ -461,7 +478,7 @@ const applyJavascriptParserOptionsDefaults = (
* @param {boolean} options.cache is caching enabled
* @param {boolean} options.syncWebAssembly is syncWebAssembly enabled
* @param {boolean} options.asyncWebAssembly is asyncWebAssembly enabled
* @param {boolean} options.css is css enabled
* @param {CssExperimentOptions} options.css is css enabled
* @param {boolean} options.futureDefaults is future defaults enabled
* @returns {void}
*/
Expand Down Expand Up @@ -1083,7 +1100,7 @@ const applyPerformanceDefaults = (performance, { production }) => {
* @param {Object} options options
* @param {boolean} options.production is production
* @param {boolean} options.development is development
* @param {boolean} options.css is css enabled
* @param {CssExperimentOptions} options.css is css enabled
* @param {boolean} options.records using records
* @returns {void}
*/
Expand Down
3 changes: 3 additions & 0 deletions lib/config/normalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ const getNormalizedWebpackOptions = config => {
experiments.lazyCompilation,
options =>
options === true ? {} : options === false ? undefined : options
),
css: optionalNestedConfig(experiments.css, options =>
options === true ? {} : options === false ? undefined : options
)
})),
externals: config.externals,
Expand Down
145 changes: 145 additions & 0 deletions lib/css/CssExportsGenerator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sergey Melyukov @smelukov
*/

"use strict";

const { ReplaceSource, RawSource, ConcatSource } = require("webpack-sources");
const { UsageState } = require("../ExportsInfo");
const Generator = require("../Generator");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");

/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
/** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
/** @typedef {import("../NormalModule")} NormalModule */
/** @typedef {import("../util/Hash")} Hash */

const TYPES = new Set(["javascript"]);

class CssExportsGenerator extends Generator {
constructor() {
super();
}

/**
* @param {NormalModule} module module for which the bailout reason should be determined
* @param {ConcatenationBailoutReasonContext} context context
* @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
*/
getConcatenationBailoutReason(module, context) {
return undefined;
}

/**
* @param {NormalModule} module module for which the code should be generated
* @param {GenerateContext} generateContext context for generate
* @returns {Source} generated code
*/
generate(module, generateContext) {
const source = new ReplaceSource(new RawSource(""));
const initFragments = [];
const cssExports = new Map();

generateContext.runtimeRequirements.add(RuntimeGlobals.module);

const runtimeRequirements = new Set();

const templateContext = {
runtimeTemplate: generateContext.runtimeTemplate,
dependencyTemplates: generateContext.dependencyTemplates,
moduleGraph: generateContext.moduleGraph,
chunkGraph: generateContext.chunkGraph,
module,
runtime: generateContext.runtime,
runtimeRequirements: runtimeRequirements,
concatenationScope: generateContext.concatenationScope,
codeGenerationResults: generateContext.codeGenerationResults,
initFragments,
cssExports
};

const handleDependency = dependency => {
const constructor = /** @type {new (...args: any[]) => Dependency} */ (
dependency.constructor
);
const template = generateContext.dependencyTemplates.get(constructor);
if (!template) {
throw new Error(
"No template for dependency: " + dependency.constructor.name
);
}

template.apply(dependency, source, templateContext);
};
module.dependencies.forEach(handleDependency);

if (generateContext.concatenationScope) {
const source = new ConcatSource();
const usedIdentifiers = new Set();
for (const [k, v] of cssExports) {
let identifier = Template.toIdentifier(k);
let i = 0;
while (usedIdentifiers.has(identifier)) {
identifier = Template.toIdentifier(k + i);
}
usedIdentifiers.add(identifier);
generateContext.concatenationScope.registerExport(k, identifier);
source.add(
`${
generateContext.runtimeTemplate.supportsConst ? "const" : "var"
} ${identifier} = ${JSON.stringify(v)};\n`
);
}
return source;
} else {
const otherUsed =
generateContext.moduleGraph
.getExportsInfo(module)
.otherExportsInfo.getUsed(generateContext.runtime) !==
UsageState.Unused;
if (otherUsed) {
generateContext.runtimeRequirements.add(
RuntimeGlobals.makeNamespaceObject
);
}
return new RawSource(
`${otherUsed ? `${RuntimeGlobals.makeNamespaceObject}(` : ""}${
module.moduleArgument
}.exports = {\n${Array.from(
cssExports,
([k, v]) => `\t${JSON.stringify(k)}: ${JSON.stringify(v)}`
).join(",\n")}\n}${otherUsed ? ")" : ""};`
);
}
}

/**
* @param {NormalModule} module fresh module
* @returns {Set<string>} available types (do not mutate)
*/
getTypes(module) {
return TYPES;
}

/**
* @param {NormalModule} module the module
* @param {string=} type source type
* @returns {number} estimate size of the module
*/
getSize(module, type) {
return 42;
}

/**
* @param {Hash} hash hash that will be modified
* @param {UpdateHashContext} updateHashContext context for updating hash
*/
updateHash(hash, { module }) {}
}

module.exports = CssExportsGenerator;
4 changes: 1 addition & 3 deletions lib/css/CssLoadingRuntimeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,7 @@ class CssLoadingRuntimeModule extends RuntimeModule {
"exports, module",
`module.exports = exports;`
)}).bind(null, exports); ${
withHmr
? "moduleIds.push(token); target[token].cssExports = exports; "
: ""
withHmr ? "moduleIds.push(token); " : ""
}token = ""; exports = {}; exportsWithId.length = 0; }`,
`else if(cc == ${cc("\\")}) { token += data[++i] }`,
`else { token += data[i]; }`
Expand Down
20 changes: 17 additions & 3 deletions lib/css/CssModulesPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ const { compareModulesByIdentifier } = require("../util/comparators");
const createSchemaValidation = require("../util/create-schema-validation");
const createHash = require("../util/createHash");
const memoize = require("../util/memoize");
const CssExportsGenerator = require("./CssExportsGenerator");
const CssGenerator = require("./CssGenerator");
const CssParser = require("./CssParser");

/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").CssExperimentOptions} CssExperimentOptions */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
Expand Down Expand Up @@ -70,6 +72,12 @@ const escapeCss = (str, omitOptionalUnderscore) => {
const plugin = "CssModulesPlugin";

class CssModulesPlugin {
/**
* @param {CssExperimentOptions} options options
*/
constructor({ exportsOnly = false }) {
this._exportsOnly = exportsOnly;
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
Expand Down Expand Up @@ -143,19 +151,25 @@ class CssModulesPlugin {
.for("css")
.tap(plugin, generatorOptions => {
validateGeneratorOptions(generatorOptions);
return new CssGenerator();
return this._exportsOnly
? new CssExportsGenerator()
: new CssGenerator();
});
normalModuleFactory.hooks.createGenerator
.for("css/global")
.tap(plugin, generatorOptions => {
validateGeneratorOptions(generatorOptions);
return new CssGenerator();
return this._exportsOnly
? new CssExportsGenerator()
: new CssGenerator();
});
normalModuleFactory.hooks.createGenerator
.for("css/module")
.tap(plugin, generatorOptions => {
validateGeneratorOptions(generatorOptions);
return new CssGenerator();
return this._exportsOnly
? new CssExportsGenerator()
: new CssGenerator();
});
const orderedCssModulesPerChunk = new WeakMap();
compilation.hooks.afterCodeGeneration.tap("CssModulesPlugin", () => {
Expand Down

0 comments on commit 1489b91

Please sign in to comment.