New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

laravel-mix-make-file-hash

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

laravel-mix-make-file-hash - npm Package Compare versions

Comparing version

to
2.0.0

208

index.js

@@ -1,49 +0,165 @@

const del = require("del")
const fs = require("fs")
const jsonFile = require("jsonfile")
const forEach = require("lodash/forEach")
const forIn = require("lodash/forIn")
const path = require("path");
const del = require("del");
const fs = require("fs");
const { promisify } = require("util");
const writeFile = promisify(fs.writeFile);
const readFile = promisify(fs.readFile);
const copyFile = promisify(fs.copyFile);
const makeFileHash = (publicPath, manifestFilePath, delSyncOptions = {}) => {
const delOptions = { ...{ force: false }, ...delSyncOptions }
const deleteStaleHashedFiles = async ({
manifest,
publicPath,
delOptions,
debug
}) => {
for (let oldHash of Object.values(manifest)) {
// A glob pattern of all files with the new file naming style e.g. 'app.*.css'
const oldHashedFilePathsGlob = path
.join(publicPath, oldHash)
.replace(/([^.]+)\.([^?]+)\?id=(.+)$/g, "$1.*.$2");
const deletedPaths = await del(
[oldHashedFilePathsGlob],
delOptions
).catch(error => console.error(error));
debug &&
deletedPaths.length &&
console.debug(
`Removed stale hash files: ${oldHashedFilePathsGlob} (${deletedPaths})`
);
}
};
// Parse the mix-manifest file
jsonFile.readFile(manifestFilePath, (err, obj) => {
const newJson = {}
const oldFiles = []
forIn(obj, (value, key) => {
// Get the hash from the ?id= query string parameter and
// move it into the file name e.g. 'app.abcd1234.css'
const newFilename = value.replace(
/([^.]+)\.([^?]+)\?id=(.+)$/g,
"$1.$3.$2"
)
// Create a glob pattern of all files with the new file naming style e.g. 'app.*.css'
const oldAsGlob = value.replace(
/([^.]+)\.([^?]+)\?id=(.+)$/g,
"$1.*.$2"
)
// Delete old versioned file(s) that match the glob pattern
del.sync([`${publicPath}${oldAsGlob}`], delOptions)
// Copy as new versioned file name
fs.copyFile(
`${publicPath}${key}`,
`${publicPath}${newFilename}`,
err => {
err && console.error(err)
}
)
newJson[key] = newFilename
oldFiles.push(key)
})
forEach(oldFiles, key => {
del.sync([`${publicPath}${key}`], delOptions)
})
// Write the new contents of the mix manifest file
jsonFile.writeFile(manifestFilePath, newJson, { spaces: 4 }, err => {
if (err) console.error(err)
})
})
}
const getNewFilename = file =>
file.replace(/([^.]+)\.([^?]+)\?id=(.+)$/g, "$1.$3.$2");
module.exports = makeFileHash
const normalizeData = content => {
if (Buffer.isBuffer(content)) content = content.toString("utf8");
content = content.replace(/^\uFEFF/, "");
return content;
};
const standardizeArgs = args =>
typeof args[0] === "object"
? args[0]
: {
publicPath: args[0],
manifestFilePath: args[1],
delOptions: args[3] || {}
};
const makeNewHashedFiles = async ({
manifest,
publicPath,
delOptions,
debug
}) => {
const newJson = {};
for (let [oldNonHash, oldHash] of Object.entries(manifest)) {
const newFilePath = getNewFilename(path.join(publicPath, oldHash));
const oldFilePath = path.join(publicPath, oldNonHash);
await copyFile(oldFilePath, newFilePath).catch(error =>
console.error(error)
);
await del([oldFilePath], delOptions).catch(error => console.error(error));
debug &&
console.debug(
`Renamed '${oldFilePath}' to '${newFilePath}' (delOptions '${JSON.stringify(
delOptions
)}')`
);
newJson[oldNonHash] = getNewFilename(oldHash);
}
return newJson;
};
const filterManifest = (manifest, fileTypesBlacklist) => {
if (!fileTypesBlacklist || fileTypesBlacklist.length === 0)
return { filteredManifest: manifest };
let removedLines;
let filteredManifest;
Object.entries(manifest).forEach(([key, val]) => {
const fileType = key.split(".").pop();
if (fileTypesBlacklist.includes(fileType)) {
return (removedLines = { ...removedLines, [key]: val });
}
filteredManifest = { ...filteredManifest, [key]: val };
});
return { filteredManifest, removedLines };
};
const writeManifest = async ({ manifestFilePath, manifest, debug }) => {
const EOL = "\n";
const jsonManifest = JSON.stringify(manifest, null, 4);
const formattedManifest = jsonManifest.replace(/\n/g, EOL) + EOL;
await writeFile(manifestFilePath, formattedManifest).catch(error =>
console.error(error)
);
debug &&
console.debug(
`Finished updating '${manifestFilePath}' with the new filenames:\n`,
JSON.parse(formattedManifest)
);
return JSON.parse(formattedManifest);
};
const makeFileHash = async (...args) => {
const {
publicPath,
manifestFilePath,
fileTypesBlacklist,
delOptions,
keepBlacklistedEntries = false,
debug
} = standardizeArgs(args);
if (!publicPath)
return console.error(`Error: 'Make file hash' needs a 'publicPath'!\n`);
if (!manifestFilePath)
return console.error(
`Error: 'Make file hash' needs a 'manifestFilePath'!\n`
);
const rawManifest = await readFile(manifestFilePath).catch(error =>
console.error(error)
);
const manifest = await JSON.parse(normalizeData(rawManifest));
debug && console.debug(`Manifest found: '${manifestFilePath}'`);
const { filteredManifest, removedLines } = filterManifest(
manifest,
fileTypesBlacklist
);
debug &&
removedLines &&
keepBlacklistedEntries &&
console.debug(`Files that will not be re-hashed:\n`, removedLines);
debug &&
removedLines &&
!keepBlacklistedEntries &&
console.debug(`Files removed from manifest:\n`, removedLines);
// Don't force delete by default
const delOptionsUnforced = {
...{ force: false },
...delOptions
};
await deleteStaleHashedFiles({
manifest,
publicPath,
delOptions: delOptionsUnforced,
debug
});
const newManifest = await makeNewHashedFiles({
manifest: filteredManifest,
publicPath,
delOptions: delOptionsUnforced,
debug
});
const combinedManifest =
keepBlacklistedEntries && removedLines
? { ...newManifest, ...removedLines }
: newManifest;
return await writeManifest({
manifest: combinedManifest,
manifestFilePath,
debug
});
};
module.exports = makeFileHash;
{
"name": "laravel-mix-make-file-hash",
"version": "1.2.0",
"version": "2.0.0",
"description": "Convert the default Laravel Mix querystring hashing to filename hashing.",

@@ -21,4 +21,5 @@ "main": "index.js",

"dependencies": {
"del": "^5.1.0",
"lodash": "^4.17.11"
}
}
# Laravel Mix make file hash
Mix has querystring hashing by default which doesn't work too well with some caching systems.
By default, the hashing system in Laravel Mix will append a querystring to the filename to invalidate the cache.
Querystring hashing looks like this:<br>
Example from `mix-manifest.json`:
```json
{
"/dist/main.js": "/dist/main.js?id=1e70e7c11a9185f57e64"
}
```
(OLD) main.css?id=abcd1234
```
After mix has done it's thing, this script converts that querystring hashing to filename hashing:
The problem is that this way of hashing may not work with some caching systems.
Instead, **"Laravel Mix make file hash" will move the hash into the filename of the hashed files**. So your `mix-manifest.json` will look something like this:
```json
{
"/dist/main.js": "/dist/main.1e70e7c11a9185f57e64.js"
}
```
(NEW) main.abcd1234.css
```
## Under the hood
To accomplish the re-hashing, this script will:
1. Read the `mix-manifest.json` generated by Laravel Mix.
2. Remove stale hashed files.
3. Rename the files found in the manifest to include the hash within the filename.
4. Update `mix-manifest.json` with the new filenames.
5. Return the contents of the new manifest for further usage (if required).
Turn on debug to activate noisy feedback:
`{ debug: true }`
## Installation
Install package from [npmjs.com](https://www.npmjs.com/package/laravel-mix-make-file-hash):
```bash
npm i -D laravel-mix-make-file-hash
# or
yarn add laravel-mix-make-file-hash -D
```
## Usage
## Normal usage
This is not a Laravel mix plugin so use with `mix.then()` like this:
```js
if (mix.inProduction()) {
mix.version();
mix.then(() => {
const convertToFileHash = require("laravel-mix-make-file-hash");
convertToFileHash({
publicPath: "web",
manifestFilePath: "web/mix-manifest.json"
});
});
}
```
// Allow versioning in production
mix.version()
## Promise usage
// Run after mix finishes
mix.then(() => {
const laravelMixMakeFileHash = require("laravel-mix-make-file-hash")
laravelMixMakeFileHash('web', 'web/mix-manifest.json')
})
`convertToFileHash` returns a Promise containing the new `mix-manifest.json` contents. This means you can perform another task after the `convertToFileHash` has finished the re-hashing.
```js
if (mix.inProduction()) {
mix.version();
mix.then(async () => {
const convertToFileHash = require("laravel-mix-make-file-hash");
const fileHashedManifest = await convertToFileHash({
publicPath: "web",
manifestFilePath: "web/mix-manifest.json"
});
// Do something here...
});
}

@@ -41,15 +81,33 @@ ```

| Name | Type | Default | Description |
| ---------------------- | ---------------- | ------------------ | ------------------------------------------------------------------------------------ |
| publicPath \* | `string` | `undefined` | The path to your public folder (eg: `"web"`) |
| manifestFilePath \* | `string` | `undefined` | The filePath to your mix-manifest.json<br /> (eg: `"web/mix-manifest.json"`) |
| fileTypesBlacklist | `string`/`array` | `undefined` | A list of filetypes to ignore re-hashing |
| keepBlacklistedEntries | `boolean` | `false` | Whether to keep blacklisted entries in the manifest |
| delOptions | `object` | `{ force: false }` | Options to provide to del - [See options](https://www.npmjs.com/package/del#options) |
| debug | `boolean` | `false` | Debug exactly what's happening (or meant to happen) during runtime |
\*&nbsp;= Required
## Full config example
```js
laravelMixMakeFileHash(
publicPath,
manifestFilePath,
delSyncOptions = { force: true },
)
if (mix.inProduction()) {
mix.version();
mix.then(async () => {
const convertToFileHash = require("laravel-mix-make-file-hash");
convertToFileHash({
publicPath: "web",
manifestFilePath: "web/mix-manifest.json",
fileTypesBlacklist: ["html"],
keepBlacklistedEntries: true,
delOptions: { force: false },
debug: false
});
})
```
## Under the hood
## Links
It'll first look at your manifest, then create a new file with the updated hash and then remove the old file.
Most of the code is from tomgrohl on this
[Laravel Mix issue on querystring hashing](https://github.com/JeffreyWay/laravel-mix/issues/1022#issuecomment-379168021)
This script was created for the [Agency Webpack Mix Config](https://github.com/ben-rogerson/agency-webpack-mix-config).