Skip to content

Commit

Permalink
Fix: Support replacement tokens in the webpack filename (#189)
Browse files Browse the repository at this point in the history
Webpack only supports token replacement for chunks and modules, not plain assets. Instead, teach the plugin to do token replacement the same way that webpack does it for supported types.

Co-authored-by: Ben Lowery <[email protected]>
  • Loading branch information
blowery and blowery authored Dec 15, 2023
1 parent 9c585d8 commit ee3ac48
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 9 deletions.
19 changes: 19 additions & 0 deletions packages/webpack-plugin/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,25 @@ describe('webpack-plugin-stylex', () => {
});
});

it('supports [contenthash] in output filename', async () => {
const compiler = createCompiler('index.js', {
filename: 'stylex.[contenthash].css',
});
return new Promise((resolve, reject) => {
compiler.run((error, stats) => {
try {
expect(error).toBe(null);
expect(
assetExists('stylex.09fffeec3686166d4767.css', compiler, stats),
).toBe(true);
resolve();
} catch (e) {
reject(e);
}
});
});
});

describe('when in dev mode', () => {
it('preserves stylex.inject calls and does not extract CSS', (done) => {
const compiler = createCompiler('index.js', { dev: true });
Expand Down
67 changes: 58 additions & 9 deletions packages/webpack-plugin/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,66 @@ class StylexPlugin {
},
);
} else {
// We'll emit an asset ourselves. This comes with some complications in from Webpack.
// If the filename contains replacement tokens, like [contenthash], we need to
// process those tokens ourselves. Webpack does provide a way to reuse the configured
// hashing functions. We'll take advantage of that to process tokens.
const getContentHash = (source) => {
const { outputOptions } = compilation;
const { hashDigest, hashDigestLength, hashFunction, hashSalt } =
outputOptions;
const hash = compiler.webpack.util.createHash(hashFunction);

if (hashSalt) {
hash.update(hashSalt);
}

hash.update(source);

const fullContentHash = hash.digest(hashDigest);

return fullContentHash.toString().slice(0, hashDigestLength);
};
// Consume collected rules and emit the stylex CSS asset
compilation.hooks.additionalAssets.tap(PLUGIN_NAME, () => {
try {
const collectedCSS = getStyleXRules();
if (collectedCSS) {
compilation.emitAsset(this.filename, new RawSource(collectedCSS));
compilation.hooks.processAssets.tap(
{
name: PLUGIN_NAME,
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
},
() => {
try {
const collectedCSS = getStyleXRules();

if (collectedCSS) {
// build up a content hash for the rules using webpack's configured hashing functions
const contentHash = getContentHash(collectedCSS);

// pretend to be a chunk so we can reuse the webpack routine to process the filename and do token replacement
// see https://github.com/webpack/webpack/blob/main/lib/Compilation.js#L4733
// see https://github.com/webpack/webpack/blob/main/lib/TemplatedPathPlugin.js#L102
const data = {
filename: this.filename,
contentHash: contentHash,
chunk: {
id: this.filename,
name: path.parse(this.filename).name,
hash: contentHash,
},
};

const { path: hashedPath, info: assetsInfo } =
compilation.getPathWithInfo(data.filename, data);
compilation.emitAsset(
hashedPath,
new RawSource(collectedCSS),
assetsInfo,
);
}
} catch (e) {
compilation.errors.push(e);
}
} catch (e) {
compilation.errors.push(e);
}
});
},
);
}
});
}
Expand Down

0 comments on commit ee3ac48

Please sign in to comment.