From 883b15bfe46f85a07e6d2b73a3e213f82d70db05 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 11:04:14 -0800 Subject: [PATCH 01/11] add site namespace to Rakefile --- Rakefile | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index 1dbdf0d..9d828d5 100644 --- a/Rakefile +++ b/Rakefile @@ -30,14 +30,14 @@ namespace :update do temp_dir = Dir.mktmpdir artifact_bundle_url = "https://github.com/calda/SwiftFormat-nightly/releases/download/#{latest_version_number}/swiftformat.artifactbundle.zip" artifact_bundle_zip_path = "#{temp_dir}/swiftformat.artifactbundle.zip" - + sh "curl #{artifact_bundle_url} -L --output #{artifact_bundle_zip_path}" checksum = `swift package compute-checksum #{artifact_bundle_zip_path}` # Update the Package.swift file to reference this version package_manifest_path = 'Package.swift' package_manifest_content = File.read(package_manifest_path) - + updated_swift_format_reference = <<-EOS .binaryTarget( name: "swiftformat", @@ -45,11 +45,15 @@ namespace :update do checksum: "#{checksum.strip}" ), EOS - + regex = /[ ]*.binaryTarget\([\S\s]*name: "swiftformat"[\S\s]*?\),\s/ updated_package_manifest = package_manifest_content.gsub(regex, updated_swift_format_reference) File.open(package_manifest_path, "w") { |file| file.puts updated_package_manifest } - + puts "Updated Package.swift to reference SwiftFormat #{latest_version_number}" end end + +namespace :site do + +end From 7a9d4233692d31be75d8a59ddcc4f026cc0aeac7 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 11:13:35 -0800 Subject: [PATCH 02/11] add serve task --- Rakefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Rakefile b/Rakefile index 9d828d5..b18c76b 100644 --- a/Rakefile +++ b/Rakefile @@ -55,5 +55,8 @@ namespace :update do end namespace :site do + desc 'Serves the site to support previewing its content during development' + task :serve do + end end From 7e33cffb6946619d5f20a014dcab19436b8aa39a Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 11:19:58 -0800 Subject: [PATCH 03/11] Add site readme --- site/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 site/README.md diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..6f9e17d --- /dev/null +++ b/site/README.md @@ -0,0 +1,11 @@ +# Website + +## Local development + +To start the local development server, run the following command: + +```bash +bundle exec rake site:serve +``` + +Once the server is running, open [http://localhost:4000](http://localhost:4000) in your browser to preview the site. From 44f490d59f79f699777dcf7c3cd13703da8d56a9 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 11:26:14 -0800 Subject: [PATCH 04/11] add and install jekyll --- Gemfile | 1 + Gemfile.lock | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/Gemfile b/Gemfile index 9c085a2..1fe32b4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,4 @@ source 'https://rubygems.org' do gem "rake", "~> 13.0.0" + gem "jekyll", "~> 4.4.1" end diff --git a/Gemfile.lock b/Gemfile.lock index 2ffc012..1813fd0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,13 +4,88 @@ GEM GEM remote: https://rubygems.org/ specs: + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + base64 (0.3.0) + bigdecimal (3.3.1) + colorator (1.1.0) + concurrent-ruby (1.3.5) + csv (3.3.5) + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + eventmachine (1.2.7) + ffi (1.17.2) + ffi (1.17.2-arm64-darwin) + forwardable-extended (2.6.0) + google-protobuf (4.33.1) + bigdecimal + rake (>= 13) + google-protobuf (4.33.1-arm64-darwin) + bigdecimal + rake (>= 13) + http_parser.rb (0.8.0) + i18n (1.14.7) + concurrent-ruby (~> 1.0) + jekyll (4.4.1) + addressable (~> 2.4) + base64 (~> 0.2) + colorator (~> 1.0) + csv (~> 3.0) + em-websocket (~> 0.5) + i18n (~> 1.0) + jekyll-sass-converter (>= 2.0, < 4.0) + jekyll-watch (~> 2.0) + json (~> 2.6) + kramdown (~> 2.3, >= 2.3.1) + kramdown-parser-gfm (~> 1.0) + liquid (~> 4.0) + mercenary (~> 0.3, >= 0.3.6) + pathutil (~> 0.9) + rouge (>= 3.0, < 5.0) + safe_yaml (~> 1.0) + terminal-table (>= 1.8, < 4.0) + webrick (~> 1.7) + jekyll-sass-converter (3.1.0) + sass-embedded (~> 1.75) + jekyll-watch (2.2.1) + listen (~> 3.0) + json (2.16.0) + kramdown (2.5.1) + rexml (>= 3.3.9) + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.4) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.4.0) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (6.0.2) rake (13.0.6) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + rexml (3.4.4) + rouge (4.6.1) + safe_yaml (1.0.5) + sass-embedded (1.94.2) + google-protobuf (~> 4.31) + rake (>= 13) + sass-embedded (1.94.2-arm64-darwin) + google-protobuf (~> 4.31) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + unicode-display_width (2.6.0) + webrick (1.9.1) PLATFORMS arm64-darwin-23 ruby DEPENDENCIES + jekyll (~> 4.4.1)! rake (~> 13.0.0)! BUNDLED WITH From 55ed3a3b55cbcc05dc21a94d29c5cf6ac85b3239 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 11:37:38 -0800 Subject: [PATCH 05/11] add missing details closing tags to README --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 0f8c97e..20c6b4f 100644 --- a/README.md +++ b/README.md @@ -1056,6 +1056,8 @@ _You can enable the following settings in Xcode by running [this script](resourc ] ``` + + * (link) [Long](https://github.com/airbnb/swift#column-width) type aliases of protocol compositions should wrap before the `=` and before each individual `&`. [![SwiftFormat: wrapArguments](https://img.shields.io/badge/SwiftFormat-wrapArguments-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#wrapArguments)
@@ -1084,6 +1086,8 @@ _You can enable the following settings in Xcode by running [this script](resourc & UniverseSimulatorServiceProviding ``` +
+ * (link) **Sort protocol composition type aliases alphabetically.** [![SwiftFormat: sortTypealiases](https://img.shields.io/badge/SwiftFormat-sortTypealiases-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#sortTypealiases)
@@ -1110,6 +1114,8 @@ _You can enable the following settings in Xcode by running [this script](resourc & UniverseSimulatorServiceProviding ``` +
+ * (link) Omit the right-hand side of the expression when unwrapping an optional property to a non-optional property with the same name. [![SwiftFormat: redundantOptionalBinding](https://img.shields.io/badge/SwiftFormat-redundantOptionalBinding-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundantOptionalBinding)
@@ -1142,6 +1148,8 @@ _You can enable the following settings in Xcode by running [this script](resourc else { … } ``` +
+ * (link) **Else statements should start on the same line as the previous condition's closing brace, unless the conditions are separated by a blank line or comments.** [![SwiftFormat: elseOnSameLine](https://img.shields.io/badge/SwiftFormat-elseOnSameLine-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#elseOnSameLine)
@@ -1196,6 +1204,8 @@ _You can enable the following settings in Xcode by running [this script](resourc } ``` +
+ * (link) **Multi-line conditional statements should break after the leading keyword.** Indent each individual statement by [2 spaces](https://github.com/airbnb/swift#spaces-over-tabs). [![SwiftFormat: wrapArguments](https://img.shields.io/badge/SwiftFormat-wrapArguments-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#wrapArguments)
@@ -1609,6 +1619,8 @@ _You can enable the following settings in Xcode by running [this script](resourc } ``` +
+ * (link) **Indent the body and closing triple-quote of multiline string literals**, unless the string literal begins on its own line in which case the string literal contents and closing triple-quote should have the same indentation as the opening triple-quote. [![SwiftFormat: indent](https://img.shields.io/badge/SwiftFormat-indent-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#indent)
@@ -1779,6 +1791,8 @@ _You can enable the following settings in Xcode by running [this script](resourc } ``` +
+ * (link) For function calls and declarations, there should be no spaces before or inside the parentheses of the argument list. [![SwiftFormat: spaceInsideParens](https://img.shields.io/badge/SwiftFormat-spaceInsideParens-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#spaceInsideParens) [![SwiftFormat: spaceAroundParens](https://img.shields.io/badge/SwiftFormat-spaceAroundParens-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#spaceAroundParens)
@@ -3010,6 +3024,8 @@ _You can enable the following settings in Xcode by running [this script](resourc } ``` +
+ * (link) **Avoid global functions whenever possible.** Prefer methods within type definitions.
@@ -4694,6 +4710,8 @@ _You can enable the following settings in Xcode by running [this script](resourc } ``` +
+ * (link) **Prefer throwing tests to `try!`**. `try!` will crash your test suite like a force-unwrapped optional. XCTest and Swift Testing support throwing test methods, so use that instead. [![SwiftFormat: noForceTryInTests](https://img.shields.io/badge/SwiftFormat-noForceTryInTests-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#noForceTryInTests)
From 6a80f3560576569193c37e3f9396d4bcd9a9d956 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 11:52:58 -0800 Subject: [PATCH 06/11] add main github action --- .github/workflows/main.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b65dd6..8e81282 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,3 +40,16 @@ jobs: xcode: ${{ matrix.xcode }} - name: Run Unit Tests run: swift test + + site-build: + name: Site Build + runs-on: macos-15 + steps: + - uses: actions/checkout@v6 + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: Build site + run: bundle exec rake site:build + env: + JEKYLL_ENV: production From 2ae527b96345e3718df9cb446c95ddb826e11fbd Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 11:58:24 -0800 Subject: [PATCH 07/11] modernize gemfile --- Gemfile | 8 ++++---- Gemfile.lock | 7 ++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 1fe32b4..48506b8 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source 'https://rubygems.org' do - gem "rake", "~> 13.0.0" - gem "jekyll", "~> 4.4.1" -end +source 'https://rubygems.org' + +gem "rake", "~> 13.0.0" +gem "jekyll", "~> 4.4.1" diff --git a/Gemfile.lock b/Gemfile.lock index 1813fd0..48e937e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,3 @@ -GEM - specs: - GEM remote: https://rubygems.org/ specs: @@ -85,8 +82,8 @@ PLATFORMS ruby DEPENDENCIES - jekyll (~> 4.4.1)! - rake (~> 13.0.0)! + jekyll (~> 4.4.1) + rake (~> 13.0.0) BUNDLED WITH 2.5.4 From 9d2ab0c82cfb23a74ce1066f2c1729d95c57e2c3 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 12:19:51 -0800 Subject: [PATCH 08/11] add site content generator --- Rakefile | 6 ++++ site/.gitignore | 3 ++ site/site_content.rb | 80 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 site/.gitignore create mode 100644 site/site_content.rb diff --git a/Rakefile b/Rakefile index b18c76b..22eeb6f 100644 --- a/Rakefile +++ b/Rakefile @@ -55,6 +55,12 @@ namespace :update do end namespace :site do + desc 'Prints the README content used to build the site' + task :filter_readme do + require_relative 'site/site_content' + puts SiteContent.new.filter_readme + end + desc 'Serves the site to support previewing its content during development' task :serve do diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 0000000..8eef764 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,3 @@ +# These files are generated. +index.md +assets/css/syntax.css diff --git a/site/site_content.rb b/site/site_content.rb new file mode 100644 index 0000000..f101cd2 --- /dev/null +++ b/site/site_content.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'open3' + +class SiteContent + attr_reader :readme_path, :index_path, :syntax_css_path + + def initialize() + site_dir = File.expand_path(__dir__) + @readme_path = File.expand_path('../README.md', __dir__) + @index_path = File.join(site_dir, 'index.md') + @syntax_css_path = File.join(site_dir, 'assets/css/syntax.css') + end + + def filter_readme + (filter_readme_lines + ['']).join("\n") + end + + # Write index.md file. + def write_index + File.write(index_path, generate_front_matter + process_readme_content) + end + + # Write syntax.css file. + def generate_syntax_css + stdout, stderr, status = Open3.capture3('bundle', 'exec', 'rougify', 'style', 'github.light') + raise "rougify failed:\n#{stderr}" unless status.success? + + File.write(syntax_css_path, stdout) + end + + private + + def generate_front_matter + <<~FRONT + --- + layout: default + --- + + FRONT + end + + def process_readme_content + filter_readme.gsub('
', '
') + end + + def filter_readme_lines + lines = File.readlines(readme_path, chomp: true) + filtered = [] + skip_plugin_section = false + skip_amendments_section = false + + lines.each do |line| + # Exclude the SPM command plugin section from the site. + if line.start_with?('## Swift Package Manager command plugin') + skip_plugin_section = true + next + elsif skip_plugin_section + skip_plugin_section = false if line.start_with?('## ') + next if skip_plugin_section + end + + if line.start_with?('## Amendments') + skip_amendments_section = true + next + elsif skip_amendments_section + skip_amendments_section = false if line.start_with?('** ') + next if skip_amendments_section + end + + # Exclude the badges from the site. + stripped = line.strip + next if stripped.start_with?('[![](') && stripped.include?('swiftpackageindex.com') + + filtered << line.rstrip + end + + filtered + end +end From 49c70c76bdd42155dfe4f88d0d75a3a12e008263 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 12:52:51 -0800 Subject: [PATCH 09/11] polish --- .gitignore | 5 +++- Rakefile | 20 ++++++++++++-- site/.gitignore | 6 ++--- site/site_content.rb | 8 ++---- site/src/_config.yml | 6 +++++ site/src/_layouts/default.html | 16 ++++++++++++ site/src/assets/css/style.css | 48 ++++++++++++++++++++++++++++++++++ 7 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 site/src/_config.yml create mode 100644 site/src/_layouts/default.html create mode 100644 site/src/assets/css/style.css diff --git a/.gitignore b/.gitignore index b69502f..65da1d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ .build .swiftpm .DS_Store -.vscode \ No newline at end of file +.vscode + +# The site generated by Jekyll. +_site \ No newline at end of file diff --git a/Rakefile b/Rakefile index 22eeb6f..a31cc5f 100644 --- a/Rakefile +++ b/Rakefile @@ -61,8 +61,24 @@ namespace :site do puts SiteContent.new.filter_readme end - desc 'Serves the site to support previewing its content during development' - task :serve do + desc 'Prepares index.md and syntax highlighting assets' + task :prepare do + require_relative 'site/site_content' + puts '📋 Generating index.md from README.md with frontmatter...' + SiteContent.new.write_index + puts '🎨 Generating syntax highlighting CSS...' + SiteContent.new.generate_syntax_css + end + desc 'Builds the static site into _site/' + task build: :prepare do + env = { 'JEKYLL_ENV' => ENV.fetch('JEKYLL_ENV', 'production') } + sh env, 'bundle exec jekyll build --source site/src' + end + + desc 'Serves the site to support previewing its content during development' + task serve: :prepare do + env = { 'JEKYLL_ENV' => 'development' } + sh env, 'bundle exec jekyll serve --source site/src' end end diff --git a/site/.gitignore b/site/.gitignore index 8eef764..46dab74 100644 --- a/site/.gitignore +++ b/site/.gitignore @@ -1,3 +1,3 @@ -# These files are generated. -index.md -assets/css/syntax.css +# These site source files are generated. +src/index.md +src/assets/css/syntax.css diff --git a/site/site_content.rb b/site/site_content.rb index f101cd2..f086e98 100644 --- a/site/site_content.rb +++ b/site/site_content.rb @@ -6,7 +6,7 @@ class SiteContent attr_reader :readme_path, :index_path, :syntax_css_path def initialize() - site_dir = File.expand_path(__dir__) + site_dir = File.expand_path('src', __dir__) @readme_path = File.expand_path('../README.md', __dir__) @index_path = File.join(site_dir, 'index.md') @syntax_css_path = File.join(site_dir, 'assets/css/syntax.css') @@ -18,7 +18,7 @@ def filter_readme # Write index.md file. def write_index - File.write(index_path, generate_front_matter + process_readme_content) + File.write(index_path, generate_front_matter + filter_readme) end # Write syntax.css file. @@ -40,10 +40,6 @@ def generate_front_matter FRONT end - def process_readme_content - filter_readme.gsub('
', '
') - end - def filter_readme_lines lines = File.readlines(readme_path, chomp: true) filtered = [] diff --git a/site/src/_config.yml b/site/src/_config.yml new file mode 100644 index 0000000..04594c4 --- /dev/null +++ b/site/src/_config.yml @@ -0,0 +1,6 @@ +title: Airbnb Swift Style Guide +github_url: https://github.com/airbnb/swift +markdown: kramdown +kramdown: + input: GFM + parse_block_html: true diff --git a/site/src/_layouts/default.html b/site/src/_layouts/default.html new file mode 100644 index 0000000..3e1fbca --- /dev/null +++ b/site/src/_layouts/default.html @@ -0,0 +1,16 @@ + + + + + + {{ page.title | default: site.title }} + + + + + {{ content }} + + + diff --git a/site/src/assets/css/style.css b/site/src/assets/css/style.css new file mode 100644 index 0000000..49c7fe4 --- /dev/null +++ b/site/src/assets/css/style.css @@ -0,0 +1,48 @@ +body { + font-family: system-ui, sans-serif; + line-height: 1.6; + margin: 0 auto; + padding: 3rem 2rem 2rem; + max-width: 900px; +} + +code, +pre { + font-family: 'SF Mono', Monaco, Menlo, 'Courier New', monospace; + background: #f5f5f5; +} + +code { + padding: 0.1rem 0.35rem; + border-radius: 3px; + font-size: 0.9em; +} + +pre { + margin: 1.5rem 0; + padding: 1rem; + overflow-x: auto; +} + +pre code { + background: transparent; + padding: 0; +} + +footer { + margin-top: 3rem; + padding-top: 1rem; + border-top: 1px solid #ddd; + text-align: center; + font-weight: 600; +} + +footer a { + color: #555; + text-decoration: none; +} + +footer a:hover { + color: #000; + text-decoration: underline; +} From b35f85859629a992d14278e0b27a0206c3ab1874 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 13:03:21 -0800 Subject: [PATCH 10/11] newline --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 65da1d1..7381c9b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ .vscode # The site generated by Jekyll. -_site \ No newline at end of file +_site From 9fbbe2f84e8e4b46db27232c4fabf8f01ac8df19 Mon Sep 17 00:00:00 2001 From: Michael Bachand Date: Sat, 22 Nov 2025 13:09:41 -0800 Subject: [PATCH 11/11] simplify --- site/src/assets/css/style.css | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/site/src/assets/css/style.css b/site/src/assets/css/style.css index 49c7fe4..dd98738 100644 --- a/site/src/assets/css/style.css +++ b/site/src/assets/css/style.css @@ -6,13 +6,13 @@ body { max-width: 900px; } -code, -pre { +pre, +code { font-family: 'SF Mono', Monaco, Menlo, 'Courier New', monospace; - background: #f5f5f5; } -code { +:not(pre) > code { + background: #f5f5f5; padding: 0.1rem 0.35rem; border-radius: 3px; font-size: 0.9em; @@ -24,11 +24,6 @@ pre { overflow-x: auto; } -pre code { - background: transparent; - padding: 0; -} - footer { margin-top: 3rem; padding-top: 1rem; @@ -41,8 +36,3 @@ footer a { color: #555; text-decoration: none; } - -footer a:hover { - color: #000; - text-decoration: underline; -}