Skip to content

Commit

Permalink
[v4.0] feat: implement hashing content in Rust (#5155)
Browse files Browse the repository at this point in the history
  • Loading branch information
TrickyPi committed Sep 30, 2023
1 parent 0b0eabd commit fbc25af
Show file tree
Hide file tree
Showing 416 changed files with 504 additions and 491 deletions.
51 changes: 1 addition & 50 deletions browser/LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI

# Licenses of bundled dependencies
The published Rollup artifact additionally contains code with the following licenses:
MIT, ISC
MIT

# Bundled dependencies:
## @jridgewell/sourcemap-codec
Expand Down Expand Up @@ -86,35 +86,6 @@ Repository: lukeed/flru
---------------------------------------

## hash.js
License: MIT
By: Fedor Indutny
Repository: git@github.com:indutny/hash.js

---------------------------------------

## inherits
License: ISC
Repository: git://github.com/isaacs/inherits

> The ISC License
>
> Copyright (c) Isaac Z. Schlueter
>
> Permission to use, copy, modify, and/or distribute this software for any
> purpose with or without fee is hereby granted, provided that the above
> copyright notice and this permission notice appear in all copies.
>
> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
> LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
> OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
> PERFORMANCE OF THIS SOFTWARE.
---------------------------------------

## is-reference
License: MIT
By: Rich Harris
Expand All @@ -141,23 +112,3 @@ Repository: https://github.com/rich-harris/magic-string
> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---------------------------------------

## minimalistic-assert
License: ISC
Repository: https://github.com/calvinmetcalf/minimalistic-assert.git

> Copyright 2015 Calvin Metcalf
>
> Permission to use, copy, modify, and/or distribute this software for any purpose
> with or without fee is hereby granted, provided that the above copyright notice
> and this permission notice appear in all copies.
>
> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
> LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
> OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
> PERFORMANCE OF THIS SOFTWARE.
6 changes: 0 additions & 6 deletions browser/src/crypto.ts

This file was deleted.

6 changes: 1 addition & 5 deletions browser/src/wasm.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
// eslint-disable-next-line import/no-unresolved
import { parse } from '../../wasm/bindings_wasm.js';

export default {
parse
};
export { parse, xxhash_base64_url as xxhashBase64Url } from '../../wasm/bindings_wasm.js';
1 change: 0 additions & 1 deletion build-plugins/replace-browser-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { Plugin } from 'vite';
const resolve = (path: string) => fileURLToPath(new URL(`../${path}`, import.meta.url));

const JS_REPLACED_MODULES = [
'crypto',
'fs',
'hookActions',
'path',
Expand Down
1 change: 1 addition & 0 deletions native.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
/* auto-generated by NAPI-RS */

export function parse(code: string, allowReturnOutsideFunction: boolean): Buffer
export function xxhashBase64Url(input: Uint8Array): string
3 changes: 2 additions & 1 deletion native.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)

Check warning on line 252 in native.js

View check run for this annotation

Codecov / codecov/patch

native.js#L252

Added line #L252 was not covered by tests
}

const { parse } = nativeBinding
const { parse, xxhashBase64Url } = nativeBinding

module.exports.parse = parse
module.exports.xxhashBase64Url = xxhashBase64Url
27 changes: 25 additions & 2 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ members = [
"bindings_napi",
"bindings_wasm",
"parse_ast",
"xxhash"
]
1 change: 1 addition & 0 deletions rust/bindings_napi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ crate-type = ["cdylib"]
napi = { version = "2.12.2", default-features = false, features = ["napi4"] }
napi-derive = "2.12.2"
parse_ast = { path = "../parse_ast" }
xxhash = { path = "../xxhash" }

[build-dependencies]
napi-build = "2.0.1"
6 changes: 6 additions & 0 deletions rust/bindings_napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
use napi::bindgen_prelude::*;
use napi_derive::napi;
use parse_ast::parse_ast;
use xxhash;

#[napi]
pub fn parse(code: String, allow_return_outside_function: bool) -> Buffer {
parse_ast(code, allow_return_outside_function).into()
}

#[napi]
pub fn xxhash_base64_url(input: Uint8Array) -> String {
xxhash::xxhash_base64_url(&input.to_vec())
}
2 changes: 2 additions & 0 deletions rust/bindings_wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ edition = "2021"
[dependencies]
wasm-bindgen = "0.2.87"
parse_ast = { path = "../parse_ast" }
xxhash = { path = "../xxhash" }
js-sys = "0.3.64"

[lib]
crate-type = ["cdylib", "rlib"]
7 changes: 7 additions & 0 deletions rust/bindings_wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
use js_sys::Uint8Array;
use parse_ast::parse_ast;
use wasm_bindgen::prelude::*;
use xxhash;

#[wasm_bindgen]
pub fn parse(code: String, allow_return_outside_function: bool) -> Vec<u8> {
parse_ast(code, allow_return_outside_function)
}

#[wasm_bindgen]
pub fn xxhash_base64_url(input: Uint8Array) -> String {
xxhash::xxhash_base64_url(&input.to_vec())
}
3 changes: 1 addition & 2 deletions rust/parse_ast/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
#![feature(ptr_internals)]
use std::sync::Arc;

