diff --git a/CHANGELOG.md b/CHANGELOG.md index 93193e1c..2f8dd984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Go to the `v1` branch to see the changelog of Lume 1. - New plugin: `check_urls` to detect broken links [#675]. - New plugin: `icons` to load automatically icons from popular icon catalogs. - New plugin: `google_fonts` to download and self-host automatically fonts from Google Fonts. +- New plugin: `gzip` to compress files. [#680] ### Fixed - Nav plugin: Breadcrumb with urls with CJK characters. @@ -564,6 +565,7 @@ Go to the `v1` branch to see the changelog of Lume 1. [#677]: https://github.com/lumeland/lume/issues/677 [#678]: https://github.com/lumeland/lume/issues/678 [#679]: https://github.com/lumeland/lume/issues/679 +[#680]: https://github.com/lumeland/lume/issues/680 [Unreleased]: https://github.com/lumeland/lume/compare/v2.3.3...HEAD [2.3.3]: https://github.com/lumeland/lume/compare/v2.3.2...v2.3.3 diff --git a/core/processors.ts b/core/processors.ts index 15de2c82..ebc2f222 100644 --- a/core/processors.ts +++ b/core/processors.ts @@ -42,7 +42,7 @@ export type Processor = ( allPages: Page[], ) => void | false | Promise; -function pageMatches(exts: Extensions, page: Page): boolean { +export function pageMatches(exts: Extensions, page: Page): boolean { if (exts === "*") { return true; } diff --git a/core/utils/lume_config.ts b/core/utils/lume_config.ts index cf0a3c50..1242508c 100644 --- a/core/utils/lume_config.ts +++ b/core/utils/lume_config.ts @@ -13,6 +13,7 @@ export const pluginNames = [ "fff", "filter_pages", "google_fonts", + "gzip", "inline", "jsx", "jsx_preact", diff --git a/deps/foras.ts b/deps/foras.ts new file mode 100644 index 00000000..2cdcb50d --- /dev/null +++ b/deps/foras.ts @@ -0,0 +1,4 @@ +import { Foras } from "https://deno.land/x/foras@v2.1.4/src/deno/mod.ts"; +export * from "https://deno.land/x/foras@v2.1.4/src/deno/mod.ts"; + +await Foras.initBundledOnce(); diff --git a/plugins/gzip.ts b/plugins/gzip.ts new file mode 100644 index 00000000..2c228085 --- /dev/null +++ b/plugins/gzip.ts @@ -0,0 +1,57 @@ +import { merge } from "../core/utils/object.ts"; +import { Page } from "../core/file.ts"; +import { pageMatches } from "../core/processors.ts"; +import { gzip as compress, Memory } from "../deps/foras.ts"; + +import type { Extensions } from "../core/utils/path.ts"; +import type Site from "../core/site.ts"; + +export interface Options { + /** The list of extensions this plugin applies to */ + extensions?: Extensions; + + /** + * Level param between 0 and 9 (9 is the smallest but takes the longest to encode) + */ + level?: number; +} + +// Default options +export const defaults: Options = { + extensions: [".html", ".css", ".js", ".mjs", ".svg", ".json", ".xml", ".txt"], + level: 6, +}; + +/** + * A plugin to compress files with gzip + */ +export function gzip(userOptions?: Options) { + const options = merge(defaults, userOptions); + + return (site: Site) => { + site.addEventListener("beforeSave", () => { + const textEncoder = new TextEncoder(); + + for (const page of site.pages) { + if (!pageMatches(options.extensions, page)) { + continue; + } + + const content = page.content as string; + const compressedContent = compress( + new Memory(textEncoder.encode(content)), + options.level, + ).copyAndDispose(); + + const compressedPage = Page.create({ + url: page.outputPath + ".gz", + content: compressedContent, + }); + + site.pages.push(compressedPage); + } + }); + }; +} + +export default gzip; diff --git a/tests/__snapshots__/gzip.test.ts.snap b/tests/__snapshots__/gzip.test.ts.snap new file mode 100644 index 00000000..24d578ef --- /dev/null +++ b/tests/__snapshots__/gzip.test.ts.snap @@ -0,0 +1,1866 @@ +export const snapshot = {}; + +snapshot[`gzip plugin 1`] = ` +{ + formats: [ + { + engines: 0, + ext: ".page.toml", + loader: [AsyncFunction: toml], + pageType: "page", + }, + { + engines: 1, + ext: ".page.ts", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 1, + ext: ".page.js", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 0, + ext: ".page.jsonc", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + engines: 0, + ext: ".page.json", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + assetLoader: [AsyncFunction: text], + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".json", + loader: [AsyncFunction: json], + pageType: "asset", + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".jsonc", + loader: [AsyncFunction: json], + }, + { + engines: 1, + ext: ".md", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".markdown", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".js", + loader: [AsyncFunction: module], + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".ts", + loader: [AsyncFunction: module], + }, + { + engines: 1, + ext: ".vento", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".vto", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: toml], + engines: 0, + ext: ".toml", + loader: [AsyncFunction: toml], + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yaml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + { + assetLoader: [AsyncFunction: text], + engines: undefined, + ext: ".css", + pageType: "asset", + }, + { + copy: true, + engines: undefined, + ext: ".png", + }, + ], + src: [ + "/", + "/404.md", + "/_data.yml", + "/favicon.png", + "/images", + "/images/avatar.jpg", + "/page5.yaml", + "/pages", + "/pages/2020-06-21_page2.page.json", + "/pages/2021-01-02-18-32_page4.page.ts", + "/pages/_data", + "/pages/_data.yml", + "/pages/_data/colors.yml", + "/pages/_data/documents.ts", + "/pages/_data/drinks.js", + "/pages/_data/names.json", + "/pages/ghost", + "/pages/ghost/2021-12-29-page6.md", + "/pages/ghost/_data.yml", + "/pages/page1.md", + "/pages/page3.page.js", + "/pages/subpage", + "/pages/subpage/_data.yml", + "/pages/subpage/page7.page.js", + "/static.yml", + "/styles.css", + ], +} +`; + +snapshot[`gzip plugin 2`] = ` +[ + { + entry: "/favicon.png", + flags: [], + outputPath: "/favicon.png", + }, +] +`; + +snapshot[`gzip plugin 3`] = ` +[ + { + content: "Uint8Array(105)", + data: { + basename: "404", + content: "Uint8Array(105)", + page: [ + "src", + "data", + "asset", + ], + url: "/404.html.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: "Uint8Array(56)", + data: { + basename: "index", + content: "Uint8Array(56)", + page: [ + "src", + "data", + "asset", + ], + url: "/overrided-page2/index.html.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: "Uint8Array(56)", + data: { + basename: "index", + content: "Uint8Array(56)", + page: [ + "src", + "data", + "asset", + ], + url: "/page5/index.html.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: "Uint8Array(56)", + data: { + basename: "index", + content: "Uint8Array(56)", + page: [ + "src", + "data", + "asset", + ], + url: "/pages/new-name/page7/index.html.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: "Uint8Array(119)", + data: { + basename: "index", + content: "Uint8Array(119)", + page: [ + "src", + "data", + "asset", + ], + url: "/pages/page4/index.html.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: "Uint8Array(64)", + data: { + basename: "index", + content: "Uint8Array(64)", + page: [ + "src", + "data", + "asset", + ], + url: "/pages/page6/index.html.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: "Uint8Array(97)", + data: { + basename: "index", + content: "Uint8Array(97)", + page: [ + "src", + "data", + "asset", + ], + url: "/static/index.html.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: "Uint8Array(75)", + data: { + basename: "styles", + content: "Uint8Array(75)", + page: [ + "src", + "data", + "asset", + ], + url: "/styles.css.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: " +

This page is exported to /404.html, not /404/index.html

+", + data: { + basename: "404", + children: "

This page is exported to /404.html, not /404/index.html

+", + content: "This page is exported to \`/404.html\`, not \`/404/index.html\` +", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + url: "/404.html", + }, + src: { + asset: false, + ext: ".md", + path: "/404", + remote: undefined, + }, + }, + { + content: " +Content of Page 5", + data: { + basename: "page5", + children: "Content of Page 5", + content: "Content of Page 5", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + title: "Page 5", + url: "/page5/", + }, + src: { + asset: false, + ext: ".yaml", + path: "/page5", + remote: undefined, + }, + }, + { + content: " +Content of Page 2", + data: { + basename: "page2", + children: "Content of Page 2", + colors: "Array(3)", + content: "Content of Page 2", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + "image", + ], + names: "Array(2)", + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 2", + url: "/overrided-page2/", + }, + src: { + asset: false, + ext: ".page.json", + path: "/pages/2020-06-21_page2", + remote: undefined, + }, + }, + { + content: " +Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + data: { + basename: "page4", + children: "Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + colors: "Array(3)", + content: "default", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Overrided site name", + tags: "Array(1)", + title: "Page 4", + url: "/pages/page4/", + }, + src: { + asset: false, + ext: ".page.ts", + path: "/pages/2021-01-02-18-32_page4", + remote: undefined, + }, + }, + { + content: " +

Content of Page 6

+", + data: { + basename: "page6", + children: "

Content of Page 6

+", + colors: "Array(3)", + content: "Content of Page 6 +", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(2)", + title: "Page 6", + url: "/pages/page6/", + }, + src: { + asset: false, + ext: ".md", + path: "/pages/ghost/2021-12-29-page6", + remote: undefined, + }, + }, + { + content: "Content of Page 3", + data: { + basename: "page3", + colors: "Array(3)", + content: "Content of Page 3", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 3", + url: "/page_3", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/page3", + remote: undefined, + }, + }, + { + content: " +Content of Page 7", + data: { + basename: "page7", + children: "Content of Page 7", + colors: "Array(3)", + content: "Content of Page 7", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(3)", + url: "/pages/new-name/page7/", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/subpage/page7", + remote: undefined, + }, + }, + { + content: " +This yaml should be ignored because it's copied statically", + data: { + basename: "static", + children: "This yaml should be ignored because it's copied statically", + content: "This yaml should be ignored because it's copied statically", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + url: "/static/", + }, + src: { + asset: false, + ext: ".yml", + path: "/static", + remote: undefined, + }, + }, + { + content: "body { + font-family: sans-serif; + color: black; +} +", + data: { + basename: "styles", + content: "body { + font-family: sans-serif; + color: black; +} +", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + url: "/styles.css", + }, + src: { + asset: true, + ext: ".css", + path: "/styles", + remote: undefined, + }, + }, +] +`; + +snapshot[`gzip plugin with options 1`] = ` +{ + formats: [ + { + engines: 0, + ext: ".page.toml", + loader: [AsyncFunction: toml], + pageType: "page", + }, + { + engines: 1, + ext: ".page.ts", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 1, + ext: ".page.js", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 0, + ext: ".page.jsonc", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + engines: 0, + ext: ".page.json", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".json", + loader: [AsyncFunction: json], + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".jsonc", + loader: [AsyncFunction: json], + }, + { + engines: 1, + ext: ".md", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".markdown", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".js", + loader: [AsyncFunction: module], + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".ts", + loader: [AsyncFunction: module], + }, + { + engines: 1, + ext: ".vento", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".vto", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: toml], + engines: 0, + ext: ".toml", + loader: [AsyncFunction: toml], + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yaml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + { + assetLoader: [AsyncFunction: text], + engines: undefined, + ext: ".css", + pageType: "asset", + }, + ], + src: [ + "/", + "/index.vto", + "/styles.css", + ], +} +`; + +snapshot[`gzip plugin with options 2`] = `[]`; + +snapshot[`gzip plugin with options 3`] = ` +[ + { + content: "Uint8Array(2834)", + data: { + basename: "styles", + content: "Uint8Array(2834)", + page: [ + "src", + "data", + "asset", + ], + url: "/styles.css.gz", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: \` + + + + + essential + + + + + +
Free shipping on all orders over £50
+
+
+ essential +
+
+ + +
+ +
+ + Shop + About + Articles +
+
+
+
+
Clean and affordable beauty
+

Beautiful, natural skin

+

A unique layout with a striking design

+ +
+ +
+
+
+
Why Cheslsea?
+

Natural and organic is the future of skincare and life as we know it.

+
+
+ + + +
+
+

Let's face it. Many cosmetics are bad for your skin. We use only natural ingredients and still provide consistently great tanning results.

+ Learn more +
+
+
+ +
+

Best Sellers

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Limited

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

New In

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Sale

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Half Price

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+
+
+ Shop now +
+
+
+
+
Limited Run
+

Moisturise

+

Whether in the sun or on the couch, hydration is key to maintaining happy, healthy skin.

+ Shop now +
+
+
+ +
+
+ +
+
+ +
+
+
+

Testimonials

+
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+
+
+
+ +
+
+
+
Only the best
+

Cleansers

+

We're humans too and we understand that skin care and cosmetics should rejuvenate and rehydrate in the short and long run.

+ Shop now +
+
+
+ +
+
+ +
+
+
+
+
+
essential
+
+
+
More Info
+ Shop + About + Info + FAQ +
+
+
Helpful Links
+ Shop + About + Info + FAQ +
+
+
Find out more
+ Shop + About + Info + FAQ +
+
+
+
©2021 design by novolio. images by unsplash
+ +\`, + data: { + basename: "index", + children: \` + + + + + + essential + + + + + +
Free shipping on all orders over £50
+
+
+ essential +
+
+ + +
+ +
+ + Shop + About + Articles +
+
+
+
+
Clean and affordable beauty
+

Beautiful, natural skin

+

A unique layout with a striking design

+ +
+ +
+
+
+
Why Cheslsea?
+

Natural and organic is the future of skincare and life as we know it.

+
+
+ + + +
+
+

Let's face it. Many cosmetics are bad for your skin. We use only natural ingredients and still provide consistently great tanning results.

+ Learn more +
+
+
+ +
+

Best Sellers

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Limited

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

New In

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Sale

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Half Price

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+
+
+ Shop now +
+
+
+
+
Limited Run
+

Moisturise

+

Whether in the sun or on the couch, hydration is key to maintaining happy, healthy skin.

+ Shop now +
+
+
+ +
+
+ +
+
+ +
+
+
+

Testimonials

+
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+
+
+
+ +
+
+
+
Only the best
+

Cleansers

+

We're humans too and we understand that skin care and cosmetics should rejuvenate and rehydrate in the short and long run.

+ Shop now +
+
+
+ +
+
+ +
+
+
+
+
+
essential
+
+
+
More Info
+ Shop + About + Info + FAQ +
+
+
Helpful Links
+ Shop + About + Info + FAQ +
+
+
Find out more
+ Shop + About + Info + FAQ +
+
+
+
©2021 design by novolio. images by unsplash
+ +\`, + content: \` + + + + + + essential + + + + + +
Free shipping on all orders over £50
+
+
+ essential +
+
+ + +
+ +
+ + Shop + About + Articles +
+
+
+
+
Clean and affordable beauty
+

Beautiful, natural skin

+

A unique layout with a striking design

+ +
+ +
+
+
+
Why Cheslsea?
+

Natural and organic is the future of skincare and life as we know it.

+
+
+ + + +
+
+

Let's face it. Many cosmetics are bad for your skin. We use only natural ingredients and still provide consistently great tanning results.

+ Learn more +
+
+
+ +
+

Best Sellers

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Limited

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

New In

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Sale

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+ +
+

Half Price

+

Lorem ipsum dolor sit amet, consectetur.

+
Shop now
+
+ +
+
+
+ Shop now +
+
+
+
+
Limited Run
+

Moisturise

+

Whether in the sun or on the couch, hydration is key to maintaining happy, healthy skin.

+ Shop now +
+
+
+ +
+
+ +
+
+ +
+
+
+

Testimonials

+
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ + + + Elle Spearman +
+ + + + +
+
+
+
+
+ +
+
+
+
Only the best
+

Cleansers

+

We're humans too and we understand that skin care and cosmetics should rejuvenate and rehydrate in the short and long run.

+ Shop now +
+
+
+ +
+
+ +
+
+
+
+
+
essential
+
+
+
More Info
+ Shop + About + Info + FAQ +
+
+
Helpful Links
+ Shop + About + Info + FAQ +
+
+
Find out more
+ Shop + About + Info + FAQ +
+
+
+
©2021 design by novolio. images by unsplash
+ +\`, + date: [], + mergedKeys: [ + "tags", + ], + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + tags: "Array(0)", + url: "/", + }, + src: { + asset: false, + ext: ".vto", + path: "/index", + remote: undefined, + }, + }, + { + content: '/* https://unocss.dev/transformers/directives#usage */ +.custom-div { + margin-top:0;margin-bottom:0;text-align:center;font-weight:500; + ; +}.custom-div:hover{--un-text-opacity:1;color:rgb(248 113 113 / var(--un-text-opacity));} + +.grid { + display:grid;grid-template-columns:repeat(2,minmax(0,1fr)); +} + +@media (min-width: 640px){ + .grid { + grid-template-columns:repeat(3,minmax(0,1fr)); + } +} + +.btn-blue { + background-color: #3b82f6; +} + +/* https://unocss.dev/transformers/variant-group#usage */ +.variant-group { + font-weight:300;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; +}.variant-group:hover{--un-bg-opacity:1;background-color:rgb(156 163 175 / var(--un-bg-opacity));font-weight:500;} + +/* layer: preflights */ +*,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: ;}::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: ;} +/* layer: shortcuts */ +.container{width:100%;} +@media (min-width: 640px){ +.container{max-width:640px;} +} +@media (min-width: 768px){ +.container{max-width:768px;} +} +@media (min-width: 1024px){ +.container{max-width:1024px;} +} +@media (min-width: 1280px){ +.container{max-width:1280px;} +} +@media (min-width: 1536px){ +.container{max-width:1536px;} +} +/* layer: default */ +.absolute{position:absolute;} +.fixed{position:fixed;} +.relative{position:relative;} +.inset-0{inset:0;} +.left-0{left:0;} +.right-0{right:0;} +.top-0{top:0;} +.z-10{z-index:10;} +.z-40{z-index:40;} +.grid{display:grid;} +.col-span-1{grid-column:span 1/span 1;} +.row-span-1{grid-row:span 1/span 1;} +.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr));} +.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr));} +.mx-6{margin-left:1.5rem;margin-right:1.5rem;} +.mx-auto{margin-left:auto;margin-right:auto;} +.-mb-10{margin-bottom:-2.5rem;} +.-mb-8{margin-bottom:-2rem;} +.-ml-8{margin-left:-2rem;} +.-mt-10{margin-top:-2.5rem;} +.-mt-32{margin-top:-8rem;} +.-mt-4{margin-top:-1rem;} +.mb-4{margin-bottom:1rem;} +.mb-6{margin-bottom:1.5rem;} +.ml-4{margin-left:1rem;} +.mr-12{margin-right:3rem;} +.mt-12{margin-top:3rem;} +.mt-2{margin-top:0.5rem;} +.mt-3{margin-top:0.75rem;} +.mt-4{margin-top:1rem;} +.mt-6{margin-top:1.5rem;} +.block{display:block;} +.inline-block{display:inline-block;} +.hidden{display:none;} +.h-10{height:2.5rem;} +.h-24{height:6rem;} +.h-28{height:7rem;} +.h-32{height:8rem;} +.h-6{height:1.5rem;} +.h-64{height:16rem;} +.h-8{height:2rem;} +.h-full{height:100%;} +.h-screen{height:100vh;} +.max-w-md{max-width:28rem;} +.max-w-sm{max-width:24rem;} +.w-10{width:2.5rem;} +.w-28{width:7rem;} +.w-32{width:8rem;} +.w-6{width:1.5rem;} +.w-full{width:100%;} +.w-screen{width:100vw;} +.flex{display:flex;} +.flex-col{flex-direction:column;} +.flex-wrap{flex-wrap:wrap;} +.items-center{align-items:center;} +.justify-end{justify-content:flex-end;} +.justify-center{justify-content:center;} +.justify-between{justify-content:space-between;} +.gap-12{gap:3rem;} +.gap-4{gap:1rem;} +.gap-6{gap:1.5rem;} +.space-y-4>:not([hidden])~:not([hidden]){--un-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--un-space-y-reverse)));margin-bottom:calc(1rem * var(--un-space-y-reverse));} +.bg-black{--un-bg-opacity:1;background-color:rgb(0 0 0 / var(--un-bg-opacity));} +.bg-blue-100{--un-bg-opacity:1;background-color:rgb(219 234 254 / var(--un-bg-opacity));} +.bg-blue-200{--un-bg-opacity:1;background-color:rgb(191 219 254 / var(--un-bg-opacity));} +.bg-gray-100{--un-bg-opacity:1;background-color:rgb(243 244 246 / var(--un-bg-opacity));} +.bg-gray-200{--un-bg-opacity:1;background-color:rgb(229 231 235 / var(--un-bg-opacity));} +.bg-white{--un-bg-opacity:1;background-color:rgb(255 255 255 / var(--un-bg-opacity));} +.object-cover{object-fit:cover;} +.p-12{padding:3rem;} +.p-4{padding:1rem;} +.px-12{padding-left:3rem;padding-right:3rem;} +.px-6{padding-left:1.5rem;padding-right:1.5rem;} +.px-8{padding-left:2rem;padding-right:2rem;} +.py-12{padding-top:3rem;padding-bottom:3rem;} +.py-24{padding-top:6rem;padding-bottom:6rem;} +.py-4{padding-top:1rem;padding-bottom:1rem;} +.text-center{text-align:center;} +.text-justify{text-align:justify;} +.text-2xl{font-size:1.5rem;line-height:2rem;} +.text-3xl{font-size:1.875rem;line-height:2.25rem;} +.text-4xl{font-size:2.25rem;line-height:2.5rem;} +.text-sm{font-size:0.875rem;line-height:1.25rem;} +.text-xl{font-size:1.25rem;line-height:1.75rem;} +.text-xs{font-size:0.75rem;line-height:1rem;} +.text-blue-900{--un-text-opacity:1;color:rgb(30 58 138 / var(--un-text-opacity));} +.text-gray-500{--un-text-opacity:1;color:rgb(107 114 128 / var(--un-text-opacity));} +.text-gray-700{--un-text-opacity:1;color:rgb(55 65 81 / var(--un-text-opacity));} +.text-gray-800{--un-text-opacity:1;color:rgb(31 41 55 / var(--un-text-opacity));} +.text-gray-900{--un-text-opacity:1;color:rgb(17 24 39 / var(--un-text-opacity));} +.text-white{--un-text-opacity:1;color:rgb(255 255 255 / var(--un-text-opacity));} +.text-opacity-40{--un-text-opacity:0.4;} +.font-light{font-weight:300;} +.font-normal{font-weight:400;} +.font-semibold{font-weight:600;} +.tracking-wider{letter-spacing:0.05em;} +.tracking-widest{letter-spacing:0.1em;} +.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";} +.uppercase{text-transform:uppercase;} +.lowercase{text-transform:lowercase;} +.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;} +.opacity-5{opacity:0.05;} +.opacity-75{opacity:0.75;} +.opacity-95{opacity:0.95;} +@media (min-width: 768px){ +.md\\\\:col-span-2{grid-column:span 2/span 2;} +.md\\\\:col-span-3{grid-column:span 3/span 3;} +.md\\\\:col-span-5{grid-column:span 5/span 5;} +.md\\\\:row-span-2{grid-row:span 2/span 2;} +.md\\\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr));} +.md\\\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr));} +.md\\\\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr));} +.md\\\\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr));} +.md\\\\:grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr));} +.md\\\\:hidden{display:none;} +.md\\\\:h-auto{height:auto;} +.md\\\\:h-screen{height:100vh;} +.md\\\\:flex{display:flex;} +.md\\\\:gap-24{gap:6rem;} +.md\\\\:text-4xl{font-size:2.25rem;line-height:2.5rem;} +.md\\\\:text-5xl{font-size:3rem;line-height:1;} +} +@media (min-width: 1024px){ +.lg\\\\:col-span-1{grid-column:span 1/span 1;} +.lg\\\\:col-span-2{grid-column:span 2/span 2;} +.lg\\\\:row-span-2{grid-row:span 2/span 2;} +.lg\\\\:-ml-8{margin-left:-2rem;} +.lg\\\\:mb-0{margin-bottom:0;} +.lg\\\\:mt-0{margin-top:0;} +.lg\\\\:w-auto{width:auto;} +.lg\\\\:flex-1{flex:1 1 0%;} +.lg\\\\:rotate-90{--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-rotate:90deg;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +.lg\\\\:transform{transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +.lg\\\\:text-base{font-size:1rem;line-height:1.5rem;} +}', + data: { + basename: "styles", + content: '/* https://unocss.dev/transformers/directives#usage */ +.custom-div { + @apply text-center my-0 font-medium; + --at-apply: "hover:text-red"; +} + +.grid { + --uno: grid grid-cols-2; +} + +@screen sm { + .grid { + --uno: grid-cols-3; + } +} + +.btn-blue { + background-color: theme("colors.blue.500"); +} + +/* https://unocss.dev/transformers/variant-group#usage */ +.variant-group { + @apply hover:(bg-gray-400 font-medium) font-(light mono); +} +', + date: [], + mergedKeys: [ + "tags", + ], + page: [ + "src", + "data", + "asset", + ], + paginate: "paginate", + search: [], + tags: "Array(0)", + url: "/styles.css", + }, + src: { + asset: true, + ext: ".css", + path: "/styles", + remote: undefined, + }, + }, +] +`; diff --git a/tests/gzip.test.ts b/tests/gzip.test.ts new file mode 100644 index 00000000..5a4ab486 --- /dev/null +++ b/tests/gzip.test.ts @@ -0,0 +1,34 @@ +import { assertSiteSnapshot, build, getSite } from "./utils.ts"; +import gzip from "../plugins/gzip.ts"; +import unocss from "../plugins/unocss.ts"; + +Deno.test("gzip plugin", async (t) => { + const site = getSite({ + src: "normal", + }); + + site.loadAssets([".css", ".json"]); + site.copy([".png"]); + + site.use(gzip()); + + await build(site); + await assertSiteSnapshot(t, site); +}); + +Deno.test("gzip plugin with options", async (t) => { + const site = getSite({ + src: "unocss", + }); + + site.use(unocss({ + cssFile: "styles.css", + })); + site.use(gzip({ + extensions: [".css"], + level: 1, + })); + + await build(site); + await assertSiteSnapshot(t, site); +}); diff --git a/tests/plugins.test.ts b/tests/plugins.test.ts index 06cbf948..d960c525 100644 --- a/tests/plugins.test.ts +++ b/tests/plugins.test.ts @@ -20,6 +20,7 @@ Deno.test("Plugins list in init", () => { "fff", "filter_pages", "google_fonts", + "gzip", "inline", "jsx", "jsx_preact",