rsnext/packages/next/build/webpack/plugins/subresource-integrity-plugin.ts
Shu Ding b18489461b
Make sure polyfills are added for browsers without module support (#41029)
This PR makes sure that the same polyfills are added in app dir for
browsers that match `nomodule`. Main difference from the polyfills in
pages is that the script tags here cannot have `defer` as all other
scripts will be async by default, and polyfills must be executed before
all of them.

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-09-29 16:22:21 -07:00

64 lines
2.2 KiB
TypeScript

import { webpack, sources } from 'next/dist/compiled/webpack/webpack'
import crypto from 'crypto'
import { SUBRESOURCE_INTEGRITY_MANIFEST } from '../../../shared/lib/constants'
const PLUGIN_NAME = 'SubresourceIntegrityPlugin'
export type SubresourceIntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512'
export class SubresourceIntegrityPlugin {
constructor(private readonly algorithm: SubresourceIntegrityAlgorithm) {}
public apply(compiler: webpack.Compiler) {
compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => {
compilation.hooks.afterOptimizeAssets.tap(
{
name: PLUGIN_NAME,
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
},
(assets) => {
// Collect all the assets.
let files = new Set<string>()
for (const asset of compilation.getAssets()) {
files.add(asset.name)
}
// For each file, deduped, calculate the file hash.
const hashes: Record<string, string> = {}
for (const file of files.values()) {
// Get the buffer for the asset.
const asset = assets[file]
if (!asset) {
throw new Error(`could not get asset: ${file}`)
}
// Get the buffer for the asset.
const buffer = asset.buffer()
// Create the hash for the content.
const hash = crypto
.createHash(this.algorithm)
.update(buffer)
.digest()
.toString('base64')
hashes[file] = `${this.algorithm}-${hash}`
}
const json = JSON.stringify(hashes, null, 2)
const file = 'server/' + SUBRESOURCE_INTEGRITY_MANIFEST
assets[file + '.js'] = new sources.RawSource(
'self.__SUBRESOURCE_INTEGRITY_MANIFEST=' + json
// Work around webpack 4 type of RawSource being used
// TODO: use webpack 5 type by default
) as unknown as webpack.sources.RawSource
assets[file + '.json'] = new sources.RawSource(
json
// Work around webpack 4 type of RawSource being used
// TODO: use webpack 5 type by default
) as unknown as webpack.sources.RawSource
}
)
})
}
}