Socket
Socket
Sign inDemoInstall

@11ty/eleventy-plugin-bundle

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@11ty/eleventy-plugin-bundle - npm Package Compare versions

Comparing version 1.0.1 to 1.0.2

BundleFileOutput.js

133

codeManager.js

@@ -1,60 +0,11 @@

const fs = require("fs");
const path = require("path");
// const fsp = fs.promises;
const { createHash } = require("crypto");
const hashCache = {};
const directoryExistsCache = {};
const BundleFileOutput = require("./BundleFileOutput");
const debug = require("debug")("Eleventy:Bundle");
class BundleFileOutput {
constructor(outputDirectory, bundleDirectory) {
this.outputDirectory = outputDirectory;
this.bundleDirectory = bundleDirectory;
this.hashLength = 10;
}
class CodeManager {
// code is placed in this bucket by default
static DEFAULT_BUCKET_NAME = "default";
getFilenameHash(content) {
if(hashCache[content]) {
return hashCache[content];
}
// code is hoisted to this bucket when necessary
static HOISTED_BUCKET_NAME = "default";
let hash = createHash("sha256");
hash.update(content);
let base64hash = hash.digest('base64').replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
let filenameHash = base64hash.substring(0, this.hashLength);
hashCache[content] = filenameHash;
return filenameHash;
}
getFilename(filename, extension) {
return filename + (extension && !extension.startsWith(".") ? `.${extension}` : "");
}
modifyPathToUrl(dir, filename) {
return "/" + path.join(dir, filename).split(path.sep).join("/");
}
writeBundle(content, type, writeToFileSystem) {
let dir = path.join(this.outputDirectory, this.bundleDirectory);
let filenameHash = this.getFilenameHash(content);
let filename = this.getFilename(filenameHash, type);
if(writeToFileSystem) {
if(!directoryExistsCache[dir]) {
fs.mkdirSync(dir, { recursive: true });
directoryExistsCache[dir] = true;
}
let fullPath = path.join(dir, filename);
debug("Writing bundle %o", fullPath);
fs.writeFileSync(fullPath, content);
}
return this.modifyPathToUrl(this.bundleDirectory, filename);
}
}
class CodeManager {
constructor(name) {

@@ -65,4 +16,9 @@ this.name = name;

this.transforms = [];
this.isHoisting = true;
}
setHoisting(enabled) {
this.isHoisting = !!enabled;
}
reset() {

@@ -78,3 +34,3 @@ this.pages = {};

}
return ["default"];
return [CodeManager.DEFAULT_BUCKET_NAME];
}

@@ -90,2 +46,8 @@

_initBucket(pageUrl, bucket) {
if(!this.pages[pageUrl][bucket]) {
this.pages[pageUrl][bucket] = new Set();
}
}
addToPage(pageUrl, code = [], bucket) {

@@ -104,16 +66,18 @@ if(!Array.isArray(code) && code) {

let buckets = CodeManager.normalizeBuckets(bucket);
for(let b of buckets) {
if(!this.pages[pageUrl][b]) {
this.pages[pageUrl][b] = new Set();
let codeContent = code.map(entry => {
if(this.trimOnAdd) {
return entry.trim();
}
return entry;
});
let content = code.map(entry => {
if(this.trimOnAdd) {
return entry.trim();
}
return entry;
}).join("\n");
debug("Adding %o to bundle %o for %o (bucket: %o)", content, this.name, pageUrl, b);
this.pages[pageUrl][b].add(content);
for(let b of buckets) {
this._initBucket(pageUrl, b);
debug("Adding code to bundle %o for %o (bucket: %o): %o", this.name, pageUrl, b, codeContent);
for(let content of codeContent) {
this.pages[pageUrl][b].add(content);
}
}

@@ -135,2 +99,10 @@ }

getBucketsForPage(pageData) {
let pageUrl = pageData.url;
if(!this.pages[pageUrl]) {
return [];
}
return Object.keys(this.pages[pageUrl]);
}
async getForPage(pageData, buckets) {

@@ -177,8 +149,33 @@ let url = pageData.url;

let content = await this.getForPage(pageData, buckets);
let writer = new BundleFileOutput(output, bundle);
return writer.writeBundle(content, this.name, write);
}
// Used when a bucket is output multiple times on a page and needs to be hoisted
hoistBucket(pageData, bucketName) {
let newTargetBucketName = CodeManager.HOISTED_BUCKET_NAME;
if(!this.isHoisting || bucketName === newTargetBucketName) {
return;
}
let url = pageData.url;
if(!this.pages[url] || !this.pages[url][bucketName]) {
debug("No bundle code found for %o on %o, %O", this.name, url, this.pages);
return;
}
debug("Code in bucket (%o) is being hoisted to a new bucket (%o)", bucketName, newTargetBucketName);
this._initBucket(url, newTargetBucketName);
for(let codeEntry of this.pages[url][bucketName]) {
this.pages[url][bucketName].delete(codeEntry);
this.pages[url][newTargetBucketName].add(codeEntry);
}
// delete the bucket
delete this.pages[url][bucketName];
}
}
module.exports = CodeManager;

@@ -10,3 +10,4 @@ const pkg = require("./package.json");

// post-process
transforms: []
transforms: [],
hoistDuplicateBundlesFor: [],
}, options);

@@ -13,0 +14,0 @@

@@ -6,2 +6,4 @@ const CodeManager = require("./codeManager.js");

module.exports = function(eleventyConfig, options = {}) {
// TODO throw an error if addPlugin is called more than once per build here.
let managers = {};

@@ -11,2 +13,9 @@

managers[name] = new CodeManager(name);
if(Array.isArray(options.hoistDuplicateBundlesFor) && options.hoistDuplicateBundlesFor.includes(name)) {
managers[name].setHoisting(true);
} else {
managers[name].setHoisting(false);
}
managers[name].setTransforms(options.transforms);

@@ -13,0 +22,0 @@

@@ -0,1 +1,3 @@

const debug = require("debug")("Eleventy:Bundle");
/* This class defers any `bundleGet` calls to a post-build transform step,

@@ -13,2 +15,3 @@ * to allow `getBundle` to be called before all of the `css` additions have been processed

// type if `get` (return string) or `file` (bundle writes to file, returns file url)
static getAssetKey(type, name, bucket) {

@@ -57,5 +60,61 @@ if(Array.isArray(bucket)) {

getAllBucketsForPage(pageData) {
let availableBucketsForPage = new Set();
for(let name in this.managers) {
for(let bucket of this.managers[name].getBucketsForPage(pageData)) {
availableBucketsForPage.add(`${name}::${bucket}`);
}
}
return availableBucketsForPage;
}
getManager(name) {
if(!this.managers[name]) {
throw new Error(`No asset manager found for ${name}. Known names: ${Object.keys(this.managers)}`);
}
return this.managers[name];
}
async replaceAll(pageData) {
let matches = this.findAll();
let availableBucketsForPage = this.getAllBucketsForPage(pageData);
let usedBucketsOnPage = new Set();
let bucketsOutputStringCount = {};
let bucketsFileCount = {};
for(let match of matches) {
if(typeof match === "string") {
continue;
}
// type is `file` or `get`
let {type, name, bucket} = match;
let key = `${name}::${bucket}`;
if(!usedBucketsOnPage.has(key)) {
usedBucketsOnPage.add(key);
}
if(type === "get") {
if(!bucketsOutputStringCount[key]) {
bucketsOutputStringCount[key] = 0;
}
bucketsOutputStringCount[key]++;
} else if(type === "file") {
if(!bucketsFileCount[key]) {
bucketsFileCount[key] = 0;
}
bucketsFileCount[key]++;
}
}
// Hoist code in non-default buckets that are output multiple times
// Only hoist if 2+ `get` OR 1 `get` and 1+ `file`
for(let bucketInfo in bucketsOutputStringCount) {
let stringOutputCount = bucketsOutputStringCount[bucketInfo];
if(stringOutputCount > 1 || stringOutputCount === 1 && bucketsFileCount[bucketInfo] > 0) {
let [name, bucketName] = bucketInfo.split("::");
this.getManager(name).hoistBucket(pageData, bucketName);
}
}
let content = await Promise.all(matches.map(match => {

@@ -67,11 +126,10 @@ if(typeof match === "string") {

let {type, name, bucket} = match;
if(!this.managers[name]) {
throw new Error(`No asset manager found for ${name}. Known keys: ${Object.keys(this.managers)}`);
}
let manager = this.getManager(name);
if(type === "get") {
// returns promise
return this.managers[name].getForPage(pageData, bucket);
return manager.getForPage(pageData, bucket);
} else if(type === "file") {
// returns promise
return this.managers[name].writeBundle(pageData, bucket, {
return manager.writeBundle(pageData, bucket, {
output: this.outputDirectory,

@@ -85,2 +143,9 @@ bundle: this.bundleDirectory,

for(let bucketInfo of availableBucketsForPage) {
if(!usedBucketsOnPage.has(bucketInfo)) {
let [name, bucketName] = bucketInfo.split("::");
debug(`WARNING! \`${pageData.inputPath}\` has unbundled \`${name}\` assets (in the '${bucketName}' bucket) that were not written to or used on the page. You might want to add a call to \`getBundle('${name}', '${bucketName}')\` to your content! Learn more: https://github.com/11ty/eleventy-plugin-bundle#asset-bucketing`);
}
}
return content.join("");

@@ -87,0 +152,0 @@ }

{
"name": "@11ty/eleventy-plugin-bundle",
"version": "1.0.1",
"version": "1.0.2",
"description": "Little bundles of code, little bundles of joy.",

@@ -28,2 +28,8 @@ "main": "eleventy.bundle.js",

],
"repository": {
"type": "git",
"url": "git://github.com/11ty/eleventy-plugin-bundle.git"
},
"bugs": "https://github.com/11ty/eleventy-plugin-bundle/issues",
"homepage": "https://www.11ty.dev/",
"author": {

@@ -45,7 +51,7 @@ "name": "Zach Leatherman",

"devDependencies": {
"@11ty/eleventy": "2.0.0-beta.3",
"ava": "^5.1.0",
"@11ty/eleventy": "^2.0.0",
"ava": "^5.2.0",
"postcss": "^8.4.21",
"postcss-nested": "^6.0.0",
"sass": "^1.58.0"
"postcss-nested": "^6.0.1",
"sass": "^1.58.3"
},

@@ -52,0 +58,0 @@ "dependencies": {

@@ -5,5 +5,5 @@ # eleventy-plugin-bundle

Create minimal per-page or app-level bundles of CSS, JavaScript, or HTML bundles to be included in your Eleventy project.
Create minimal per-page or app-level bundles of CSS, JavaScript, or HTML to be included in your Eleventy project.
Makes implementing Critical CSS, per-page in-use-only CSS/JS bundles, SVG icon libraries, secondary HTML content to load via XHR.
Makes it easy to implement Critical CSS, in-use-only CSS/JS bundles, SVG icon libraries, or secondary HTML content to load via XHR.

@@ -49,8 +49,11 @@ ## Why?

// Default bundle types
bundles: ["css", "js", "html"],
// Extra bundle names ("css", "js", "html" are guaranteed)
bundles: [],
// Array of async-friendly callbacks to transform bundle content.
// Works with getBundle and getBundleFileUrl
transforms: []
transforms: [],
// Array of bundle names eligible for duplicate bundle hoisting
hoistDuplicateBundlesFor: [], // e.g. ["css", "js"]
});

@@ -60,2 +63,4 @@ };

Read more about [`hoistDuplicateBundlesFor` and duplicate bundle hoisting](https://github.com/11ty/eleventy-plugin-bundle/issues/5).
</details>

@@ -65,3 +70,3 @@

The following shortcodes are provided by this plugin:
The following Universal Shortcodes (available in `njk`, `liquid`, `hbs`, `11ty.js`, and `webc`) are provided by this plugin:

@@ -71,2 +76,4 @@ * `css`, `js`, and `html` to add code to a bundle.

Here’s a [real-world commit showing this in use on the `eleventy-base-blog` project](https://github.com/11ty/eleventy-base-blog/commit/c9595d8f42752fa72c66991c71f281ea960840c9?diff=split).
### Add bundle code in a Markdown file in Eleventy

@@ -240,5 +247,5 @@

Starting with `@11ty/eleventy-plugin-webc@0.9.0` this plugin is used by default in the Eleventy WebC plugin. Specifically, [WebC Bundler Mode](https://www.11ty.dev/docs/languages/webc/#css-and-js-(bundler-mode)) now uses the bundle plugin under the hood.
Starting with `@11ty/eleventy-plugin-webc@0.9.0` (track at [issue #48](https://github.com/11ty/eleventy-plugin-webc/issues/48)) this plugin is used by default in the Eleventy WebC plugin. Specifically, [WebC Bundler Mode](https://www.11ty.dev/docs/languages/webc/#css-and-js-(bundler-mode)) now uses the bundle plugin under the hood.
To add CSS to a page bundle in WebC, you would use a `<style>` element in a WebC page or component:
To add CSS to a bundle in WebC, you would use a `<style>` element in a WebC page or component:

@@ -257,5 +264,5 @@ ```html

* Existing calls via WebC helpers `getCss` or `getJs` (e.g. `<style @raw="getCss(page.url)">`) have been wired up to `getBundle('css')` and `getBundle('js')` automatically.
* Existing calls via WebC helpers `getCss` or `getJs` (e.g. `<style @raw="getCss(page.url)">`) have been wired up to `getBundle` (for `"css"` and `"js"` respectively) automatically.
* For consistency, you may prefer using the bundle plugin method names everywhere: `<style @raw="getBundle('css')">` and `<script @raw="getBundle('js')">` both work fine.
* Outside of WebC, the [Universal Filters `webcGetCss` and `webcGetJs`](https://www.11ty.dev/docs/languages/webc/#css-and-js-(bundler-mode)) were available to access CSS and JS bundles but are considered deprecated in favor of new bundle plugin Universal Shortcodes `getBundle("css")` and `getBundle("js")` respectively.
* Outside of WebC, the [Universal Filters `webcGetCss` and `webcGetJs`](https://www.11ty.dev/docs/languages/webc/#css-and-js-(bundler-mode)) were available to access CSS and JS bundles but are considered deprecated in favor of the `getBundle` Universal Shortcode (`{% getBundle "css" %}` and `{% getBundle "js" %}` respectively).

@@ -262,0 +269,0 @@ #### Modify the bundle output

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc