├── .rubocop_todo.yml
├── .prettierignore
├── .Gemfile.lock.un~
├── lib
├── jekyll-cloudinary.rb
└── jekyll
│ ├── cloudinary
│ └── version.rb
│ └── cloudinary.rb
├── .trunk
├── .gitignore
├── config
│ ├── .shellcheckrc
│ └── .markdownlint.yaml
└── trunk.yaml
├── Rakefile
├── .github
├── dependabot.yml
└── workflows
│ ├── release.yml
│ ├── gem-push.yml
│ ├── rubocop.yml
│ └── codeql-analysis.yml
├── script
└── fmt
├── _release.sh
├── .vscode
└── settings.json
├── Gemfile
├── _config.yml
├── .rubocop.yml
├── LICENSE
├── .gitignore
├── CONTRIBUTING.md
├── jekyll-cloudinary.gemspec
├── RELEASES.md
├── CODE_OF_CONDUCT.md
├── Gemfile.lock~
├── Gemfile.lock
└── README.md
/.rubocop_todo.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.md
2 |
--------------------------------------------------------------------------------
/.Gemfile.lock.un~:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nhoizey/jekyll-cloudinary/master/.Gemfile.lock.un~
--------------------------------------------------------------------------------
/lib/jekyll-cloudinary.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "jekyll/cloudinary"
4 |
--------------------------------------------------------------------------------
/.trunk/.gitignore:
--------------------------------------------------------------------------------
1 | *out
2 | *logs
3 | *actions
4 | *notifications
5 | plugins
6 | user_trunk.yaml
7 | user.yaml
8 |
--------------------------------------------------------------------------------
/lib/jekyll/cloudinary/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Jekyll
4 | module Cloudinary
5 | VERSION = "1.21"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "bundler/gem_tasks"
4 | require "rubocop/rake_task"
5 |
6 | RuboCop::RakeTask.new
7 |
8 | task default: :rubocop
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: bundler
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "04:00"
8 | open-pull-requests-limit: 10
9 |
--------------------------------------------------------------------------------
/.trunk/config/.shellcheckrc:
--------------------------------------------------------------------------------
1 | enable=all
2 | source-path=SCRIPTDIR
3 | disable=SC2154
4 |
5 | # If you're having issues with shellcheck following source, disable the errors via:
6 | # disable=SC1090
7 | # disable=SC1091
8 |
--------------------------------------------------------------------------------
/script/fmt:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "Rubocop $(bundle exec rubocop --version)"
4 | bundle exec rubocop -S -D -E $@
5 | success=$?
6 | if ((success != 0)); then
7 | echo -e "\nTry running \`script/fmt -a\` to automatically fix errors"
8 | fi
9 | exit $success
10 |
--------------------------------------------------------------------------------
/_release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # https://github.com/svenfuchs/gem-release
3 |
4 | if [ -z "$1" ]; then
5 | echo "Usage: provide the release type (patch, minor, major)."
6 | exit -1
7 | else
8 | release_type="$@"
9 | fi
10 |
11 | gem bump --version "$release_type" --tag --release
12 |
--------------------------------------------------------------------------------
/.trunk/config/.markdownlint.yaml:
--------------------------------------------------------------------------------
1 | # Autoformatter friendly markdownlint config (all formatting rules disabled)
2 | default: true
3 | blank_lines: false
4 | bullet: false
5 | html: false
6 | indentation: false
7 | line_length: false
8 | spaces: false
9 | url: false
10 | whitespace: false
11 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "spellright.language": [
3 | "en"
4 | ],
5 | "spellright.documentTypes": [
6 | "markdown",
7 | "latex",
8 | "plaintext"
9 | ],
10 | "ruby.intellisense": "rubyLocate",
11 | "ruby.codeCompletion": "rcodetools",
12 | "ruby.useBundler": true,
13 | "ruby.useLanguageServer": true
14 | }
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | # Specify your gem's dependencies in jekyll-cloudinary.gemspec
6 | gemspec
7 | gem "jekyll", "~> 4.3"
8 | gem "cloudinary", "~> 1.26"
9 | gem "rake", "~> 13.0.6"
10 |
11 | group :rubocop do
12 | gem "rubocop", "~> 1.50", require: false
13 | gem 'rubocop-minitest', require: false
14 | gem "rubocop-rake", require: false
15 | gem "rubocop-performance", require: false
16 | gem "rubocop-rails", require: false
17 | end
18 |
19 |
--------------------------------------------------------------------------------
/.trunk/trunk.yaml:
--------------------------------------------------------------------------------
1 | version: 0.1
2 | cli:
3 | version: 1.0.1
4 | plugins:
5 | sources:
6 | - id: trunk
7 | ref: v0.0.5
8 | uri: https://github.com/trunk-io/plugins
9 | lint:
10 | enabled:
11 | - git-diff-check
12 | - shellcheck@0.8.0
13 | - rubocop@1.30.1
14 | - markdownlint@0.32.2
15 | - shfmt@3.5.0
16 | - actionlint@1.6.21
17 | - gitleaks@8.15.0
18 | - prettier@2.7.1
19 | runtimes:
20 | enabled:
21 | - go@1.18.3
22 | - node@16.14.2
23 | - ruby@3.1.0
24 | actions:
25 | enabled:
26 | - trunk-announce
27 | - trunk-check-pre-push
28 | - trunk-fmt-pre-commit
29 | - trunk-upgrade-available
30 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | title: Jekyll Cloudinary Plugin
2 | description: Jekyll plugin providing Cloudinary-powered responsive image generation
3 |
4 | timezone: America/Vancouver
5 |
6 | exclude: ["vendor/", "node_modules/", "*.gemspec", "*.gem", "Gemfile", "Gemfile.lock", "package.json", "package-lock.json", "script/", "LICENSE.txt", "lib/", "bin/", "README.md", "Rakefile","normalize.scss.md"]
7 |
8 | remote_theme: pmarsceill/just-the-docs
9 |
10 | color_scheme: dark
11 |
12 | # Aux links for the upper right navigation
13 | aux_links:
14 | "Source for Jekyll Cloudinary Plugin":
15 | - "//github.com/mavaddat/jekyll-cloudinary"
16 |
17 | # Makes Aux links open in a new tab. Default is false
18 | aux_links_new_tab: false
19 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Publish Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Get version from tag
13 | id: tag_name
14 | run: |
15 | echo ::set-output name=current_version::${GITHUB_REF#refs/tags/v}
16 | shell: bash
17 | - name: Get notes
18 | id: generate_notes
19 | uses: anmarkoulis/commitizen-changelog-reader@master
20 | with:
21 | tag_name: ${{ github.ref }}
22 | changelog: ./RELEASES.md
23 | - uses: actions/checkout@master
24 | - name: Create a Release ${{ github.ref }}
25 | uses: elgohr/Github-Release-Action@master
26 | env:
27 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
28 | with:
29 | args: ${{join(fromJson(steps.generate_notes.outputs.notes).notes, '')}}
30 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: .rubocop_todo.yml
2 |
3 | inherit_gem:
4 | jekyll: .rubocop.yml
5 |
6 | AllCops:
7 | TargetRubyVersion: 3.2.0
8 | NewCops: enable
9 | Exclude:
10 | - script/**/*
11 | - vendor/**/*
12 |
13 | Style/StringLiterals:
14 | Enabled: true
15 | EnforcedStyle: double_quotes
16 |
17 | Style/StringLiteralsInInterpolation:
18 | Enabled: true
19 | EnforcedStyle: double_quotes
20 |
21 | Layout/LineLength:
22 | Exclude:
23 | - lib/jekyll/cloudinary.rb
24 | Max: 120
25 |
26 | Lint/UselessAssignment:
27 | Exclude:
28 | - lib/jekyll/cloudinary.rb
29 |
30 | Metrics/AbcSize:
31 | Exclude:
32 | - lib/jekyll/cloudinary.rb
33 | Metrics/BlockNesting:
34 | Exclude:
35 | - lib/jekyll/cloudinary.rb
36 | Metrics/CyclomaticComplexity:
37 | Exclude:
38 | - lib/jekyll/cloudinary.rb
39 | Metrics/MethodLength:
40 | Exclude:
41 | - lib/jekyll/cloudinary.rb
42 | Metrics/PerceivedComplexity:
43 | Exclude:
44 | - lib/jekyll/cloudinary.rb
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 Mavaddat Javid
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | /.config
4 | /coverage/
5 | /InstalledFiles
6 | /pkg/
7 | /spec/reports/
8 | /spec/examples.txt
9 | /test/tmp/
10 | /test/version_tmp/
11 | /tmp/
12 |
13 | # Used by dotenv library to load environment variables.
14 | # .env
15 |
16 | # Ignore Byebug command history file.
17 | .byebug_history
18 |
19 | ## Specific to RubyMotion:
20 | .dat*
21 | .repl_history
22 | build/
23 | *.bridgesupport
24 | build-iPhoneOS/
25 | build-iPhoneSimulator/
26 |
27 | ## Specific to RubyMotion (use of CocoaPods):
28 | #
29 | # We recommend against adding the Pods directory to your .gitignore. However
30 | # you should judge for yourself, the pros and cons are mentioned at:
31 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32 | #
33 | # vendor/Pods/
34 |
35 | ## Documentation cache and generated files:
36 | /.yardoc/
37 | /_yardoc/
38 | /doc/
39 | /rdoc/
40 |
41 | ## Environment normalization:
42 | /.bundle/
43 | /vendor/bundle
44 | /vendor/cache
45 | /lib/bundler/man/
46 |
47 | # for a library or gem, you might want to ignore these files since the code is
48 | # intended to run in multiple environments; otherwise, check them in:
49 | # Gemfile.lock
50 | # .ruby-version
51 | # .ruby-gemset
52 |
53 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
54 | .rvmrc
55 |
56 | # Used by RuboCop. Remote config files pulled in from inherit_from directive.
57 | # .rubocop-https?--*
--------------------------------------------------------------------------------
/.github/workflows/gem-push.yml:
--------------------------------------------------------------------------------
1 | name: Ruby Gem
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 | name: Build + Publish
12 | runs-on: ubuntu-latest
13 | permissions:
14 | contents: read
15 | packages: write
16 | strategy:
17 | matrix:
18 | ruby-version: [head]
19 |
20 | steps:
21 | - name: Query Ruby Version
22 | id: get_version
23 | run: |
24 | echo "ruby_version=$(curl -s https://api.github.com/repos/ruby/ruby/tags | jq -r '.[0].name')" >> $GITHUB_ENV
25 | - uses: actions/checkout@v3
26 | - name: Set up Ruby ${{ env.ruby_version }}
27 | uses: ruby/setup-ruby@v1
28 | with:
29 | ruby-version: ${{ matrix.ruby-version }}
30 |
31 | - name: Publish to GPR
32 | run: |
33 | mkdir -p $HOME/.gem
34 | touch $HOME/.gem/credentials
35 | chmod 0600 $HOME/.gem/credentials
36 | printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
37 | gem build *.gemspec
38 | gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
39 | env:
40 | GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
41 | OWNER: ${{ github.repository_owner }}
42 |
43 | - name: Publish to RubyGems
44 | run: |
45 | mkdir -p $HOME/.gem
46 | touch $HOME/.gem/credentials
47 | chmod 0600 $HOME/.gem/credentials
48 | printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
49 | gem build *.gemspec
50 | gem push *.gem
51 | env:
52 | GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
53 |
--------------------------------------------------------------------------------
/.github/workflows/rubocop.yml:
--------------------------------------------------------------------------------
1 | name: RuboCop
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 |
9 | strategy:
10 | matrix:
11 | ruby-version: [head, 2.7.4]
12 | os: [ubuntu-latest, windows-latest, macOS-latest]
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 | - name: Set up Ruby ${{ matrix.ruby-version }}
17 | uses: ruby/setup-ruby@v1
18 | with:
19 | ruby-version: ${{ matrix.ruby-version }}
20 | bundler-cache: true
21 | - name: Cache gems
22 | uses: actions/cache@v3
23 | with:
24 | path: vendor/bundle
25 | key: ${{ matrix.os }}-rubocop-${{ hashFiles('**/Gemfile.lock') }}
26 | restore-keys: |
27 | ${{ matrix.os }}-rubocop-
28 | - name: Install gems
29 | run: |
30 | wget https://raw.githubusercontent.com/jekyll/jekyll/master/.rubocop_todo.yml --directory-prefix="$(bundle show jekyll)";
31 | bundle install --jobs $(nproc) --retry 3
32 | bundle exec rspec
33 | bundle exec rake
34 | bundle exec rubocop --parallel
35 | if: matrix.os == 'ubuntu-latest'
36 | - name: Install gems
37 | run: |
38 | wget https://raw.githubusercontent.com/jekyll/jekyll/master/.rubocop_todo.yml --directory-prefix="$(bundle show jekyll)";
39 | bundle install --jobs $env:NUMBER_OF_PROCESSORS --retry 3
40 | bundle exec rspec
41 | bundle exec rake
42 | bundle exec rubocop --parallel
43 | if: matrix.os == 'windows-latest'
44 | - name: Install gems
45 | run: |
46 | wget https://raw.githubusercontent.com/jekyll/jekyll/master/.rubocop_todo.yml --directory-prefix="$(bundle show jekyll)";
47 | bundle install --jobs $(sysctl -n hw.logicalcpu) --retry 3
48 | bundle exec rspec
49 | bundle exec rake
50 | bundle exec rubocop --parallel
51 | if: matrix.os == 'macOS-latest'
52 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribute
2 |
3 | ## Introduction
4 |
5 | First, thank you for considering contributing to jekyll-cloudinary! It's people like you that make the open source community such a great community! 😊
6 |
7 | We welcome any type of contribution, not only code. You can help with
8 | - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
9 | - **Marketing**: writing blog posts, howto's, printing stickers, ...
10 | - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
11 | - **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
12 |
13 | ## Your First Contribution
14 |
15 | Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
16 |
17 | ## Submitting code
18 |
19 | Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests.
20 |
21 | ## Code review process
22 |
23 | The bigger the pull request, the longer it will take to review and merge. Try to [break down large pull requests in smaller chunks](https://oncletom.io/2013/the-55-commits-syndrome/) that are easier to review and merge.
24 |
25 | It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you?
26 |
27 | ## Questions
28 |
29 | If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!).
30 |
31 | ## Credits
32 |
33 | ### Contributors
34 |
35 | Thank you to [all the people who have already contributed](https://github.com/mavaddat/jekyll-cloudinary/graphs/contributors) to jekyll-cloudinary!
36 |
37 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '30 9 * * 4'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'ruby' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v2
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v2
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v2
71 |
--------------------------------------------------------------------------------
/jekyll-cloudinary.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "lib/jekyll/cloudinary/version"
4 |
5 | Gem::Specification.new do |spec|
6 | spec.version = Jekyll::Cloudinary::VERSION
7 | spec.name = "jekyll-cloudinary"
8 | spec.version = Jekyll::Cloudinary::VERSION
9 | spec.authors = ["Nicolas Hoizey", "Mavaddat Javid"]
10 | spec.email = ["nicolas@hoizey.com","info@mavaddat.ca"]
11 | spec.files = %w(Rakefile Gemfile README.md RELEASES.md LICENSE) + Dir["lib/**/*"]
12 | spec.summary = "Jekyll plugin providing Cloudinary-powered responsive image generation"
13 | spec.description = <<-DESC
14 | A plugin to enable a Jekyll-native (Liquid markup) `Cloudinary` tag that generates responsive images srcsets from an optimal number of versions for every image. It does this by finding the minimum number of image versions per file size reductions between each version. The set of breakpoints are based on a difference in the actual image file size at different widths. This means:
15 | - Deciding which image resolutions to select
16 | - Calculating how many different image versions to include
17 | Devs call these 'responsive breakpoints' or 'responsive image breakpoints'.
18 |
19 | Breakpoints for responsive design allow the same images to be displayed in various dimensions. One image for all screen resolutions and different devices is leads to cache or packet inefficiencies. This tool uploads one image and dynamically resizes it to match different screen sizes.
20 | DESC
21 | spec.homepage = "https://mavaddat.github.io/jekyll-cloudinary/"
22 | spec.license = "MIT"
23 | spec.required_ruby_version = ">= 2.6.0"
24 |
25 | spec.metadata["homepage_uri"] = spec.homepage
26 | spec.metadata["source_code_uri"] = "https://github.com/mavaddat/jekyll-cloudinary"
27 | spec.metadata["changelog_uri"] = "https://github.com/mavaddat/jekyll-cloudinary/blob/main/RELEASES.md"
28 |
29 | # Specify which files should be added to the gem when it is released.
30 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31 | spec.files = Dir.chdir(File.expand_path(__dir__)) do
32 | `git ls-files -z`.split("\x0").reject do |f|
33 | (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
34 | end
35 | end
36 | spec.bindir = "exe"
37 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
38 | spec.require_paths = ["lib"]
39 |
40 | spec.add_runtime_dependency "fastimage", "~> 2.2"
41 | spec.add_runtime_dependency "jekyll", "~> 4.2"
42 | spec.add_development_dependency "rake", "~> 13.0.6"
43 | spec.add_development_dependency "rubocop", "~> 1.50.0"
44 | spec.add_development_dependency "rspec", "~> 3.10"
45 | spec.add_development_dependency "rubocop-rspec", "~> 2.5"
46 | spec.add_development_dependency "rubocop-rails", "~> 2.13"
47 | spec.add_development_dependency "rubocop-performance", "~> 1.13"
48 | spec.add_development_dependency "cloudinary", "~> 1.21"
49 | spec.add_development_dependency "bundler"
50 | end
51 |
--------------------------------------------------------------------------------
/RELEASES.md:
--------------------------------------------------------------------------------
1 | # Releases
2 |
3 | ## [v1.21](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.21)
4 |
5 | Bump to Bundler 2.2.30. Update Rubocop idioms.
6 |
7 | ## [v1.14.1](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.14.1)
8 |
9 | Fix the loading attribute handling.
10 |
11 | Add compatibility with Jekyll 4 (thanks [@DirtyF](https://github.com/DirtyF))
12 |
13 | ## [v1.14.0](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.14.0)
14 |
15 | Add support for the loading attribute, for [native lazy loading](https://addyosmani.com/blog/lazy-loading/) (Thanks [@borisschapira](https://github.com/borisschapira))
16 |
17 | Read [the documentation for the loading attribute](https://github.com/mavaddat/jekyll-cloudinary/blob/master/README.md#loading-attribute).
18 |
19 | ## [v1.13.0](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.13.0)
20 |
21 | Add cross-origin support to prevent opaque responses in Service Workers.
22 |
23 | See https://cloudfour.com/thinks/when-7-kb-equals-7-mb/
24 |
25 | ## [v1.12.4](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.12.4)
26 |
27 | Improving gem summary and description to help people find it. There was no mention of "plugin" in it… 🤔
28 |
29 | ## [v1.12.3](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.12.3)
30 |
31 | Strings are now immutable by default, be careful. ([ca68ba7](https://github.com/mavaddat/jekyll-cloudinary/commit/ca68ba7743b69983836b993761d1004494197795))
32 |
33 | ## [v1.12.2](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.12.2)
34 |
35 | Match jekyll's coding style thanks to [@DirtyF](https://github.com/DirtyF) with a little help from [Rubocop](http://rubocop.readthedocs.io/).
36 |
37 | ## [v1.12.1](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.12.1)
38 |
39 | Break early if there is no `cloud_name` in `_config.yml`.
40 |
41 | ## [v1.12.0](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.12.0)
42 |
43 | Thanks to [@suprafly](https://github.com/suprafly)'s [Pull Request](https://github.com/mavaddat/jekyll-cloudinary/pull/29), you can now host your source images on an origin domain different from your website domain.
44 |
45 | ## [v1.11.0](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.11.0)
46 |
47 | Thanks to [Pascal Brokmeier](https://github.com/pascalwhoop)'s [Pull Request](https://github.com/mavaddat/jekyll-cloudinary/pull/34), you can now have responsive images HTML and Cloudinary URLs generated only when you build for production.
48 |
49 | Just make sure to:
50 |
51 | - set the new option `only_prod` to `true`
52 | - and set the environment to `production` before building: `JEKYLL_ENV=production bundle exec jekyll build`
53 |
54 | ## [v1.10.0](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.10.0)
55 |
56 | Fixes an issue caused by Jekyll 3.8.1 introducing a change to content's path, adding an `/#excerpt` at the end in case of an excerpt.
57 |
58 | See https://github.com/mavaddat/jekyll-cloudinary/commit/5372e37e4d31bf1934d90665692b9e14f2ac2147
59 |
60 | ## [v1.9.1](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.9.1)
61 |
62 | Better warning message when the local source image is missing.
63 |
64 | ## [v1.9.0](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.9.0)
65 |
66 | Get the dimensions of the picture from the source path instead of the destination one ([#19](https://github.com/mavaddat/jekyll-cloudinary/issues/19))
67 |
68 | ## [v1.8.1](https://github.com/mavaddat/jekyll-cloudinary/releases/tag/v1.8.1)
69 |
70 | Fixes an issue with local images.
71 |
72 | ## v1.8.0
73 |
74 | Image size detection now uses FastImage instead of RMagick to remove imagemagick dependency, thanks to [@aarongustafson](https://github.com/aarongustafson) ([#25](https://github.com/mavaddat/jekyll-cloudinary/issues/25))
75 |
76 | ## v1.7.0
77 |
78 | It is now possible to use all effects and transformations from the Cloudinary API, thanks to [@aarongustafson](https://github.com/aarongustafson) ([#24](https://github.com/mavaddat/jekyll-cloudinary/issues/24))
79 |
80 | ## v1.4.0
81 |
82 | Now supports sites with baseurl ([#10](https://github.com/mavaddat/jekyll-cloudinary/issues/10))
83 |
84 | ## v1.3.1
85 |
86 | Restores natural width if some computed ones are missing.
87 |
88 | ## v1.3.0
89 |
90 | Restores `width` and `height` attributes.
91 |
92 | ## v1.2.17
93 |
94 | Fixes a little typo with huge impact…
95 |
96 | ## v1.2.16
97 |
98 | Code improvements thanks to Rubocop, initialized by [@DirtyF](https://github.com/DirtyF)
99 |
100 | ## v1.2.15
101 |
102 | Fixed bugs:
103 | - Don’t add `` `height` & `width` attributes ([#7](https://github.com/mavaddat/jekyll-cloudinary/issues/7))
104 | - Dont’t wrap `alt` text in `
` ([#8](https://github.com/mavaddat/jekyll-cloudinary/issues/8))
105 | - `require 'rmagick'` tripped me up ([#11](https://github.com/mavaddat/jekyll-cloudinary/issues/11))
106 |
107 | Thanks [@eeeps](https://github.com/eeeps) for catching these issues!
108 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8 |
9 | ## Our Standards
10 |
11 | Examples of behavior that contributes to a positive environment for our community include:
12 |
13 | * Demonstrating empathy and kindness toward other people
14 | * Being respectful of differing opinions, viewpoints, and experiences
15 | * Giving and gracefully accepting constructive feedback
16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17 | * Focusing on what is best not just for us as individuals, but for the overall community
18 |
19 | Examples of unacceptable behavior include:
20 |
21 | * The use of sexualized language or imagery, and sexual attention or
22 | advances of any kind
23 | * Trolling, insulting or derogatory comments, and personal or political attacks
24 | * Public or private harassment
25 | * Publishing others' private information, such as a physical or email
26 | address, without their explicit permission
27 | * Other conduct which could reasonably be considered inappropriate in a
28 | professional setting
29 |
30 | ## Enforcement Responsibilities
31 |
32 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33 |
34 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35 |
36 | ## Scope
37 |
38 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39 |
40 | ## Enforcement
41 |
42 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at support@mavaddat.ca. All complaints will be reviewed and investigated promptly and fairly.
43 |
44 | All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45 |
46 | ## Enforcement Guidelines
47 |
48 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49 |
50 | ### 1. Correction
51 |
52 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53 |
54 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55 |
56 | ### 2. Warning
57 |
58 | **Community Impact**: A violation through a single incident or series of actions.
59 |
60 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61 |
62 | ### 3. Temporary Ban
63 |
64 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65 |
66 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67 |
68 | ### 4. Permanent Ban
69 |
70 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71 |
72 | **Consequence**: A permanent ban from any sort of public interaction within the community.
73 |
74 | ## Attribution
75 |
76 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78 |
79 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80 |
81 | [homepage]: https://www.contributor-covenant.org
82 |
83 | For answers to common questions about this code of conduct, see the FAQ at
84 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
85 |
--------------------------------------------------------------------------------
/Gemfile.lock~:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | jekyll-cloudinary (1.21)
5 | fastimage (~> 2.2)
6 | jekyll (~> 4.2)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | activesupport (6.1.7)
12 | concurrent-ruby (~> 1.0, >= 1.0.2)
13 | i18n (>= 1.6, < 2)
14 | minitest (>= 5.1)
15 | tzinfo (~> 2.0)
16 | zeitwerk (~> 2.3)
17 | addressable (2.8.1)
18 | public_suffix (>= 2.0.2, < 6.0)
19 | ast (2.4.2)
20 | aws_cf_signer (0.1.3)
21 | cloudinary (1.24.0)
22 | aws_cf_signer
23 | rest-client (>= 2.0.0)
24 | colorator (1.1.0)
25 | concurrent-ruby (1.1.10)
26 | diff-lcs (1.5.0)
27 | domain_name (0.5.20190701)
28 | unf (>= 0.0.5, < 1.0.0)
29 | em-websocket (0.5.3)
30 | eventmachine (>= 0.12.9)
31 | http_parser.rb (~> 0)
32 | eventmachine (1.2.7)
33 | eventmachine (1.2.7-x64-mingw32)
34 | fastimage (2.2.6)
35 | ffi (1.15.5)
36 | ffi (1.15.5-x64-mingw-ucrt)
37 | ffi (1.15.5-x64-mingw32)
38 | forwardable-extended (2.6.0)
39 | http-accept (1.7.0)
40 | http-cookie (1.0.5)
41 | domain_name (~> 0.5)
42 | http_parser.rb (0.8.0)
43 | i18n (1.12.0)
44 | concurrent-ruby (~> 1.0)
45 | jekyll (4.3.1)
46 | addressable (~> 2.4)
47 | colorator (~> 1.0)
48 | em-websocket (~> 0.5)
49 | i18n (~> 1.0)
50 | jekyll-sass-converter (>= 2.0, < 4.0)
51 | jekyll-watch (~> 2.0)
52 | kramdown (~> 2.3, >= 2.3.1)
53 | kramdown-parser-gfm (~> 1.0)
54 | liquid (~> 4.0)
55 | mercenary (>= 0.3.6, < 0.5)
56 | pathutil (~> 0.9)
57 | rouge (>= 3.0, < 5.0)
58 | safe_yaml (~> 1.0)
59 | terminal-table (>= 1.8, < 4.0)
60 | webrick (~> 1.7)
61 | jekyll-sass-converter (2.2.0)
62 | sassc (> 2.0.1, < 3.0)
63 | jekyll-watch (2.2.1)
64 | listen (~> 3.0)
65 | json (2.6.3)
66 | kramdown (2.4.0)
67 | rexml
68 | kramdown-parser-gfm (1.1.0)
69 | kramdown (~> 2.0)
70 | liquid (4.0.3)
71 | listen (3.7.1)
72 | rb-fsevent (~> 0.10, >= 0.10.3)
73 | rb-inotify (~> 0.9, >= 0.9.10)
74 | mercenary (0.4.0)
75 | mime-types (3.4.1)
76 | mime-types-data (~> 3.2015)
77 | mime-types-data (3.2022.0105)
78 | minitest (5.16.3)
79 | netrc (0.11.0)
80 | parallel (1.22.1)
81 | parser (3.1.3.0)
82 | ast (~> 2.4.1)
83 | pathutil (0.16.2)
84 | forwardable-extended (~> 2.6)
85 | public_suffix (5.0.0)
86 | rack (3.0.1)
87 | rainbow (3.1.1)
88 | rake (13.0.6)
89 | rb-fsevent (0.11.2)
90 | rb-inotify (0.10.1)
91 | ffi (~> 1.0)
92 | regexp_parser (2.6.1)
93 | rest-client (2.1.0)
94 | http-accept (>= 1.7.0, < 2.0)
95 | http-cookie (>= 1.0.2, < 2.0)
96 | mime-types (>= 1.16, < 4.0)
97 | netrc (~> 0.8)
98 | rest-client (2.1.0-x64-mingw32)
99 | ffi (~> 1.9)
100 | http-accept (>= 1.7.0, < 2.0)
101 | http-cookie (>= 1.0.2, < 2.0)
102 | mime-types (>= 1.16, < 4.0)
103 | netrc (~> 0.8)
104 | rexml (3.2.5)
105 | rouge (3.30.0)
106 | rspec (3.12.0)
107 | rspec-core (~> 3.12.0)
108 | rspec-expectations (~> 3.12.0)
109 | rspec-mocks (~> 3.12.0)
110 | rspec-core (3.12.0)
111 | rspec-support (~> 3.12.0)
112 | rspec-expectations (3.12.0)
113 | diff-lcs (>= 1.2.0, < 2.0)
114 | rspec-support (~> 3.12.0)
115 | rspec-mocks (3.12.0)
116 | diff-lcs (>= 1.2.0, < 2.0)
117 | rspec-support (~> 3.12.0)
118 | rspec-support (3.12.0)
119 | rubocop (1.41.0)
120 | json (~> 2.3)
121 | parallel (~> 1.10)
122 | parser (>= 3.1.2.1)
123 | rainbow (>= 2.2.2, < 4.0)
124 | regexp_parser (>= 1.8, < 3.0)
125 | rexml (>= 3.2.5, < 4.0)
126 | rubocop-ast (>= 1.23.0, < 2.0)
127 | ruby-progressbar (~> 1.7)
128 | unicode-display_width (>= 1.4.0, < 3.0)
129 | rubocop-ast (1.24.0)
130 | parser (>= 3.1.1.0)
131 | <<<<<<< HEAD
132 | rubocop-minitest (0.25.0)
133 | =======
134 | rubocop-minitest (0.24.0)
135 | >>>>>>> upstream/master
136 | rubocop (>= 0.90, < 2.0)
137 | rubocop-performance (1.15.1)
138 | rubocop (>= 1.7.0, < 2.0)
139 | rubocop-ast (>= 0.4.0)
140 | rubocop-rails (2.17.3)
141 | activesupport (>= 4.2.0)
142 | rack (>= 1.1)
143 | rubocop (>= 1.33.0, < 2.0)
144 | rubocop-rake (0.6.0)
145 | rubocop (~> 1.0)
146 | rubocop-rspec (2.16.0)
147 | rubocop (~> 1.33)
148 | ruby-progressbar (1.11.0)
149 | safe_yaml (1.0.5)
150 | sassc (2.4.0)
151 | ffi (~> 1.9)
152 | sassc (2.4.0-x64-mingw32)
153 | ffi (~> 1.9)
154 | terminal-table (3.0.2)
155 | unicode-display_width (>= 1.1.1, < 3)
156 | tzinfo (2.0.5)
157 | concurrent-ruby (~> 1.0)
158 | unf (0.1.4)
159 | unf_ext
160 | unf_ext (0.0.8.2)
161 | unicode-display_width (2.3.0)
162 | webrick (1.7.0)
163 | zeitwerk (2.6.6)
164 |
165 | PLATFORMS
166 | x64-mingw-ucrt
167 | x64-mingw32
168 | x86_64-darwin-19
169 | x86_64-linux
170 |
171 | DEPENDENCIES
172 | bundler
173 | cloudinary (~> 1.24)
174 | jekyll (~> 4.2)
175 | jekyll-cloudinary!
176 | rake (~> 13.0.6)
177 | rspec (~> 3.10)
178 | rubocop (~> 1.41)
179 | rubocop-minitest
180 | rubocop-performance
181 | rubocop-rails
182 | rubocop-rake
183 | rubocop-rspec (~> 2.5)
184 |
185 | BUNDLED WITH
186 | 2.3.0
187 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | jekyll-cloudinary (1.21)
5 | fastimage (~> 2.2)
6 | jekyll (~> 4.2)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | activesupport (6.1.7.3)
12 | concurrent-ruby (~> 1.0, >= 1.0.2)
13 | i18n (>= 1.6, < 2)
14 | minitest (>= 5.1)
15 | tzinfo (~> 2.0)
16 | zeitwerk (~> 2.3)
17 | addressable (2.8.1)
18 | public_suffix (>= 2.0.2, < 6.0)
19 | ast (2.4.2)
20 | aws_cf_signer (0.1.3)
21 | cloudinary (1.26.0)
22 | aws_cf_signer
23 | rest-client (>= 2.0.0)
24 | colorator (1.1.0)
25 | concurrent-ruby (1.2.2)
26 | diff-lcs (1.5.0)
27 | domain_name (0.5.20190701)
28 | unf (>= 0.0.5, < 1.0.0)
29 | em-websocket (0.5.3)
30 | eventmachine (>= 0.12.9)
31 | http_parser.rb (~> 0)
32 | eventmachine (1.2.7)
33 | eventmachine (1.2.7-x64-mingw32)
34 | fastimage (2.2.7)
35 | ffi (1.15.5)
36 | ffi (1.15.5-x64-mingw-ucrt)
37 | ffi (1.15.5-x64-mingw32)
38 | forwardable-extended (2.6.0)
39 | google-protobuf (3.21.12-x64-mingw-ucrt)
40 | google-protobuf (3.21.12-x64-mingw32)
41 | google-protobuf (3.21.12-x86_64-darwin)
42 | google-protobuf (3.21.12-x86_64-linux)
43 | http-accept (1.7.0)
44 | http-cookie (1.0.5)
45 | domain_name (~> 0.5)
46 | http_parser.rb (0.8.0)
47 | i18n (1.12.0)
48 | concurrent-ruby (~> 1.0)
49 | jekyll (4.3.2)
50 | addressable (~> 2.4)
51 | colorator (~> 1.0)
52 | em-websocket (~> 0.5)
53 | i18n (~> 1.0)
54 | jekyll-sass-converter (>= 2.0, < 4.0)
55 | jekyll-watch (~> 2.0)
56 | kramdown (~> 2.3, >= 2.3.1)
57 | kramdown-parser-gfm (~> 1.0)
58 | liquid (~> 4.0)
59 | mercenary (>= 0.3.6, < 0.5)
60 | pathutil (~> 0.9)
61 | rouge (>= 3.0, < 5.0)
62 | safe_yaml (~> 1.0)
63 | terminal-table (>= 1.8, < 4.0)
64 | webrick (~> 1.7)
65 | jekyll-sass-converter (3.0.0)
66 | sass-embedded (~> 1.54)
67 | jekyll-watch (2.2.1)
68 | listen (~> 3.0)
69 | json (2.6.3)
70 | kramdown (2.4.0)
71 | rexml
72 | kramdown-parser-gfm (1.1.0)
73 | kramdown (~> 2.0)
74 | liquid (4.0.4)
75 | listen (3.8.0)
76 | rb-fsevent (~> 0.10, >= 0.10.3)
77 | rb-inotify (~> 0.9, >= 0.9.10)
78 | mercenary (0.4.0)
79 | mime-types (3.4.1)
80 | mime-types-data (~> 3.2015)
81 | mime-types-data (3.2023.0218.1)
82 | minitest (5.18.0)
83 | netrc (0.11.0)
84 | parallel (1.23.0)
85 | parser (3.2.2.1)
86 | ast (~> 2.4.1)
87 | pathutil (0.16.2)
88 | forwardable-extended (~> 2.6)
89 | public_suffix (5.0.1)
90 | rack (3.0.7)
91 | rainbow (3.1.1)
92 | rake (13.0.6)
93 | rb-fsevent (0.11.2)
94 | rb-inotify (0.10.1)
95 | ffi (~> 1.0)
96 | regexp_parser (2.8.0)
97 | rest-client (2.1.0)
98 | http-accept (>= 1.7.0, < 2.0)
99 | http-cookie (>= 1.0.2, < 2.0)
100 | mime-types (>= 1.16, < 4.0)
101 | netrc (~> 0.8)
102 | rest-client (2.1.0-x64-mingw32)
103 | ffi (~> 1.9)
104 | http-accept (>= 1.7.0, < 2.0)
105 | http-cookie (>= 1.0.2, < 2.0)
106 | mime-types (>= 1.16, < 4.0)
107 | netrc (~> 0.8)
108 | rexml (3.2.5)
109 | rouge (3.30.0)
110 | rspec (3.12.0)
111 | rspec-core (~> 3.12.0)
112 | rspec-expectations (~> 3.12.0)
113 | rspec-mocks (~> 3.12.0)
114 | rspec-core (3.12.0)
115 | rspec-support (~> 3.12.0)
116 | rspec-expectations (3.12.0)
117 | diff-lcs (>= 1.2.0, < 2.0)
118 | rspec-support (~> 3.12.0)
119 | rspec-mocks (3.12.0)
120 | diff-lcs (>= 1.2.0, < 2.0)
121 | rspec-support (~> 3.12.0)
122 | rspec-support (3.12.0)
123 | rubocop (1.50.2)
124 | json (~> 2.3)
125 | parallel (~> 1.10)
126 | parser (>= 3.2.0.0)
127 | rainbow (>= 2.2.2, < 4.0)
128 | regexp_parser (>= 1.8, < 3.0)
129 | rexml (>= 3.2.5, < 4.0)
130 | rubocop-ast (>= 1.28.0, < 2.0)
131 | ruby-progressbar (~> 1.7)
132 | unicode-display_width (>= 2.4.0, < 3.0)
133 | rubocop-ast (1.28.1)
134 | parser (>= 3.2.1.0)
135 | rubocop-capybara (2.17.1)
136 | rubocop (~> 1.41)
137 | rubocop-minitest (0.31.0)
138 | rubocop (>= 1.39, < 2.0)
139 | rubocop-performance (1.17.1)
140 | rubocop (>= 1.7.0, < 2.0)
141 | rubocop-ast (>= 0.4.0)
142 | rubocop-rails (2.19.1)
143 | activesupport (>= 4.2.0)
144 | rack (>= 1.1)
145 | rubocop (>= 1.33.0, < 2.0)
146 | rubocop-rake (0.6.0)
147 | rubocop (~> 1.0)
148 | rubocop-rspec (2.20.0)
149 | rubocop (~> 1.33)
150 | rubocop-capybara (~> 2.17)
151 | ruby-progressbar (1.13.0)
152 | safe_yaml (1.0.5)
153 | sass-embedded (1.57.1-x64-mingw-ucrt)
154 | google-protobuf (~> 3.21)
155 | sass-embedded (1.57.1-x64-mingw32)
156 | google-protobuf (~> 3.21)
157 | sass-embedded (1.57.1-x86_64-darwin)
158 | google-protobuf (~> 3.21)
159 | sass-embedded (1.57.1-x86_64-linux-gnu)
160 | google-protobuf (~> 3.21)
161 | terminal-table (3.0.2)
162 | unicode-display_width (>= 1.1.1, < 3)
163 | tzinfo (2.0.6)
164 | concurrent-ruby (~> 1.0)
165 | unf (0.1.4)
166 | unf_ext
167 | unf_ext (0.0.8.2)
168 | unf_ext (0.0.8.2-x64-mingw-ucrt)
169 | unf_ext (0.0.8.2-x64-mingw32)
170 | unicode-display_width (2.4.2)
171 | webrick (1.7.0)
172 | zeitwerk (2.6.7)
173 |
174 | PLATFORMS
175 | x64-mingw-ucrt
176 | x64-mingw32
177 | x86_64-darwin-19
178 | x86_64-linux
179 |
180 | DEPENDENCIES
181 | bundler
182 | cloudinary (~> 1.26)
183 | jekyll (~> 4.3)
184 | jekyll-cloudinary!
185 | rake (~> 13.0.6)
186 | rspec (~> 3.10)
187 | rubocop (~> 1.50)
188 | rubocop-minitest
189 | rubocop-performance
190 | rubocop-rails
191 | rubocop-rake
192 | rubocop-rspec (~> 2.5)
193 |
194 | BUNDLED WITH
195 | 2.3.0
196 |
--------------------------------------------------------------------------------
/lib/jekyll/cloudinary.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Jekyll
4 | module Cloudinary
5 |
6 | class CloudinaryTag < Liquid::Tag
7 | require "fastimage"
8 |
9 | def initialize(tag_name, markup, tokens)
10 | @markup = markup
11 | super
12 | end
13 |
14 | def render(context)
15 | # Default settings
16 | settings_defaults = {
17 | "cloud_name" => "",
18 | "only_prod" => false,
19 | "verbose" => false,
20 | }
21 | preset_defaults = {
22 | "min_width" => 320,
23 | "max_width" => 1200,
24 | "fallback_max_width" => 1200,
25 | "steps" => 5,
26 | "sizes" => "100vw",
27 | "figure" => "auto",
28 | "attributes" => {},
29 | "width_height" => true,
30 | # Cloudinary transformations
31 | "height" => false,
32 | "crop" => "limit",
33 | "aspect_ratio" => false,
34 | "gravity" => false,
35 | "zoom" => false,
36 | "x" => false,
37 | "y" => false,
38 | "format" => false,
39 | "fetch_format" => "auto",
40 | "quality" => "auto",
41 | "radius" => false,
42 | "angle" => false,
43 | "effect" => false,
44 | "opacity" => false,
45 | "border" => false,
46 | "background" => false,
47 | "overlay" => false,
48 | "underlay" => false,
49 | "default_image" => false,
50 | "delay" => false,
51 | "color" => false,
52 | "color_space" => false,
53 | "dpr" => false,
54 | "page" => false,
55 | "density" => false,
56 | "flags" => false,
57 | "transformation" => false,
58 | }
59 |
60 | # TODO: Add validation for these parameters
61 | transformation_options = {
62 | "height" => "h",
63 | "crop" => "c", # can include add-on: imagga_scale
64 | "aspect_ratio" => "ar",
65 | "gravity" => "g",
66 | "zoom" => "z",
67 | "x" => "x",
68 | "y" => "y",
69 | "fetch_format" => "f",
70 | "quality" => "q", # can include add-on: jpegmini
71 | "radius" => "r",
72 | "angle" => "a",
73 | "effect" => "e", # can include add-on: viesus_correct
74 | "opacity" => "o",
75 | "border" => "bo",
76 | "background" => "b",
77 | "overlay" => "l",
78 | "underlay" => "u",
79 | "default_image" => "d",
80 | "delay" => "dl",
81 | "color" => "co",
82 | "color_space" => "cs",
83 | "dpr" => "dpr",
84 | "page" => "pg",
85 | "density" => "dn",
86 | "flags" => "fl",
87 | "transformation" => "t",
88 | }
89 |
90 | # Settings
91 | site = context.registers[:site]
92 | site_url = site.config["url"] || ""
93 | site_baseurl = site.config["baseurl"] || ""
94 | if site.config["cloudinary"].nil?
95 | Jekyll.logger.abort_with("[Cloudinary]", "You must set your cloud_name in _config.yml")
96 | end
97 | settings = settings_defaults.merge(site.config["cloudinary"])
98 | if settings["cloud_name"] == ""
99 | Jekyll.logger.abort_with("[Cloudinary]", "You must set your cloud_name in _config.yml")
100 | end
101 | url = settings["origin_url"] || (site_url + site_baseurl)
102 |
103 | # Get Markdown converter
104 | markdown_converter = site.find_converter_instance(::Jekyll::Converters::Markdown)
105 |
106 | preset_user_defaults = {}
107 | if settings["presets"]
108 | if settings["presets"]["default"]
109 | preset_user_defaults = settings["presets"]["default"]
110 | end
111 | end
112 |
113 | preset = preset_defaults.merge(preset_user_defaults)
114 |
115 | # Render any liquid variables in tag arguments and unescape template code
116 | rendered_markup = Liquid::Template
117 | .parse(@markup)
118 | .render(context)
119 | .gsub(%r!\\\{\\\{|\\\{\\%!, '\{\{' => "{{", '\{\%' => "{%")
120 |
121 | # Extract tag segments
122 | markup =
123 | %r!^(?:(? even when there is a caption
181 | img_attr = "".dup
182 | if html_attr["alt"]
183 | img_attr << " alt=\"#{html_attr["alt"]}\""
184 | html_attr.delete("alt")
185 | end
186 | if html_attr["title"]
187 | img_attr << " title=\"#{html_attr["title"]}\""
188 | html_attr.delete("title")
189 | end
190 | if html_attr["loading"]
191 | img_attr << " loading=\"#{html_attr["loading"]}\""
192 | html_attr.delete("loading")
193 | end
194 |
195 | attr_string = html_attr.map { |a, v| "#{a}=\"#{v}\"" }.join(" ")
196 |
197 | # Figure out the Cloudinary transformations
198 | transformations = []
199 | transformations_string = ""
200 | transformation_options.each do |key, shortcode|
201 | if preset[key]
202 | transformations << "#{shortcode}_#{preset[key]}"
203 | end
204 | end
205 | unless transformations.empty?
206 | transformations_string = transformations.compact.reject(&:empty?).join(",") + ","
207 | end
208 |
209 | # Build source image URL
210 | is_image_remote = %r!^https?!.match(image_src)
211 | if is_image_remote
212 | # It's remote
213 | image_dest_path = image_src
214 | image_dest_url = image_src
215 | natural_width, natural_height = FastImage.size(image_dest_url)
216 | if natural_width.nil?
217 | Jekyll.logger.warn("remote url doesn't exists " + image_dest_url)
218 | return "
"
219 | end
220 | width_height = "width=\"#{natural_width}\" height=\"#{natural_height}\""
221 | fallback_url = "https://res.cloudinary.com/#{settings["cloud_name"]}/image/#{type}/#{transformations_string}w_#{preset["fallback_max_width"]}/#{image_dest_url}"
222 | else
223 | # It's a local image
224 | is_image_src_absolute = %r!^/.*$!.match(image_src)
225 | if is_image_src_absolute
226 | image_src_path = File.join(
227 | site.config["source"],
228 | image_src
229 | )
230 | image_dest_path = File.join(
231 | site.config["destination"],
232 | image_src
233 | )
234 | image_dest_url = File.join(
235 | url,
236 | image_src
237 | )
238 | else
239 | image_src_path = File.join(
240 | site.config["source"],
241 | File.dirname(context["page"]["path"].chomp("/#excerpt")),
242 | image_src
243 | )
244 | image_dest_path = File.join(
245 | site.config["destination"],
246 | File.dirname(context["page"]["url"]),
247 | image_src
248 | )
249 | image_dest_url = File.join(
250 | url,
251 | File.dirname(context["page"]["url"]),
252 | image_src
253 | )
254 | end
255 | if File.exist?(image_src_path)
256 | natural_width, natural_height = FastImage.size(image_src_path)
257 | width_height = "width=\"#{natural_width}\" height=\"#{natural_height}\""
258 | fallback_url = "https://res.cloudinary.com/#{settings["cloud_name"]}/image/#{type}/#{transformations_string}w_#{preset["fallback_max_width"]}/#{image_dest_url}"
259 | else
260 | natural_width = 100_000
261 | width_height = ""
262 | Jekyll.logger.warn(
263 | "[Cloudinary]",
264 | "Couldn't find this image to check its width: #{image_src_path}."
265 | )
266 | fallback_url = image_dest_url
267 | end
268 | end
269 |
270 | # Don't generate responsive image HTML and Cloudinary URLs for local development
271 | if settings["only_prod"] && ENV["JEKYLL_ENV"] != "production"
272 | return "
"
273 | end
274 |
275 | srcset = []
276 | steps = preset["steps"].to_i
277 | min_width = preset["min_width"].to_i
278 | max_width = preset["max_width"].to_i
279 | step_width = (max_width - min_width) / (steps - 1)
280 | sizes = preset["sizes"]
281 |
282 | if natural_width < min_width
283 | if settings["verbose"]
284 | Jekyll.logger.warn(
285 | "[Cloudinary]",
286 | "Width of source image '#{File.basename(image_src)}' (#{natural_width}px) \
287 | in #{context["page"]["path"]} not enough for ANY srcset version"
288 | )
289 | end
290 | srcset << "https://res.cloudinary.com/#{settings["cloud_name"]}/image/#{type}/#{transformations_string}w_#{natural_width}/#{image_dest_url} #{natural_width}w"
291 | else
292 | missed_sizes = []
293 | (1..steps).each do |factor|
294 | width = min_width + (factor - 1) * step_width
295 | if width <= natural_width
296 | srcset << "https://res.cloudinary.com/#{settings["cloud_name"]}/image/#{type}/#{transformations_string}w_#{width}/#{image_dest_url} #{width}w"
297 | else
298 | missed_sizes.push(width)
299 | end
300 | end
301 | unless missed_sizes.empty?
302 | srcset << "https://res.cloudinary.com/#{settings["cloud_name"]}/image/#{type}/#{transformations_string}w_#{natural_width}/#{image_dest_url} #{natural_width}w"
303 | if settings["verbose"]
304 | Jekyll.logger.warn(
305 | "[Cloudinary]",
306 | "Width of source image '#{File.basename(image_src)}' (#{natural_width}px) \
307 | in #{context["page"]["path"]} not enough for #{missed_sizes.join("px, ")}px \
308 | version#{missed_sizes.length > 1 ? "s" : ""}"
309 | )
310 | end
311 | end
312 | end
313 | srcset_string = srcset.join(",\n")
314 |
315 | # preset['figure'] can be 'never', 'auto' or 'always'
316 | if (caption || preset["figure"] == "always") && preset["figure"] != "never"
317 | "\n
\n
"
320 | end
321 | end
322 | end
323 |
324 | end
325 | end
326 |
327 | Liquid::Template.register_tag("cloudinary", Jekyll::Cloudinary::CloudinaryTag)
328 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Jekyll Cloudinary Liquid tag
2 | [](https://github.com/nhoizey/jekyll-cloudinary/actions/workflows/rubocop.yml)
3 |
6 |
7 | `jekyll-cloudinary` is a [Jekyll](http://jekyllrb.com/) plugin adding a [Liquid](http://liquidmarkup.org) tag to ease the use of [Cloudinary](https://nho.io/cloudinary-signup) for responsive images in your Markdown/[Kramdown](http://kramdown.gettalong.org/) posts.
8 |
9 | It builds the HTML for responsive images in the posts, using the `srcset` and `sizes` attributes for the `
` tag (see [the "varying size and density" section of this post](https://jakearchibald.com/2015/anatomy-of-responsive-images/#varying-size-and-density) if this is new for you, and why it's recommended to [not use `
` code and local URLs.
105 |
106 | [`JEKYLL_ENV=development` is the default value](https://jekyllrb.com/docs/configuration/#specifying-a-jekyll-environment-at-build-time).
107 |
108 | If you don't set `only_prod` or set it to `false`, responsive image HTML and Cloudinary URLs are always generated, whatever the environment. jekyll-cloudinary had only this behavior before version 1.11.0.
109 |
110 | #### `verbose` (default: `false`)
111 |
112 | When set to `true`, this setting will show messages in the console when something goes wrong, such as:
113 |
114 | ```
115 | [Cloudinary] Couldn't find this image to check its width: /path/to/jekyll/_site/assets/img.jpg
116 | ```
117 |
118 | or
119 |
120 | ```
121 | [Cloudinary] Natural width of source image 'img.jpg' (720px) in _posts/2016-06-09-post.md not enough for creating 1600px version
122 | ```
123 |
124 | #### `origin_url`
125 |
126 | When `origin_url` is set, **jekyll-cloudinary** will use this URL rather than `site.url` as origin of the source images.
127 |
128 | This allows you to store your source image on a different domain than your website.
129 |
130 | ### Optional (but highly recommended) presets
131 |
132 | You can now define the presets you need for your posts' images, starting with the default one:
133 |
134 | #### Default preset
135 |
136 | The default preset is the one you don't even have to mention when using the Liquid tag, and that will be used if a preset you use in the tag doesn't exist.
137 |
138 | ```yaml
139 | cloudinary:
140 | …
141 | presets:
142 | default:
143 | min_width: 320
144 | max_width: 1600
145 | fallback_max_width: 800
146 | steps: 5
147 | sizes: "(min-width: 50rem) 50rem, 90vw"
148 | ```
149 |
150 | This preset will generate five images 320 to 1600 pixels wide in the `srcset` and define `sizes` as `"(min-width: 50rem) 50rem, 90vw"`. The fallback image defined in the `src` will have a width of 800 pixels.
151 |
152 | With this preset, you only have to write this in your Markdown post:
153 |
154 |
155 | ```liquid
156 | {% cloudinary /assets/img.jpg alt="beautiful!" %}
157 | ```
158 |
159 |
160 | To get this HTML:
161 |
162 | ```html
163 |
177 | ```
178 |
179 | There is a true default `default` preset, but you're strongly encouraged to override it with your own default preset.
180 |
181 | #### Additional presets
182 |
183 | You can add other presets if you need several image sizes in your posts.
184 |
185 | Here is an example for images that take only one third of the post width:
186 |
187 | ```yaml
188 | cloudinary:
189 | …
190 | presets:
191 | …
192 | onethird:
193 | min_width: 110
194 | max_width: 535
195 | fallback_max_width: 300
196 | steps: 3
197 | sizes: "(min-width: 50rem) 17rem, 30vw"
198 | attributes:
199 | class: "one3rd"
200 | loading: "lazy"
201 | ```
202 |
203 | To use this additional preset, you will have to write this in your Markdown post:
204 |
205 |
206 | ```liquid
207 | {% cloudinary onethird /assets/img.jpg %}
208 | ```
209 |
210 |
211 | The generated element will also get a `class="one3rd"` that can be useful for example with this CSS:
212 |
213 | ```css
214 | .one3rd {
215 | max-width: 33%;
216 | float: right;
217 | margin: 0 0 1em 1em;
218 | }
219 | ```
220 |
221 | ### Detailed preset settings
222 |
223 | #### `figure` (default: `auto`)
224 |
225 | This setting lets you decide what to do when there is a `caption` attribute in the Cloudinary Liquid tag.
226 |
227 | The value can be:
228 |
229 | - `auto` (default): will generate a ``, losing the caption
231 | - `always`: will always generate a `
` if they are `alt`, `title` or `loading`, or to the `
376 | ```
377 |
378 | And for the screenshot:
379 |
380 | ```html
381 |
394 |