use convert_ast::converter::AstConverter;
use swc::config::IsModule::Unknown;
use swc::{config::ParseOptions, Compiler};
use swc_common::sync::Lrc;
use swc_common::{FileName, FilePathMapping, Globals, SourceMap, GLOBALS};
use swc_ecma_ast::EsVersion;
use swc_ecma_parser::{EsConfig, Syntax};

use convert_ast::converter::AstConverter;

use crate::convert_ast::annotations::SequentialComments;

mod convert_ast;
Expand Down
10 changes: 10 additions & 0 deletions rust/xxhash/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "xxhash"
version = "0.0.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
base64 = '0.21.4'
xxhash-rust = { version = "0.8.7", features = ["xxh3"] }
7 changes: 7 additions & 0 deletions rust/xxhash/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use base64::{engine::general_purpose, Engine as _};
use xxhash_rust::xxh3::xxh3_128;

pub fn xxhash_base64_url(input: &[u8]) -> String {
let hash = xxh3_128(input).to_le_bytes();
general_purpose::URL_SAFE_NO_PAD.encode(&hash)
}
4 changes: 2 additions & 2 deletions src/Graph.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import flru from 'flru';
import native from '../native';
import { parse } from '../native';
import type ExternalModule from './ExternalModule';
import Module from './Module';
import { ModuleLoader, type UnresolvedModule } from './ModuleLoader';
Expand Down Expand Up @@ -127,7 +127,7 @@ export default class Graph {
code: string,
{ allowReturnOutsideFunction = false }: { allowReturnOutsideFunction?: boolean } = {}
): AstNode {
const astBuffer = native.parse(code, allowReturnOutsideFunction);
const astBuffer = parse(code, allowReturnOutsideFunction);
const readString = getReadStringFunction(astBuffer);
return convertProgram(astBuffer.buffer, readString);
}
Expand Down
12 changes: 4 additions & 8 deletions src/utils/FileEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
OutputChunk
} from '../rollup/types';
import { BuildPhase } from './buildPhase';
import { createHash } from './crypto';
import { getXxhash } from './crypto';
import { getOrCreate } from './getOrCreate';
import { defaultHashSize } from './hashPlaceholders';
import { LOGLEVEL_WARN } from './logging';
Expand All @@ -33,10 +33,6 @@ import { extname } from './path';
import { isPathFragment } from './relativeId';
import { makeUnique, renderNamePattern } from './renderNamePattern';

function getSourceHash(source: string | Uint8Array): string {
return createHash().update(source).digest('hex');
}

function generateAssetFileName(
name: string | undefined,
source: string | Uint8Array,
Expand Down Expand Up @@ -274,7 +270,7 @@ export class FileEmitter {
if (consumedFile.fileName) {
this.finalizeAdditionalAsset(consumedFile, consumedFile.source, output);
} else {
const sourceHash = getSourceHash(consumedFile.source);
const sourceHash = getXxhash(consumedFile.source);
getOrCreate(consumedAssetsByHash, sourceHash, () => []).push(consumedFile);
}
} else if (consumedFile.type === 'prebuilt-chunk') {
Expand All @@ -294,7 +290,7 @@ export class FileEmitter {
let referenceId = idBase;

do {
referenceId = createHash().update(referenceId).digest('hex').slice(0, 8);
referenceId = getXxhash(referenceId).slice(0, 8).replaceAll('-', '$');
} while (
this.filesByReferenceId.has(referenceId) ||
this.outputFileEmitters.some(({ filesByReferenceId }) => filesByReferenceId.has(referenceId))
Expand Down Expand Up @@ -449,7 +445,7 @@ export class FileEmitter {

// Deduplicate assets if an explicit fileName is not provided
if (!fileName) {
const sourceHash = getSourceHash(source);
const sourceHash = getXxhash(source);
fileName = fileNamesBySource.get(sourceHash);
if (!fileName) {
fileName = generateAssetFileName(
Expand Down
18 changes: 16 additions & 2 deletions src/utils/crypto.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
import { createHash as cryptoCreateHash, type Hash } from 'node:crypto';
import { xxhashBase64Url } from '../../native';

export const createHash = (): Hash => cryptoCreateHash('sha256');
let textEncoder: TextEncoder;
export function getXxhash(input: string | Uint8Array) {
let buffer: Uint8Array;
if (typeof input === 'string') {
if (typeof Buffer === 'undefined') {
textEncoder ??= new TextEncoder();
buffer = textEncoder.encode(input);
} else {
buffer = Buffer.from(input);
}
} else {
buffer = input;
}
return xxhashBase64Url(buffer);
}
4 changes: 2 additions & 2 deletions src/utils/hashPlaceholders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const hashPlaceholderLeft = '!~{';
const hashPlaceholderRight = '}~';
const hashPlaceholderOverhead = hashPlaceholderLeft.length + hashPlaceholderRight.length;

// This is the size of a sha256
export const maxHashSize = 64;
// This is the size of a 128-bits xxhash with base64url encoding
export const maxHashSize = 22;
export const defaultHashSize = 8;

export type HashPlaceholderGenerator = (optionName: string, hashSize?: number) => string;
Expand Down
Loading

0 comments on commit fbc25af

Please sign in to comment.