vercel/next.js · #99826
Add webpack utils
test/e2e/app-dir/webpack-loader-utils/app/input.utils-test.jsadded1 + / 0 −
@@ -0,0 +1 @@+export default nulltest/e2e/app-dir/webpack-loader-utils/app/layout.tsxadded9 + / 0 −
@@ -0,0 +1,9 @@+import type { ReactNode } from 'react'++export default function Root({ children }: { children: ReactNode }) {+ return (+ <html>+ <body>{children}</body>+ </html>+ )+}test/e2e/app-dir/webpack-loader-utils/app/page.tsxadded11 + / 0 −
@@ -0,0 +1,11 @@+import utils from './input.utils-test'+import styleLoaderSource from './styles.style-test.txt'++export default function Page() {+ return (+ <>+ <pre id="utils">{JSON.stringify(utils)}</pre>+ <pre id="style-loader-source">{styleLoaderSource}</pre>+ </>+ )+}test/e2e/app-dir/webpack-loader-utils/app/styles.style-test.txtadded1 + / 0 −
@@ -0,0 +1 @@+.foo { color: red }test/e2e/app-dir/webpack-loader-utils/capture-loader.jsadded4 + / 0 −
@@ -0,0 +1,4 @@+module.exports = function (source) {+ // Export the pitched source so its requests can be checked without executing them.+ return `export default ${JSON.stringify(source)}`+}test/e2e/app-dir/webpack-loader-utils/next.config.tsadded22 + / 0 −
@@ -0,0 +1,22 @@+import type { NextConfig } from 'next'++const utilsLoader = './utils-loader.js'+const styleLoaders = ['./capture-loader.js', 'style-loader']++const nextConfig: NextConfig = {+ turbopack: {+ rules: {+ '*.utils-test.js': { loaders: [utilsLoader] },+ '*.style-test.txt': { loaders: styleLoaders, as: '*.js' },+ },+ },+ webpack(config) {+ config.module.rules.push(+ { test: /\.utils-test\.js$/, use: [utilsLoader] },+ { test: /\.style-test\.txt$/, use: styleLoaders }+ )+ return config+ },+}++export default nextConfigtest/e2e/app-dir/webpack-loader-utils/types.d.tsadded4 + / 0 −
@@ -0,0 +1,4 @@+declare module '*.style-test.txt' {+ const source: string+ export default source+}test/e2e/app-dir/webpack-loader-utils/utils-loader.jsadded14 + / 0 −
@@ -0,0 +1,14 @@+module.exports = function () {+ const { absolutify, contextify, createHash } = this.utils+ return `export default ${JSON.stringify({+ relative: contextify(this.rootContext, this.resourcePath + '?q'),+ absolute:+ absolutify(this.rootContext, './app/input.utils-test.js') ===+ this.resourcePath,+ hash: createHash().update('abc').digest('hex'),+ xxhash: createHash('xxhash64').update('abc').digest('hex'),+ webpack: this.webpack === true,+ compiler: this._compiler !== undefined,+ mode: this.mode,+ })}`+}test/e2e/app-dir/webpack-loader-utils/webpack-loader-utils.test.tsadded30 + / 0 −
@@ -0,0 +1,30 @@+import { nextTestSetup } from 'e2e-utils'++describe('webpack-loader-utils', () => {+ const { next, isNextDev } = nextTestSetup({+ files: __dirname,+ dependencies: {+ 'style-loader': '4.0.0',+ },+ })++ it('exposes path and hash utilities to loaders', async () => {+ const $ = await next.render$('/')+ expect(JSON.parse($('#utils').text())).toEqual({+ relative: './app/input.utils-test.js?q',+ absolute: true,+ hash: '44bc2cf5ad770999',+ xxhash: '44bc2cf5ad770999',+ webpack: !process.env.IS_TURBOPACK_TEST,+ compiler: !process.env.IS_TURBOPACK_TEST,+ mode: isNextDev ? 'development' : 'production',+ })+ })++ it('runs style-loader pitching with the real loader context', async () => {+ const $ = await next.render$('/')+ const source = $('#style-loader-source').text()+ expect(source).toContain('injectStylesIntoStyleTag.js')+ expect(source).toContain('!!./styles.style-test.txt')+ })+})test/unit/turbopack-loader-utils/utils.test.tsadded294 + / 0 −
@@ -0,0 +1,294 @@+import path from 'path'+import {+ absolutify,+ contextify,+ createHash,+ type WebpackHash,+} from '../../../turbopack/crates/turbopack-node/js/src/transforms/webpack-loaders-utils'++const webpackDir = path.dirname(+ require.resolve('webpack/package.json', {+ paths: [path.join(__dirname, '../../../packages/next')],+ })+)+const webpackCreateHash: (+ algorithm: string | (new () => WebpackHash)+) => WebpackHash = require(path.join(webpackDir, 'lib/util/createHash'))+const identifier = require(path.join(webpackDir, 'lib/util/identifier'))++describe('webpack loader utils', () => {+ it.each([+ ['/root', '/root/src/a.js'],+ ['/root', '/root/src/a.js?q=1#frag'],+ ['/root', '/other/a.js'],+ ['/root', '/root'],+ ['/root/src', '/root'],+ ['/root', '!!/root/loader.js?q!/root/src/a.js'],+ ['/root', '/a-regexp/'],+ ['/root', 'pkg/file'],+ ['/root', './loader.js!../a.js?q=1'],+ ['/root', './loader.js|../a.js'],+ ['/root', ''],+ ['C:\\root', 'C:\\root\\src\\a.js?q=1'],+ ['C:\\root', 'D:\\other\\a.js'],+ ['C:\\root', '!!C:\\root\\loader.js!C:\\root\\src\\a.js'],+ ['C:\\root', './loader.js!../src/a.js'],+ ['\\\\server\\share', '\\\\server\\share\\a.js'],+ ])('matches webpack identifier helpers: %s, %s', (context, request) => {+ expect(contextify(context, request)).toBe(+ identifier.contextify(context, request)+ )+ expect(absolutify(context, request)).toBe(+ identifier.absolutify(context, request)+ )+ })++ describe.each(['md4', 'xxhash64', 'sha256'])('%s', (algorithm) => {+ it('matches webpack for block boundaries and larger binary inputs', () => {+ for (const length of [+ 0, 1, 7, 8, 15, 16, 31, 32, 33, 55, 56, 63, 64, 65, 127, 128, 255, 1000,+ 16368, 65536, 65537,+ ]) {+ const input = Buffer.alloc(length)+ for (let i = 0; i < length; i++) input[i] = (i * 31 + 17) & 255+ for (const encoding of [+ 'hex',+ 'base64',+ 'base64url',+ 'binary',+ undefined,+ ] as const) {+ const actual = createHash(algorithm)+ const expected = webpackCreateHash(algorithm)+ for (let offset = 0; offset < length; offset += 37) {+ const chunk = input.subarray(offset, offset + 37)+ expect(actual.update(chunk)).toBe(actual)+ expected.update(chunk)+ }+ expect(actual.digest(encoding)).toEqual(expected.digest(encoding))+ }+ }+ })++ it('matches webpack for encodings and batched strings', () => {+ const updates: Array<Array<[string | Buffer, BufferEncoding?]>> = [+ [['abc'], ['def']],+ [['\ud83d'], ['\ude00']],+ [+ ['\ud83d', 'utf8'],+ ['\ude00', 'utf8'],+ ],+ [+ ['61', 'hex'],+ ['62', 'hex'],+ ],+ [+ ['6', 'hex'],+ ['1', 'hex'],+ ],+ [+ ['YQ==', 'base64'],+ ['Yg==', 'base64'],+ ],+ [['héllo', 'latin1'], [Buffer.from([0, 255])], ['world']],+ [['a'.repeat(2001)], ['b'.repeat(17000)]],+ [['a'.repeat(16367) + '\ud83d\ude00']],+ ]+ for (const parts of updates) {+ const actual = createHash(algorithm)+ const expected = webpackCreateHash(algorithm)+ let failed = false+ for (const [data, encoding] of parts) {+ try {+ expected.update(data, encoding)+ } catch {+ expect(() => actual.update(data, encoding)).toThrow()+ failed = true+ break+ }+ actual.update(data, encoding)+ }+ if (!failed) expect(actual.digest('hex')).toBe(expected.digest('hex'))+ }+ })++ it('consumes buffer contents at update time', () => {+ const buffer = Buffer.from('abc')+ const actual = createHash(algorithm).update(buffer)+ const expected = webpackCreateHash(algorithm).update(buffer)+ buffer.fill(0)+ expect(actual.digest('hex')).toBe(expected.digest('hex'))+ })++ it('matches webpack across every short-input split and reused buffers', () => {+ const input = Buffer.alloc(129)+ for (let i = 0; i < input.length; i++) input[i] = (i * 73 + 151) & 255+ for (let length = 0; length <= input.length; length++) {+ const expected = webpackCreateHash(algorithm)+ .update(input.subarray(0, length))+ .digest('hex')+ for (let split = 0; split <= length; split++) {+ const actual = createHash(algorithm)+ actual.update(input.subarray(0, split))+ actual.update(Buffer.alloc(0))+ actual.update(input.subarray(split, length))+ expect(actual.digest('hex')).toBe(expected)+ }+ }+ const scratch = Buffer.alloc(79)+ const actual = createHash(algorithm)+ const expected = webpackCreateHash(algorithm)+ for (let i = 0; i < 1000; i++) {+ scratch.fill(i & 255)+ actual.update(scratch)+ expected.update(scratch)+ }+ scratch.fill(0)+ expect(actual.digest('hex')).toBe(expected.digest('hex'))+ })++ it('matches webpack for a long incremental stream', () => {+ const chunk = Buffer.alloc(65536, 0xa5)+ const actual = createHash(algorithm)+ const expected = webpackCreateHash(algorithm)+ for (let i = 0; i < 128; i++) {+ actual.update(chunk)+ expected.update(chunk)+ }+ expect(actual.digest('hex')).toBe(expected.digest('hex'))+ })++ it('matches webpack for varied binary data and chunk boundaries', () => {+ let seed = 0x9e3779b9+ const random = () => {+ seed ^= seed << 13+ seed ^= seed >>> 17+ seed ^= seed << 5+ return seed >>> 0+ }+ for (let trial = 0; trial < 200; trial++) {+ const input = Buffer.alloc(random() % 4096)+ for (let i = 0; i < input.length; i++) input[i] = random() >>> 24+ const actual = createHash(algorithm)+ for (let offset = 0; offset < input.length; ) {+ const end = Math.min(input.length, offset + 1 + (random() % 97))+ actual.update(input.subarray(offset, end))+ offset = end+ }+ expect(actual.digest('hex')).toBe(+ webpackCreateHash(algorithm).update(input).digest('hex')+ )+ }+ })++ it('handles buffers with unaligned byte offsets', () => {+ const backing = Buffer.alloc(1024)+ for (let i = 0; i < backing.length; i++) backing[i] = (i * 113 + 79) & 255+ for (let offset = 0; offset < 16; offset++) {+ for (const length of [1, 31, 32, 33, 63, 64, 65, 511]) {+ const input = backing.subarray(offset, offset + length)+ expect(createHash(algorithm).update(input).digest('hex')).toBe(+ webpackCreateHash(algorithm).update(input).digest('hex')+ )+ }+ }+ })++ it('matches webpack for large aligned and unaligned buffers', () => {+ const backing = Buffer.alloc(1048584)+ let seed = 0x12345678+ for (let i = 0; i < backing.length; i++) {+ seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0+ backing[i] = seed >>> 24+ }+ for (const offset of [0, 1, 3, 4]) {+ const input = backing.subarray(offset, offset + 1048576)+ expect(createHash(algorithm).update(input).digest('hex')).toBe(+ webpackCreateHash(algorithm).update(input).digest('hex')+ )+ }+ })++ it('matches webpack for deterministic mixed update sequences', () => {+ let seed = 12345+ const random = () => {+ seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0+ return seed+ }+ for (let trial = 0; trial < 100; trial++) {+ const actual = createHash(algorithm)+ const expected = webpackCreateHash(algorithm)+ for (let update = 0; update < 10; update++) {+ const input = Buffer.alloc(random() % 1000)+ for (let i = 0; i < input.length; i++) input[i] = random() & 255+ if (random() % 2) {+ const encoding = ['hex', 'base64', 'latin1'][+ random() % 3+ ] as BufferEncoding+ actual.update(input.toString(encoding), encoding)+ expected.update(input.toString(encoding), encoding)+ } else {+ actual.update(input)+ expected.update(input)+ }+ }+ expect(actual.digest('hex')).toBe(expected.digest('hex'))+ }+ })+ })++ it('defaults to xxhash64', () => {+ expect(createHash().update('abc').digest('hex')).toBe(+ webpackCreateHash('xxhash64').update('abc').digest('hex')+ )+ expect(createHash(undefined).update('abc').digest('hex')).toBe(+ webpackCreateHash('xxhash64').update('abc').digest('hex')+ )+ })++ it('matches webpack for custom hash constructors', () => {+ class CustomHash implements WebpackHash {+ parts: string[] = []+ update(data: string | Buffer) {+ this.parts.push(String(data))+ return this+ }+ digest() {+ return this.parts.join('+')+ }+ }+ expect(createHash(CustomHash).update('a').update('b').digest()).toBe(+ webpackCreateHash(CustomHash).update('a').update('b').digest()+ )+ })++ it('matches webpack debug digest format and nested digests', () => {+ for (const factory of [createHash, webpackCreateHash]) {+ const hash = factory('debug')+ hash.update(Buffer.from('abc'))+ const first = hash.digest('base64') as string+ expect(Buffer.from(first, 'hex').toString()).toMatch(+ /^@webpack-debug-digest@\[abc\]\(.*\)\n$/+ )+ const nested = factory('debug').update(first).digest() as string+ expect(Buffer.from(nested, 'hex').toString()).toContain('[[abc]')+ }+ })++ it('does not silently substitute an algorithm for native-md4 or unknown hashes', () => {+ for (const algorithm of ['native-md4', 'not-an-algorithm']) {+ let expected: string | Buffer+ try {+ expected = webpackCreateHash(algorithm).update('abc').digest('hex')+ } catch {+ expect(() =>+ createHash(algorithm).update('abc').digest('hex')+ ).toThrow()+ continue+ }+ expect(createHash(algorithm).update('abc').digest('hex')).toEqual(+ expected+ )+ }+ })+})turbopack/crates/turbopack-node/js/src/transforms/webpack-loaders-utils.tsadded648 + / 0 −
@@ -0,0 +1,648 @@+import { createHash as nodeCreateHash } from 'crypto'+import path from 'path'++const WINDOWS_ABS_PATH_REGEXP = /^[a-zA-Z]:[\\/]/++function relativePathToRequest(relativePath: string): string {+ if (relativePath === '') return './.'+ if (relativePath === '..') return '../.'+ if (relativePath.startsWith('../')) return relativePath+ return `./${relativePath}`+}++// Matches webpack's lib/util/identifier.js, including regexp-like requests.+function absoluteToRequest(context: string, request: string): string {+ const windows = WINDOWS_ABS_PATH_REGEXP.test(request)+ if (!windows && request[0] !== '/') return request+ if (!windows && request.length > 1 && request.endsWith('/')) return request++ const queryIndex = request.indexOf('?')+ const resource = queryIndex === -1 ? request : request.slice(0, queryIndex)+ let relative = windows+ ? path.win32.relative(context, resource)+ : path.posix.relative(context, resource)+ if (!windows || !WINDOWS_ABS_PATH_REGEXP.test(relative)) {+ relative = relativePathToRequest(+ windows ? relative.replace(/\\/g, '/') : relative+ )+ }+ return relative + (queryIndex === -1 ? '' : request.slice(queryIndex))+}++export function contextify(context: string, request: string): string {+ return request+ .split('!')+ .map((part) => absoluteToRequest(context, part))+ .join('!')+}++export function absolutify(context: string, request: string): string {+ return request+ .split('!')+ .map((part) =>+ part.startsWith('./') || part.startsWith('../')+ ? path.join(context, part)+ : part+ )+ .join('!')+}++export interface WebpackHash {+ update(data: string | Buffer, inputEncoding?: BufferEncoding): WebpackHash+ digest(encoding?: BufferEncoding): string | Buffer+}++type HashConstructor = new () => WebpackHash++// webpack's wasm hash encodes at most this many characters per memory page.+const MAX_SHORT_STRING = Math.floor((65536 - 64) / 4) & ~3++// webpack batches consecutive unencoded strings before converting to bytes.+// This also preserves surrogate pairs split across update calls.+class BatchedHash implements WebpackHash {+ private string: string | undefined = undefined+ private encoding: BufferEncoding | undefined = undefined++ constructor(private hash: IncrementalHash) {}++ update(data: string | Buffer, inputEncoding?: BufferEncoding) {+ if (this.string !== undefined) {+ if (+ typeof data === 'string' &&+ inputEncoding === this.encoding &&+ this.string.length + data.length < MAX_SHORT_STRING+ ) {+ this.string += data+ return this+ }+ this.flush()+ }+ if (+ typeof data === 'string' &&+ data.length < MAX_SHORT_STRING &&+ (!inputEncoding || !inputEncoding.startsWith('ba'))+ ) {+ this.string = data+ this.encoding = inputEncoding+ } else this.append(data, inputEncoding)+ return this+ }++ private append(data: string | Buffer, encoding?: BufferEncoding) {+ if (typeof data === 'string') {+ do {+ this.hash.update(Buffer.from(data.slice(0, MAX_SHORT_STRING), encoding))+ data = data.slice(MAX_SHORT_STRING)+ } while (data.length > 0)+ } else this.hash.update(data)+ }++ private flush() {+ if (this.string !== undefined) {+ this.append(this.string, this.encoding)+ this.string = undefined+ }+ }++ digest(encoding?: BufferEncoding) {+ this.flush()+ const result = this.hash.digest()+ return encoding && encoding !== 'binary'+ ? result.toString(encoding)+ : result+ }+}++abstract class IncrementalHash {+ protected buffer: Buffer+ protected buffered = 0+ protected length = 0++ constructor(private blockSize: number) {+ this.buffer = Buffer.alloc(blockSize)+ }++ update(input: Buffer) {+ this.length += input.length+ let offset = 0+ if (this.buffered > 0) {+ const take = Math.min(this.blockSize - this.buffered, input.length)+ input.copy(this.buffer, this.buffered, 0, take)+ this.buffered += take+ offset = take+ if (this.buffered < this.blockSize) return+ this.processBlock(this.buffer, 0)+ this.buffered = 0+ }+ const end = input.length - ((input.length - offset) % this.blockSize)+ if (offset < end) this.processBlocks(input, offset, end)+ offset = end+ if (offset < input.length) {+ input.copy(this.buffer, 0, offset)+ this.buffered = input.length - offset+ }+ }++ protected abstract processBlock(input: Buffer, offset: number): void+ protected processBlocks(input: Buffer, offset: number, end: number) {+ for (; offset < end; offset += this.blockSize)+ this.processBlock(input, offset)+ }+ abstract digest(): Buffer+}++// Matches the observable behavior of webpack's BulkUpdateDecorator.+class BulkHash implements WebpackHash {+ private hash: WebpackHash | undefined = undefined+ private buffer = ''++ constructor(private factory: () => WebpackHash) {}++ update(data: string | Buffer, inputEncoding?: BufferEncoding) {+ if (+ inputEncoding !== undefined ||+ typeof data !== 'string' ||+ data.length > 2000+ ) {+ const hash = (this.hash ??= this.factory())+ if (this.buffer.length > 0) {+ hash.update(this.buffer)+ this.buffer = ''+ }+ hash.update(data, inputEncoding)+ } else {+ this.buffer += data+ if (this.buffer.length > 2000) {+ ;(this.hash ??= this.factory()).update(this.buffer)+ this.buffer = ''+ }+ }+ return this+ }++ digest(encoding?: BufferEncoding) {+ const hash = (this.hash ??= this.factory())+ if (this.buffer.length > 0) hash.update(this.buffer)+ const result = hash.digest(encoding)+ return typeof result === 'string' ? result : result.toString()+ }+}++// RFC 1320. Unrolled rounds avoid per-block allocations and round dispatch.+class Md4Hash extends IncrementalHash {+ private a = 0x67452301+ private b = 0xefcdab89 | 0+ private c = 0x98badcfe | 0+ private d = 0x10325476+ private words = new Int32Array(16)++ constructor() {+ super(64)+ }++ protected processBlock(input: Buffer, offset: number) {+ this.processBlocks(input, offset, offset + 64)+ }++ protected processBlocks(input: Buffer, offset: number, end: number) {+ const x = this.words+ let a = this.a | 0,+ b = this.b | 0,+ c = this.c | 0,+ d = this.d | 0+ for (; offset < end; offset += 64) {+ for (let i = 0; i < 16; i++) {+ const wordOffset = offset + i * 4+ x[i] =+ input[wordOffset] |+ (input[wordOffset + 1] << 8) |+ (input[wordOffset + 2] << 16) |+ (input[wordOffset + 3] << 24)+ }+ const previousA = a,+ previousB = b,+ previousC = c,+ previousD = d+ a = (a + ((b & c) | (~b & d)) + x[0]) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + ((a & b) | (~a & c)) + x[1]) | 0+ d = (d << 7) | (d >>> 25)+ c = (c + ((d & a) | (~d & b)) + x[2]) | 0+ c = (c << 11) | (c >>> 21)+ b = (b + ((c & d) | (~c & a)) + x[3]) | 0+ b = (b << 19) | (b >>> 13)+ a = (a + ((b & c) | (~b & d)) + x[4]) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + ((a & b) | (~a & c)) + x[5]) | 0+ d = (d << 7) | (d >>> 25)+ c = (c + ((d & a) | (~d & b)) + x[6]) | 0+ c = (c << 11) | (c >>> 21)+ b = (b + ((c & d) | (~c & a)) + x[7]) | 0+ b = (b << 19) | (b >>> 13)+ a = (a + ((b & c) | (~b & d)) + x[8]) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + ((a & b) | (~a & c)) + x[9]) | 0+ d = (d << 7) | (d >>> 25)+ c = (c + ((d & a) | (~d & b)) + x[10]) | 0+ c = (c << 11) | (c >>> 21)+ b = (b + ((c & d) | (~c & a)) + x[11]) | 0+ b = (b << 19) | (b >>> 13)+ a = (a + ((b & c) | (~b & d)) + x[12]) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + ((a & b) | (~a & c)) + x[13]) | 0+ d = (d << 7) | (d >>> 25)+ c = (c + ((d & a) | (~d & b)) + x[14]) | 0+ c = (c << 11) | (c >>> 21)+ b = (b + ((c & d) | (~c & a)) + x[15]) | 0+ b = (b << 19) | (b >>> 13)+ a = (a + ((b & c) | (b & d) | (c & d)) + x[0] + 0x5a827999) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + ((a & b) | (a & c) | (b & c)) + x[4] + 0x5a827999) | 0+ d = (d << 5) | (d >>> 27)+ c = (c + ((d & a) | (d & b) | (a & b)) + x[8] + 0x5a827999) | 0+ c = (c << 9) | (c >>> 23)+ b = (b + ((c & d) | (c & a) | (d & a)) + x[12] + 0x5a827999) | 0+ b = (b << 13) | (b >>> 19)+ a = (a + ((b & c) | (b & d) | (c & d)) + x[1] + 0x5a827999) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + ((a & b) | (a & c) | (b & c)) + x[5] + 0x5a827999) | 0+ d = (d << 5) | (d >>> 27)+ c = (c + ((d & a) | (d & b) | (a & b)) + x[9] + 0x5a827999) | 0+ c = (c << 9) | (c >>> 23)+ b = (b + ((c & d) | (c & a) | (d & a)) + x[13] + 0x5a827999) | 0+ b = (b << 13) | (b >>> 19)+ a = (a + ((b & c) | (b & d) | (c & d)) + x[2] + 0x5a827999) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + ((a & b) | (a & c) | (b & c)) + x[6] + 0x5a827999) | 0+ d = (d << 5) | (d >>> 27)+ c = (c + ((d & a) | (d & b) | (a & b)) + x[10] + 0x5a827999) | 0+ c = (c << 9) | (c >>> 23)+ b = (b + ((c & d) | (c & a) | (d & a)) + x[14] + 0x5a827999) | 0+ b = (b << 13) | (b >>> 19)+ a = (a + ((b & c) | (b & d) | (c & d)) + x[3] + 0x5a827999) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + ((a & b) | (a & c) | (b & c)) + x[7] + 0x5a827999) | 0+ d = (d << 5) | (d >>> 27)+ c = (c + ((d & a) | (d & b) | (a & b)) + x[11] + 0x5a827999) | 0+ c = (c << 9) | (c >>> 23)+ b = (b + ((c & d) | (c & a) | (d & a)) + x[15] + 0x5a827999) | 0+ b = (b << 13) | (b >>> 19)+ a = (a + (b ^ c ^ d) + x[0] + 0x6ed9eba1) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + (a ^ b ^ c) + x[8] + 0x6ed9eba1) | 0+ d = (d << 9) | (d >>> 23)+ c = (c + (d ^ a ^ b) + x[4] + 0x6ed9eba1) | 0+ c = (c << 11) | (c >>> 21)+ b = (b + (c ^ d ^ a) + x[12] + 0x6ed9eba1) | 0+ b = (b << 15) | (b >>> 17)+ a = (a + (b ^ c ^ d) + x[2] + 0x6ed9eba1) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + (a ^ b ^ c) + x[10] + 0x6ed9eba1) | 0+ d = (d << 9) | (d >>> 23)+ c = (c + (d ^ a ^ b) + x[6] + 0x6ed9eba1) | 0+ c = (c << 11) | (c >>> 21)+ b = (b + (c ^ d ^ a) + x[14] + 0x6ed9eba1) | 0+ b = (b << 15) | (b >>> 17)+ a = (a + (b ^ c ^ d) + x[1] + 0x6ed9eba1) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + (a ^ b ^ c) + x[9] + 0x6ed9eba1) | 0+ d = (d << 9) | (d >>> 23)+ c = (c + (d ^ a ^ b) + x[5] + 0x6ed9eba1) | 0+ c = (c << 11) | (c >>> 21)+ b = (b + (c ^ d ^ a) + x[13] + 0x6ed9eba1) | 0+ b = (b << 15) | (b >>> 17)+ a = (a + (b ^ c ^ d) + x[3] + 0x6ed9eba1) | 0+ a = (a << 3) | (a >>> 29)+ d = (d + (a ^ b ^ c) + x[11] + 0x6ed9eba1) | 0+ d = (d << 9) | (d >>> 23)+ c = (c + (d ^ a ^ b) + x[7] + 0x6ed9eba1) | 0+ c = (c << 11) | (c >>> 21)+ b = (b + (c ^ d ^ a) + x[15] + 0x6ed9eba1) | 0+ b = (b << 15) | (b >>> 17)+ a = (previousA + a) | 0+ b = (previousB + b) | 0+ c = (previousC + c) | 0+ d = (previousD + d) | 0+ }+ this.a = a+ this.b = b+ this.c = c+ this.d = d+ }++ digest() {+ const length = this.length+ this.buffer[this.buffered] = 0x80+ this.buffer.fill(0, this.buffered + 1)+ if (this.buffered >= 56) {+ this.processBlock(this.buffer, 0)+ this.buffer.fill(0)+ }+ this.buffer.writeUInt32LE((length * 8) >>> 0, 56)+ this.buffer.writeUInt32LE(Math.floor(length / 0x20000000), 60)+ this.processBlock(this.buffer, 0)+ const result = Buffer.allocUnsafe(16)+ result.writeInt32LE(this.a, 0)+ result.writeInt32LE(this.b, 4)+ result.writeInt32LE(this.c, 8)+ result.writeInt32LE(this.d, 12)+ return result+ }+}++// Unsigned 64-bit arithmetic using two 32-bit words, without BigInt or per-step+// allocations. Split multiplication into 16-bit limbs to keep carries exact.+class Uint64 {+ low: number+ high: number++ constructor(low = 0, high = 0) {+ this.low = low | 0+ this.high = high | 0+ }++ set(low: number, high: number) {+ this.low = low | 0+ this.high = high | 0+ return this+ }+ add(low: number, high: number) {+ const sum = (this.low >>> 0) + (low >>> 0)+ this.high = (this.high + high + (sum >= 0x100000000 ? 1 : 0)) | 0+ this.low = sum | 0+ return this+ }+ multiply(low: number, high: number) {+ const a = this.low >>> 0,+ b = low >>> 0+ const a0 = a & 65535,+ a1 = a >>> 16,+ b0 = b & 65535,+ b1 = b >>> 16+ const carry = Math.floor((((a0 * b0) >>> 16) + a1 * b0 + a0 * b1) / 65536)+ this.high =+ (Math.imul(this.high, low) ++ Math.imul(this.low, high) ++ a1 * b1 ++ carry) |+ 0+ this.low = Math.imul(this.low, low)+ return this+ }+ rotate(bits: number) {+ const low = this.low,+ high = this.high+ this.low = (low << bits) | (high >>> (32 - bits))+ this.high = (high << bits) | (low >>> (32 - bits))+ return this+ }+ xor(low: number, high: number) {+ this.low ^= low+ this.high ^= high+ return this+ }+ xorShift(bits: number) {+ if (bits >= 32) this.low ^= this.high >>> (bits - 32)+ else {+ this.low ^= (this.low >>> bits) | (this.high << (32 - bits))+ this.high ^= this.high >>> bits+ }+ return this+ }+}++const P1L = 0x85ebca87,+ P1H = 0x9e3779b1+const P2L = 0x27d4eb4f,+ P2H = 0xc2b2ae3d+const P3L = 0x9e3779f9,+ P3H = 0x165667b1+const P4L = 0xc2b2ae63,+ P4H = 0x85ebca77+const P5L = 0x165667c5,+ P5H = 0x27d4eb2f++const read32 = (input: Buffer, offset: number) =>+ input[offset] |+ (input[offset + 1] << 8) |+ (input[offset + 2] << 16) |+ (input[offset + 3] << 24)++const LITTLE_ENDIAN = new Uint8Array(new Uint32Array([1]).buffer)[0] === 1++// High word of an unsigned product by a constant. All intermediates stay below+// 2^53; Math.imul supplies the low word separately.+const multiplyHigh = (a: number, b: number) => {+ const a0 = a & 65535,+ a1 = a >>> 16+ const b0 = b & 65535,+ b1 = b >>> 16+ return (+ (a1 * b1 + Math.floor((((a0 * b0) >>> 16) + a1 * b0 + a0 * b1) / 65536)) | 0+ )+}++class XxHash64Hash extends IncrementalHash {+ private v1 = new Uint64(P1L, P1H).add(P2L, P2H)+ private v2 = new Uint64(P2L, P2H)+ private v3 = new Uint64()+ private v4 = new Uint64().add(~P1L, ~P1H).add(1, 0)+ private lane = new Uint64()+ private result = new Uint64()++ constructor() {+ super(32)+ }++ protected processBlock(input: Buffer, offset: number) {+ this.processBlocks(input, offset, offset + 32)+ }++ protected processBlocks(input: Buffer, offset: number, end: number) {+ const words =+ LITTLE_ENDIAN && (input.byteOffset & 3) === 0 && (offset & 3) === 0+ ? new Int32Array(input.buffer, input.byteOffset, input.length >>> 2)+ : undefined+ let a = this.v1.low | 0,+ ah = this.v1.high | 0+ let b = this.v2.low | 0,+ bh = this.v2.high | 0+ let c = this.v3.low | 0,+ ch = this.v3.high | 0+ let d = this.v4.low | 0,+ dh = this.v4.high | 0+ for (; offset < end; offset += 32) {+ const index = offset >>> 2+ let low = words ? words[index] : read32(input, offset),+ high = words ? words[index + 1] : read32(input, offset + 4)+ high =+ (Math.imul(high, P2L) + Math.imul(low, P2H) + multiplyHigh(low, P2L)) |+ 0+ low = Math.imul(low, P2L)+ // Carry-out of a 32-bit sum, kept in integer arithmetic for the JIT.+ let sum = (a + low) | 0+ high = (ah + high + (((low & a) | ((low | a) & ~sum)) >>> 31)) | 0+ low = sum | 0+ let rotated = (low << 31) | (high >>> 1)+ high = (high << 31) | (low >>> 1)+ ah =+ (Math.imul(high, P1L) ++ Math.imul(rotated, P1H) ++ multiplyHigh(rotated, P1L)) |+ 0+ a = Math.imul(rotated, P1L)++ low = words ? words[index + 2] : read32(input, offset + 8)+ high = words ? words[index + 3] : read32(input, offset + 12)+ high =+ (Math.imul(high, P2L) + Math.imul(low, P2H) + multiplyHigh(low, P2L)) |+ 0+ low = Math.imul(low, P2L)+ sum = (b + low) | 0+ high = (bh + high + (((low & b) | ((low | b) & ~sum)) >>> 31)) | 0+ low = sum | 0+ rotated = (low << 31) | (high >>> 1)+ high = (high << 31) | (low >>> 1)+ bh =+ (Math.imul(high, P1L) ++ Math.imul(rotated, P1H) ++ multiplyHigh(rotated, P1L)) |+ 0+ b = Math.imul(rotated, P1L)++ low = words ? words[index + 4] : read32(input, offset + 16)+ high = words ? words[index + 5] : read32(input, offset + 20)+ high =+ (Math.imul(high, P2L) + Math.imul(low, P2H) + multiplyHigh(low, P2L)) |+ 0+ low = Math.imul(low, P2L)+ sum = (c + low) | 0+ high = (ch + high + (((low & c) | ((low | c) & ~sum)) >>> 31)) | 0+ low = sum | 0+ rotated = (low << 31) | (high >>> 1)+ high = (high << 31) | (low >>> 1)+ ch =+ (Math.imul(high, P1L) ++ Math.imul(rotated, P1H) ++ multiplyHigh(rotated, P1L)) |+ 0+ c = Math.imul(rotated, P1L)++ low = words ? words[index + 6] : read32(input, offset + 24)+ high = words ? words[index + 7] : read32(input, offset + 28)+ high =+ (Math.imul(high, P2L) + Math.imul(low, P2H) + multiplyHigh(low, P2L)) |+ 0+ low = Math.imul(low, P2L)+ sum = (d + low) | 0+ high = (dh + high + (((low & d) | ((low | d) & ~sum)) >>> 31)) | 0+ low = sum | 0+ rotated = (low << 31) | (high >>> 1)+ high = (high << 31) | (low >>> 1)+ dh =+ (Math.imul(high, P1L) ++ Math.imul(rotated, P1H) ++ multiplyHigh(rotated, P1L)) |+ 0+ d = Math.imul(rotated, P1L)+ }+ this.v1.set(a, ah)+ this.v2.set(b, bh)+ this.v3.set(c, ch)+ this.v4.set(d, dh)+ }++ digest() {+ const hash = this.result+ const lane = this.lane+ if (this.length >= 32) {+ hash.set(this.v1.low, this.v1.high).rotate(1)+ lane.set(this.v2.low, this.v2.high).rotate(7)+ hash.add(lane.low, lane.high)+ lane.set(this.v3.low, this.v3.high).rotate(12)+ hash.add(lane.low, lane.high)+ lane.set(this.v4.low, this.v4.high).rotate(18)+ hash.add(lane.low, lane.high)+ this.merge(hash, this.v1)+ this.merge(hash, this.v2)+ this.merge(hash, this.v3)+ this.merge(hash, this.v4)+ } else hash.set(P5L, P5H)+ hash.add(this.length >>> 0, Math.floor(this.length / 0x100000000))+ let offset = 0+ while (offset + 8 <= this.buffered) {+ lane+ .set(+ this.buffer.readInt32LE(offset),+ this.buffer.readInt32LE(offset + 4)+ )+ .multiply(P2L, P2H)+ .rotate(31)+ .multiply(P1L, P1H)+ hash.xor(lane.low, lane.high).rotate(27).multiply(P1L, P1H).add(P4L, P4H)+ offset += 8+ }+ if (offset + 4 <= this.buffered) {+ lane.set(this.buffer.readInt32LE(offset), 0).multiply(P1L, P1H)+ hash.xor(lane.low, lane.high).rotate(23).multiply(P2L, P2H).add(P3L, P3H)+ offset += 4+ }+ while (offset < this.buffered) {+ lane.set(this.buffer[offset++], 0).multiply(P5L, P5H)+ hash.xor(lane.low, lane.high).rotate(11).multiply(P1L, P1H)+ }+ hash+ .xorShift(33)+ .multiply(P2L, P2H)+ .xorShift(29)+ .multiply(P3L, P3H)+ .xorShift(32)+ const result = Buffer.allocUnsafe(8)+ result.writeUInt32BE(hash.high >>> 0, 0)+ result.writeUInt32BE(hash.low >>> 0, 4)+ return result+ }++ private merge(hash: Uint64, acc: Uint64) {+ const lane = this.lane+ lane.set(acc.low, acc.high).multiply(P2L, P2H).rotate(31).multiply(P1L, P1H)+ hash.xor(lane.low, lane.high).multiply(P1L, P1H).add(P4L, P4H)+ }+}++class DebugHash implements WebpackHash {+ private string = ''++ update(data: string | Buffer) {+ if (typeof data !== 'string') data = data.toString('utf8')+ const prefix = Buffer.from('@webpack-debug-digest@').toString('hex')+ if (data.startsWith(prefix)) {+ data = Buffer.from(data.slice(prefix.length), 'hex').toString()+ }+ this.string += `[${data}](${new Error().stack!.split('\n', 3)[2]})\n`+ return this+ }++ digest() {+ return Buffer.from(`@webpack-debug-digest@${this.string}`).toString('hex')+ }+}++export function createHash(+ algorithm: string | HashConstructor = 'xxhash64'+): WebpackHash {+ if (typeof algorithm === 'function')+ return new BulkHash(() => new algorithm())+ switch (algorithm) {+ case 'md4':+ return new BatchedHash(new Md4Hash())+ case 'xxhash64':+ return new BatchedHash(new XxHash64Hash())+ case 'debug':+ return new DebugHash()+ default:+ return new BulkHash(() =>+ nodeCreateHash(algorithm === 'native-md4' ? 'md4' : algorithm)+ )+ }+}turbopack/crates/turbopack-node/js/src/transforms/webpack-loaders.ts2 + / 0 −
@@ -19,6 +19,7 @@ import { import fs from 'fs' import path from 'path' import Module, { createRequire } from 'module'+import { absolutify, contextify, createHash } from './webpack-loaders-utils' export type { IpcInfoMessage, IpcRequestMessage } from './transforms' @@ -251,6 +252,7 @@ const transform = async ( target, mode, sourceMap,+ utils: { absolutify, contextify, createHash }, getOptions() { const entry = this.loaders[this.loaderIndex] return entry.options && typeof entry.options === 'object'