From 29adb3b51d1fd11c4ed24df68a24b766dde92e7e Mon Sep 17 00:00:00 2001 From: Roberto Bissanti Date: Tue, 7 Jul 2026 17:42:32 +0200 Subject: [PATCH 01/27] Migrate Sparkle 1.x to 2.x, bump deployment target to macOS 12 - Podfile: use_frameworks!, Sparkle ~> 2.9, MASPreferences ~> 1.4, PAPreferences ~> 0.5 - MPMainController.m: SUUpdater -> Sparkle.h, SPUUpdaterDelegate - MainMenu.xib: SUUpdater -> SPUStandardUpdaterController - project.pbxproj: MACOSX_DEPLOYMENT_TARGET 10.8 -> 12.0 - MacDown-Info.plist: note for EdDSA key migration Part of Gatekeeper/notarization compliance (Sparkle 1.x is incompatible with hardened runtime + notarization). --- MacDown.xcodeproj/project.pbxproj | 12 ++++++------ MacDown/Code/Application/MPMainController.m | 6 +++--- MacDown/Localization/Base.lproj/MainMenu.xib | 4 ++-- MacDown/MacDown-Info.plist | 6 ++++++ Podfile | 18 ++++++++++++------ 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/MacDown.xcodeproj/project.pbxproj b/MacDown.xcodeproj/project.pbxproj index 81f315a6e..62f6ac8e4 100644 --- a/MacDown.xcodeproj/project.pbxproj +++ b/MacDown.xcodeproj/project.pbxproj @@ -1699,7 +1699,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 12.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -1744,7 +1744,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 12.0; SDKROOT = macosx; }; name = Release; @@ -1759,7 +1759,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MacDown/Code/MacDown-Prefix.pch"; INFOPLIST_FILE = "MacDown/MacDown-Info.plist"; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 12.0; PRODUCT_BUNDLE_IDENTIFIER = "com.uranusjr.${PRODUCT_NAME:rfc1034identifier:lower}-debug"; PRODUCT_NAME = MacDown; SDKROOT = macosx; @@ -1777,7 +1777,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MacDown/Code/MacDown-Prefix.pch"; INFOPLIST_FILE = "MacDown/MacDown-Info.plist"; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 12.0; PRODUCT_BUNDLE_IDENTIFIER = "com.uranusjr.${PRODUCT_NAME:rfc1034identifier:lower}"; PRODUCT_NAME = MacDown; SDKROOT = macosx; @@ -1807,7 +1807,7 @@ "$(inherited)", "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/MacDown-ggdtxvuybojeqbhjydkhilziizrv/Build/Products/Debug", ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 12.0; PRODUCT_BUNDLE_IDENTIFIER = "com.uranusjr.${PRODUCT_NAME:rfc1034identifier:lower}"; PRODUCT_NAME = MacDownTests; TEST_HOST = "$(BUNDLE_LOADER)"; @@ -1833,7 +1833,7 @@ "$(inherited)", "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/MacDown-ggdtxvuybojeqbhjydkhilziizrv/Build/Products/Debug", ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 12.0; PRODUCT_BUNDLE_IDENTIFIER = "com.uranusjr.${PRODUCT_NAME:rfc1034identifier:lower}"; PRODUCT_NAME = MacDownTests; TEST_HOST = "$(BUNDLE_LOADER)"; diff --git a/MacDown/Code/Application/MPMainController.m b/MacDown/Code/Application/MPMainController.m index 5a46edce0..f3e61eea1 100644 --- a/MacDown/Code/Application/MPMainController.m +++ b/MacDown/Code/Application/MPMainController.m @@ -8,7 +8,7 @@ #import "MPMainController.h" #import -#import +#import #import "MPGlobals.h" #import "MPUtilities.h" #import "NSDocumentController+Document.h" @@ -260,9 +260,9 @@ - (void)applicationDidBecomeActive:(NSNotification *)notification } -#pragma mark - SUUpdaterDelegate +#pragma mark - SPUUpdaterDelegate -- (NSString *)feedURLStringForUpdater:(SUUpdater *)updater +- (NSString *)feedURLStringForUpdater:(SPUUpdater *)updater { if (self.preferences.updateIncludesPreReleases) return [NSBundle mainBundle].infoDictionary[@"SUBetaFeedURL"]; diff --git a/MacDown/Localization/Base.lproj/MainMenu.xib b/MacDown/Localization/Base.lproj/MainMenu.xib index ad3ef6c3e..2f5c07fa9 100644 --- a/MacDown/Localization/Base.lproj/MainMenu.xib +++ b/MacDown/Localization/Base.lproj/MainMenu.xib @@ -624,9 +624,9 @@ DQ - + - + diff --git a/MacDown/MacDown-Info.plist b/MacDown/MacDown-Info.plist index 90ff8b8f1..25de9717c 100644 --- a/MacDown/MacDown-Info.plist +++ b/MacDown/MacDown-Info.plist @@ -134,6 +134,12 @@ https://macdown.uranusjr.com/sparkle/macdown/testing/appcast.xml SUFeedURL https://macdown.uranusjr.com/sparkle/macdown/stable/appcast.xml + SUPublicDSAKeyFile dsa_pub.pem UTExportedTypeDeclarations diff --git a/Podfile b/Podfile index 6c5288f69..c2a82dacc 100644 --- a/Podfile +++ b/Podfile @@ -1,4 +1,4 @@ -platform :osx, "10.8" +platform :osx, "12.0" source 'https://github.com/MacDownApp/cocoapods-specs.git' # Patched libraries. source 'https://cdn.cocoapods.org/' @@ -8,20 +8,26 @@ project 'MacDown.xcodeproj' inhibit_all_warnings! target "MacDown" do + use_frameworks! + pod 'handlebars-objc', '~> 1.4' pod 'hoedown', '~> 3.0.7', :inhibit_warnings => false pod 'JJPluralForm', '~> 2.1' pod 'LibYAML', '~> 0.1' pod 'M13OrderedDictionary', '~> 1.1' - pod 'MASPreferences', '~> 1.3' - pod 'Sparkle', '~> 1.18', :inhibit_warnings => false + pod 'MASPreferences', '~> 1.4' + + # Sparkle 2.x: EdDSA signing, XPC-based installer, hardened-runtime friendly. + # Requires use_frameworks! (above) and a source-level migration away from + # SUUpdater to SPUStandardUpdaterController (see MainMenu.xib / MPMainController.m). + pod 'Sparkle', '~> 2.9', :inhibit_warnings => false - # Locked on 0.4.x until we drop 10.8. - pod 'PAPreferences', '~> 0.4' + # No longer locked to 10.8; version bumped as part of dependency modernization. + pod 'PAPreferences', '~> 0.5' end target "MacDownTests" do - pod 'PAPreferences', '~> 0.4' + pod 'PAPreferences', '~> 0.5' end target "macdown-cmd" do From f093cd4143af44074a67eb052c182cf8308ba03d Mon Sep 17 00:00:00 2001 From: Roberto Bissanti Date: Tue, 7 Jul 2026 21:07:08 +0200 Subject: [PATCH 02/27] Fix build tooling: path quoting, node-sass, stale lockfile - Tools/update_build_number.sh: quote $(pwd -P) so it survives paths containing spaces (pre-existing bug, unrelated to the rest of this pass). - Tools/GitHub-style-generator: migrate node-sass (deprecated, native bindings) to sass (Dart Sass); update Makefile flags accordingly. - Gemfile: drop the dead travis gem, bump the cocoapods constraint. - Delete the stale Gemfile.lock (pinned Bundler 1.17.3 / CocoaPods 1.10.1, both incompatible with current Ruby); regenerate with bundle install. - Podfile.lock: sync with MASPreferences 1.4.1 / Sparkle 2.9.4. --- Gemfile | 3 +- Gemfile.lock | 128 -- Podfile.lock | 18 +- Tools/GitHub-style-generator/Makefile | 4 +- .../GitHub-style-generator/package-lock.json | 1821 ++++------------- Tools/GitHub-style-generator/package.json | 2 +- Tools/update_build_number.sh | 2 +- 7 files changed, 431 insertions(+), 1547 deletions(-) delete mode 100644 Gemfile.lock diff --git a/Gemfile b/Gemfile index a1ed95a4f..a658bb793 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,3 @@ source 'https://rubygems.org' -gem 'cocoapods', '~> 1.10' -gem 'travis', '~> 1.10' +gem 'cocoapods', '~> 1.15' diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index d6b486d33..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,128 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.3) - activesupport (5.2.4.4) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - algoliasearch (1.27.5) - httpclient (~> 2.8, >= 2.8.3) - json (>= 1.5.1) - atomos (0.1.3) - claide (1.0.3) - cocoapods (1.10.1) - addressable (~> 2.6) - claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.10.1) - cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.4.0, < 2.0) - cocoapods-plugins (>= 1.0.0, < 2.0) - cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.4.0, < 2.0) - cocoapods-try (>= 1.1.0, < 2.0) - colored2 (~> 3.1) - escape (~> 0.0.4) - fourflusher (>= 2.3.0, < 3.0) - gh_inspector (~> 1.0) - molinillo (~> 0.6.6) - nap (~> 1.0) - ruby-macho (~> 1.4) - xcodeproj (>= 1.19.0, < 2.0) - cocoapods-core (1.10.1) - activesupport (> 5.0, < 6) - addressable (~> 2.6) - algoliasearch (~> 1.0) - concurrent-ruby (~> 1.1) - fuzzy_match (~> 2.0.4) - nap (~> 1.0) - netrc (~> 0.11) - public_suffix - typhoeus (~> 1.0) - cocoapods-deintegrate (1.0.4) - cocoapods-downloader (1.4.0) - cocoapods-plugins (1.0.0) - nap - cocoapods-search (1.0.0) - cocoapods-trunk (1.5.0) - nap (>= 0.8, < 2.0) - netrc (~> 0.11) - cocoapods-try (1.2.0) - colored2 (3.1.2) - concurrent-ruby (1.1.7) - escape (0.0.4) - ethon (0.12.0) - ffi (>= 1.3.0) - faraday (1.1.0) - multipart-post (>= 1.2, < 3) - ruby2_keywords - faraday_middleware (1.0.0) - faraday (~> 1.0) - ffi (1.14.2) - fourflusher (2.3.1) - fuzzy_match (2.0.4) - gh (0.18.0) - activesupport (~> 5.0) - addressable (~> 2.4) - faraday (~> 1.0) - faraday_middleware (~> 1.0) - multi_json (~> 1.0) - net-http-persistent (~> 2.9) - net-http-pipeline - gh_inspector (1.1.3) - highline (2.0.3) - httpclient (2.8.3) - i18n (1.8.7) - concurrent-ruby (~> 1.0) - json (2.5.1) - json_pure (2.3.1) - launchy (2.4.3) - addressable (~> 2.3) - minitest (5.14.3) - molinillo (0.6.6) - multi_json (1.15.0) - multipart-post (2.1.1) - nanaimo (0.3.0) - nap (1.1.0) - net-http-persistent (2.9.4) - net-http-pipeline (1.0.1) - netrc (0.11.0) - public_suffix (4.0.6) - pusher-client (0.6.2) - json - websocket (~> 1.0) - ruby-macho (1.4.0) - ruby2_keywords (0.0.2) - thread_safe (0.3.6) - travis (1.10.0) - faraday (~> 1.0) - faraday_middleware (~> 1.0) - gh (~> 0.13) - highline (~> 2.0) - json_pure (~> 2.3) - launchy (~> 2.1, < 2.5.0) - pusher-client (~> 0.4) - typhoeus (1.4.0) - ethon (>= 0.9.0) - tzinfo (1.2.9) - thread_safe (~> 0.1) - websocket (1.2.8) - xcodeproj (1.19.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.3.0) - -PLATFORMS - ruby - -DEPENDENCIES - cocoapods (~> 1.10) - travis (~> 1.10) - -BUNDLED WITH - 1.17.3 diff --git a/Podfile.lock b/Podfile.lock index b90fe24af..8ab560347 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -7,9 +7,9 @@ PODS: - JJPluralForm (2.1) - LibYAML (0.1.4) - M13OrderedDictionary (1.1.0) - - MASPreferences (1.3) + - MASPreferences (1.4.1) - PAPreferences (0.5) - - Sparkle (1.18.1) + - Sparkle (2.9.4) DEPENDENCIES: - GBCli (~> 1.1) @@ -18,9 +18,9 @@ DEPENDENCIES: - JJPluralForm (~> 2.1) - LibYAML (~> 0.1) - M13OrderedDictionary (~> 1.1) - - MASPreferences (~> 1.3) - - PAPreferences (~> 0.4) - - Sparkle (~> 1.18) + - MASPreferences (~> 1.4) + - PAPreferences (~> 0.5) + - Sparkle (~> 2.9) SPEC REPOS: https://github.com/MacDownApp/cocoapods-specs.git: @@ -42,10 +42,10 @@ SPEC CHECKSUMS: JJPluralForm: 9a6235813990a33a63fb3eff457eb2c633af6acd LibYAML: dee5c31dcab47b168425083c2b0a6baeacdf499e M13OrderedDictionary: 6e157fe9c82aa6b3cd7198f5c5c30c7a6834c4a6 - MASPreferences: c08b8622dd17b47da87669e741efd7c92e970e8c + MASPreferences: 1ba2deb14086792857af44d22846fc4aae477fd9 PAPreferences: 9f0ffb1e67174a0df001af9d3320166ceb9ee6f5 - Sparkle: 06ea33170007c5937ee54da481b4481af98fac79 + Sparkle: 855a03e2323e88e399729d9f8fc80b080a4946b3 -PODFILE CHECKSUM: e9356b9a2ceafda7889ba385de6e9f2d8b06cba0 +PODFILE CHECKSUM: 7aebcb74266e23016e510bdec42d7c617b60a67b -COCOAPODS: 1.8.4 +COCOAPODS: 1.17.0 diff --git a/Tools/GitHub-style-generator/Makefile b/Tools/GitHub-style-generator/Makefile index f2a8016d2..4ecca6d11 100644 --- a/Tools/GitHub-style-generator/Makefile +++ b/Tools/GitHub-style-generator/Makefile @@ -1,11 +1,11 @@ -NODE_SASS = node_modules/.bin/node-sass +SASS = node_modules/.bin/sass target = ../../MacDown/Resources/Styles/GitHub-2020.css all: $(target) $(target): index.sass package-lock.json - $(NODE_SASS) --include-path node_modules $< | sed s/.markdown-body/body/ > $@ + $(SASS) --load-path node_modules --no-source-map $< | sed s/.markdown-body/body/ > $@ clean: $(RM) $(target) diff --git a/Tools/GitHub-style-generator/package-lock.json b/Tools/GitHub-style-generator/package-lock.json index b3f5742f3..ebed0c07e 100644 --- a/Tools/GitHub-style-generator/package-lock.json +++ b/Tools/GitHub-style-generator/package-lock.json @@ -1,1446 +1,459 @@ { "name": "github-style-generator", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "balanced-match": { + "packages": { + "": { + "name": "github-style-generator", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } + "primer-markdown": "^4.0.0", + "sass": "^1.80.0" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "requires": { - "array-find-index": "^1.0.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "env-paths": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "requires": { - "globule": "^1.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globule": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", - "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "requires": { - "repeating": "^2.0.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-core-module": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", - "requires": { - "has": "^1.0.3" - } - }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "js-base64": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", - "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - } - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - } - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "requires": { - "yallist": "^4.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" - }, - "node-gyp": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", - "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.3", - "nopt": "^5.0.0", - "npmlog": "^4.1.2", - "request": "^2.88.2", - "rimraf": "^3.0.2", - "semver": "^7.3.2", - "tar": "^6.0.2", - "which": "^2.0.2" + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" }, - "dependencies": { - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - } - } - }, - "node-sass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-5.0.0.tgz", - "integrity": "sha512-opNgmlu83ZCF792U281Ry7tak9IbVC+AKnXGovcQ8LG8wFaJv6cLnRlc6DIHlmNxWEexB5bZxi9SZ9JyUuOYjw==", - "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^7.0.3", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "lodash": "^4.17.15", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.13.2", - "node-gyp": "^7.1.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "2.2.5", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "nopt": { + "node_modules/chokidar": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "requires": { - "pinkie-promise": "^2.0.0" + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" } }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "license": "MIT" }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "primer-markdown": { + "node_modules/primer-markdown": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/primer-markdown/-/primer-markdown-4.0.0.tgz", "integrity": "sha512-WPDwlV8tTbyQNnVcNcOfoknub5W02qKBVdKHdwz9wGeXkLJxOGhtkatrzH0P03QqmVC6MGFknlNJFjaJOzvX9A==", - "requires": { + "dependencies": { "primer-support": "5.0.0" } }, - "primer-support": { + "node_modules/primer-support": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/primer-support/-/primer-support-5.0.0.tgz", "integrity": "sha512-inUxVSsGirn5IkPxBhFsMBgm8ZHyfOUmOWyDCN8cBXtbaLiCIAjHsPI46yS1zrWxnn0J2kvq8haomkrlHGF08g==" }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "requires": { - "is-finite": "^1.0.0" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "resolve": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", - "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", - "requires": { - "is-core-module": "^2.0.0", - "path-parse": "^1.0.6" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sass-graph": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", - "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", - "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^13.3.2" - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": ">=0.0.4" - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", - "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "requires": { - "readable-stream": "^2.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "requires": { - "get-stdin": "^4.0.1" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "tar": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", - "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - } - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" - }, - "true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "requires": { - "glob": "^7.1.2" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, + "node_modules/sass": { + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", + "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } } } diff --git a/Tools/GitHub-style-generator/package.json b/Tools/GitHub-style-generator/package.json index eea0e8fa6..38dfb0908 100644 --- a/Tools/GitHub-style-generator/package.json +++ b/Tools/GitHub-style-generator/package.json @@ -9,7 +9,7 @@ "author": "Franklin Yu", "license": "MIT", "dependencies": { - "node-sass": "^5.0.0", + "sass": "^1.80.0", "primer-markdown": "^4.0.0" } } diff --git a/Tools/update_build_number.sh b/Tools/update_build_number.sh index cdcc3cfd3..4558bfe27 100644 --- a/Tools/update_build_number.sh +++ b/Tools/update_build_number.sh @@ -17,7 +17,7 @@ set -o errexit set -o nounset pushd `dirname $0` > /dev/null -source $(pwd -P)/utils.sh +source "$(pwd -P)"/utils.sh popd > /dev/null BUILD_VERSION=$(get_build_version) From 525aace8122813a103959f2b0dd1a0e19f3f3648 Mon Sep 17 00:00:00 2001 From: Roberto Bissanti Date: Tue, 7 Jul 2026 21:07:17 +0200 Subject: [PATCH 03/27] Migrate Markdown preview from WebView to WKWebView Full replacement of the deprecated, legacy WebKit WebView with WKWebView for the preview pane: navigation/policy delegates, JS<->native messaging via WKScriptMessageHandler (replacing the WebScripting bridge), scroll sync, zoom, MathJax completion signaling, printing, and word counting. Also fixes two regressions this migration itself introduced along the way: - MathJax not rendering: the script tag pointed at the bundled MathJax.js, which is only the bootstrap loader (the config/jax/extensions/fonts it fetches relative to its own script URL at runtime were never vendored locally). Reverted to loading it from the CDN. - Preview HTML is now loaded via -loadFileURL:allowingReadAccessToURL: from a hidden sidecar file next to the document, instead of -loadHTMLString:baseURL:, since WKWebView restricts file:// sub-resource loads to the loaded page's own directory (this broke custom user styles and images referenced from outside the document's folder). Local stylesheets/scripts are now embedded inline in the HTML for the same reason. Print crash fixed by sizing the print operation's view, which WKWebView's printOperationWithPrintInfo: otherwise leaves at zero. See CHANGELOG.md for the full list. --- .gitignore | 4 + MacDown.xcodeproj/project.pbxproj | 57 +- MacDown/Code/Application/MPMainController.m | 19 +- MacDown/Code/Document/MPDocument.m | 677 +++++++++++------- MacDown/Code/Document/MPRenderer.m | 40 +- MacDown/Code/Extension/DOMNode+Text.h | 25 - MacDown/Code/Extension/DOMNode+Text.m | 133 ---- .../Extension/WebView+WebViewPrivateHeaders.h | 26 - MacDown/Code/Utility/MPMathJaxListener.h | 16 - MacDown/Code/Utility/MPMathJaxListener.m | 56 -- .../Localization/Base.lproj/MPDocument.xib | 18 +- MacDown/Resources/MathJax/init.js | 11 +- 12 files changed, 476 insertions(+), 606 deletions(-) delete mode 100644 MacDown/Code/Extension/DOMNode+Text.h delete mode 100644 MacDown/Code/Extension/DOMNode+Text.m delete mode 100644 MacDown/Code/Extension/WebView+WebViewPrivateHeaders.h delete mode 100644 MacDown/Code/Utility/MPMathJaxListener.h delete mode 100644 MacDown/Code/Utility/MPMathJaxListener.m diff --git a/.gitignore b/.gitignore index 8ec458787..e03d919c5 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,10 @@ .DS_Store +# MacDown writes a hidden sidecar HTML file next to whatever document is +# open, to render the live preview (see -[MPDocument renderer:didProduceHTMLOutput:]). +.macdown-preview-*.html + # c.f. http://www.westwind.com/reference/os-x/invisibles.html .Trashes diff --git a/MacDown.xcodeproj/project.pbxproj b/MacDown.xcodeproj/project.pbxproj index 62f6ac8e4..460414ca8 100644 --- a/MacDown.xcodeproj/project.pbxproj +++ b/MacDown.xcodeproj/project.pbxproj @@ -27,11 +27,9 @@ 1F0D9DB3194AC9EE008E1856 /* MPMarkdownPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1F0D9D89194AC83C008E1856 /* MPMarkdownPreferencesViewController.xib */; }; 1F0D9DB4194AC9EE008E1856 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1F0D9D81194AC83C008E1856 /* MainMenu.xib */; }; 1F0F3BFC195F75F200FC0B3B /* MathJax in Resources */ = {isa = PBXBuildFile; fileRef = 1F0F3BFB195F75F200FC0B3B /* MathJax */; }; - 1F23A92119928E650052DB78 /* MPMathJaxListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F23A92019928E650052DB78 /* MPMathJaxListener.m */; }; 1F25E1C31A50F4F10029371D /* MPColorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F25E1C21A50F4F10029371D /* MPColorTests.m */; }; 1F2649B01A7406DB00EF6AF3 /* NSDocumentController+Document.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F2649AF1A7406DB00EF6AF3 /* NSDocumentController+Document.m */; }; 1F27896B1973BEB100EE696A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1F2789691973BEB100EE696A /* Localizable.strings */; }; - 1F3386E61A6B999600FC88C4 /* DOMNode+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F3386E51A6B999600FC88C4 /* DOMNode+Text.m */; }; 1F33F2A11A3B4B660001C849 /* MPDocumentSplitView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F33F2A01A3B4B660001C849 /* MPDocumentSplitView.m */; }; 1F33F2A41A3B56D20001C849 /* NSColor+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F33F2A31A3B56D20001C849 /* NSColor+HTML.m */; }; 1F3619E61F36DA0F00EDA15A /* MPTerminalPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1F3619E81F36DA0F00EDA15A /* MPTerminalPreferencesViewController.xib */; }; @@ -84,12 +82,12 @@ 1FFEB32B19737D6E00B2254F /* YAMLSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FFEB32A19737D6E00B2254F /* YAMLSerialization.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc -Wno-unused-variable"; }; }; 1FFF301D1948A5320009AF24 /* MPStringLookupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FFF301C1948A5320009AF24 /* MPStringLookupTests.m */; }; 3028039E1FD84EAC0055B0DA /* contribute.md in Resources */ = {isa = PBXBuildFile; fileRef = 3028039D1FD84EAB0055B0DA /* contribute.md */; }; - 770BB49E962A302D0715A6A5 /* libPods-MacDown.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42F926DD38559C7CEDB6E42F /* libPods-MacDown.a */; }; 852D523C1E260A6400BA7162 /* MPTerminalPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 852D523A1E260A6400BA7162 /* MPTerminalPreferencesViewController.m */; }; 85E24E5C1E5019C00056E696 /* MPToolbarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 85E24E5B1E5019C00056E696 /* MPToolbarController.m */; }; 905EF1A9196164CA00FC3CE9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 905EF1A8196164CA00FC3CE9 /* Foundation.framework */; }; 905EF1B7196164F300FC3CE9 /* macdown in Copy Command Line Utility */ = {isa = PBXBuildFile; fileRef = 905EF1A7196164CA00FC3CE9 /* macdown */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; B5C5C1666A506FEA86435D25 /* libPods-macdown-cmd.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 263367245B2D78A42C2F19D7 /* libPods-macdown-cmd.a */; }; + DB54FB458210149167A97C84 /* Pods_MacDown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03863E9859907C759B4C53CD /* Pods_MacDown.framework */; }; EBFE3157737058F5A63699C8 /* libPods-MacDownTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C949FDE45493AE77DAD9BF4 /* libPods-MacDownTests.a */; }; /* End PBXBuildFile section */ @@ -149,6 +147,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 03863E9859907C759B4C53CD /* Pods_MacDown.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MacDown.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0D0EC0DE9FEA22F962083921 /* Pods-MacDownTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MacDownTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MacDownTests/Pods-MacDownTests.release.xcconfig"; sourceTree = ""; }; 1A198D1BA59D710201DD3BC8 /* Pods-MacDown.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MacDown.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MacDown/Pods-MacDown.debug.xcconfig"; sourceTree = ""; }; 1F002A21195B3DAE008B8D93 /* MPRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRenderer.h; sourceTree = ""; }; @@ -228,8 +227,6 @@ 1F20EA2A2099A18A00C81510 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "Localization/nb-NO.lproj/MPGeneralPreferencesViewController.strings"; sourceTree = ""; }; 1F20EA2B2099A18A00C81510 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "Localization/nb-NO.lproj/MPMarkdownPreferencesViewController.strings"; sourceTree = ""; }; 1F20EA2C2099A18A00C81510 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "Localization/nb-NO.lproj/MPDocument.strings"; sourceTree = ""; }; - 1F23A91F19928E650052DB78 /* MPMathJaxListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMathJaxListener.h; sourceTree = ""; }; - 1F23A92019928E650052DB78 /* MPMathJaxListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMathJaxListener.m; sourceTree = ""; }; 1F25E1C21A50F4F10029371D /* MPColorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPColorTests.m; sourceTree = ""; }; 1F2649AE1A7406DB00EF6AF3 /* NSDocumentController+Document.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDocumentController+Document.h"; sourceTree = ""; }; 1F2649AF1A7406DB00EF6AF3 /* NSDocumentController+Document.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDocumentController+Document.m"; sourceTree = ""; }; @@ -245,8 +242,6 @@ 1F2ABD051F38113C005FB620 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/InfoPlist.strings; sourceTree = ""; }; 1F2ABD061F38113E005FB620 /* ko-KR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ko-KR"; path = "ko-KR.lproj/InfoPlist.strings"; sourceTree = ""; }; 1F2ABD071F381140005FB620 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; - 1F3386E41A6B999600FC88C4 /* DOMNode+Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DOMNode+Text.h"; sourceTree = ""; }; - 1F3386E51A6B999600FC88C4 /* DOMNode+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "DOMNode+Text.m"; sourceTree = ""; }; 1F33F29F1A3B4B660001C849 /* MPDocumentSplitView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPDocumentSplitView.h; sourceTree = ""; }; 1F33F2A01A3B4B660001C849 /* MPDocumentSplitView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPDocumentSplitView.m; sourceTree = ""; }; 1F33F2A21A3B56D20001C849 /* NSColor+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSColor+HTML.h"; sourceTree = ""; }; @@ -489,8 +484,6 @@ 3028039D1FD84EAB0055B0DA /* contribute.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = contribute.md; path = Resources/contribute.md; sourceTree = ""; }; 39EFCAE04F60154F0C8C5469 /* Pods-MacDown.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MacDown.release.xcconfig"; path = "Pods/Target Support Files/Pods-MacDown/Pods-MacDown.release.xcconfig"; sourceTree = ""; }; 3C949FDE45493AE77DAD9BF4 /* libPods-MacDownTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MacDownTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 3D175F0F1974282400A5EFE8 /* WebView+WebViewPrivateHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WebView+WebViewPrivateHeaders.h"; sourceTree = ""; }; - 42F926DD38559C7CEDB6E42F /* libPods-MacDown.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MacDown.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 4DBAA63927A60EB2150642B3 /* Pods-macdown-cmd.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-macdown-cmd.debug.xcconfig"; path = "Pods/Target Support Files/Pods-macdown-cmd/Pods-macdown-cmd.debug.xcconfig"; sourceTree = ""; }; 7A8EA75FA95818275755F0B6 /* Pods-macdown-cmd.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-macdown-cmd.release.xcconfig"; path = "Pods/Target Support Files/Pods-macdown-cmd/Pods-macdown-cmd.release.xcconfig"; sourceTree = ""; }; 852D52391E260A6400BA7162 /* MPTerminalPreferencesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTerminalPreferencesViewController.h; sourceTree = ""; }; @@ -535,7 +528,7 @@ 1FF1420219A8987500CF8A6A /* JavaScriptCore.framework in Frameworks */, 1FF207631941CF22005B5654 /* WebKit.framework in Frameworks */, 1FA6DE251941CC9E000409FB /* Cocoa.framework in Frameworks */, - 770BB49E962A302D0715A6A5 /* libPods-MacDown.a in Frameworks */, + DB54FB458210149167A97C84 /* Pods_MacDown.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -564,8 +557,6 @@ 1F0D9D5D194AC7CF008E1856 /* Extension */ = { isa = PBXGroup; children = ( - 1F3386E41A6B999600FC88C4 /* DOMNode+Text.h */, - 1F3386E51A6B999600FC88C4 /* DOMNode+Text.m */, 1F33F2A21A3B56D20001C849 /* NSColor+HTML.h */, 1F33F2A31A3B56D20001C849 /* NSColor+HTML.m */, 1F2649AE1A7406DB00EF6AF3 /* NSDocumentController+Document.h */, @@ -582,7 +573,6 @@ 1F0D9D61194AC7CF008E1856 /* NSTextView+Autocomplete.m */, 1F847BC41DCC9DB600A47385 /* NSUserDefaults+Suite.h */, 1F847BC51DCC9DB600A47385 /* NSUserDefaults+Suite.m */, - 3D175F0F1974282400A5EFE8 /* WebView+WebViewPrivateHeaders.h */, 1FBBD4C9194CA35900BB3C88 /* hoedown_html_patch.h */, 1FBBD4CA194CA36500BB3C88 /* hoedown_html_patch.c */, ); @@ -597,8 +587,6 @@ 1F847BC31DCC9DA800A47385 /* MPGlobals.h */, 1F96BD7E1E584A03005E0456 /* MPHomebrewSubprocessController.h */, 1F96BD7F1E584A03005E0456 /* MPHomebrewSubprocessController.m */, - 1F23A91F19928E650052DB78 /* MPMathJaxListener.h */, - 1F23A92019928E650052DB78 /* MPMathJaxListener.m */, 1F5A370E1C868EC0006E63E7 /* MPPlugIn.h */, 1F5A370F1C868EC0006E63E7 /* MPPlugIn.m */, 1F0D9D63194AC7CF008E1856 /* MPUtilities.h */, @@ -778,9 +766,9 @@ 1FA6DE241941CC9E000409FB /* Cocoa.framework */, 905EF1A8196164CA00FC3CE9 /* Foundation.framework */, 1FA6DE261941CC9E000409FB /* Other Frameworks */, - 42F926DD38559C7CEDB6E42F /* libPods-MacDown.a */, 3C949FDE45493AE77DAD9BF4 /* libPods-MacDownTests.a */, 263367245B2D78A42C2F19D7 /* libPods-macdown-cmd.a */, + 03863E9859907C759B4C53CD /* Pods_MacDown.framework */, ); name = Frameworks; sourceTree = ""; @@ -914,7 +902,6 @@ 1F8A82A81952F19B00B6BF69 /* Update Build Number */, 905EF1B6196164E300FC3CE9 /* Copy Command Line Utility */, C5B8C48273E72106885CA8C4 /* [CP] Embed Pods Frameworks */, - 1E02F0A95AAEBCEF65493F5F /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1081,24 +1068,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1E02F0A95AAEBCEF65493F5F /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-MacDown/Pods-MacDown-resources.sh", - "${PODS_ROOT}/MASPreferences/Framework/en.lproj/MASPreferencesWindow.xib", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MASPreferencesWindow.nib", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MacDown/Pods-MacDown-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 1F8A82A81952F19B00B6BF69 /* Update Build Number */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1190,13 +1159,25 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-MacDown/Pods-MacDown-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/JJPluralForm/JJPluralForm.framework", + "${BUILT_PRODUCTS_DIR}/LibYAML/LibYAML.framework", + "${BUILT_PRODUCTS_DIR}/M13OrderedDictionary/M13OrderedDictionary.framework", + "${BUILT_PRODUCTS_DIR}/MASPreferences/MASPreferences.framework", + "${BUILT_PRODUCTS_DIR}/PAPreferences-framework/PAPreferences.framework", "${PODS_ROOT}/Sparkle/Sparkle.framework", - "${PODS_ROOT}/Sparkle/Sparkle.framework.dSYM", + "${BUILT_PRODUCTS_DIR}/handlebars-objc/HBHandlebars.framework", + "${BUILT_PRODUCTS_DIR}/hoedown/hoedown.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JJPluralForm.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LibYAML.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/M13OrderedDictionary.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MASPreferences.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PAPreferences.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework", - "${DWARF_DSYM_FOLDER_PATH}/Sparkle.framework.dSYM", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HBHandlebars.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hoedown.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1234,7 +1215,6 @@ 1F3FC87C1C854E1C000965E1 /* NSPasteboard+Types.m in Sources */, 1F8A835E1953454F00B6BF69 /* HGMarkdownHighlightingStyle.m in Sources */, 1F33F2A11A3B4B660001C849 /* MPDocumentSplitView.m in Sources */, - 1F23A92119928E650052DB78 /* MPMathJaxListener.m in Sources */, 1F2649B01A7406DB00EF6AF3 /* NSDocumentController+Document.m in Sources */, 1F0D9D67194AC7CF008E1856 /* MPUtilities.m in Sources */, 852D523C1E260A6400BA7162 /* MPTerminalPreferencesViewController.m in Sources */, @@ -1260,7 +1240,6 @@ 1F8A83621953461800B6BF69 /* pmh_parser.c in Sources */, 1F8A835D1953454F00B6BF69 /* HGMarkdownHighlighter.m in Sources */, 1F847BC61DCC9DB600A47385 /* NSUserDefaults+Suite.m in Sources */, - 1F3386E61A6B999600FC88C4 /* DOMNode+Text.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MacDown/Code/Application/MPMainController.m b/MacDown/Code/Application/MPMainController.m index f3e61eea1..47a97234c 100644 --- a/MacDown/Code/Application/MPMainController.m +++ b/MacDown/Code/Application/MPMainController.m @@ -102,21 +102,10 @@ @implementation MPMainController - (void)applicationDidFinishLaunching:(NSNotification *)notification { - // Using private API [WebCache setDisabled:YES] to disable WebView's cache - id webCacheClass = (id)NSClassFromString(@"WebCache"); - if (webCacheClass) { -// Ignoring "undeclared selector" warning -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wundeclared-selector" - BOOL setDisabledValue = YES; - NSMethodSignature *signature = [webCacheClass methodSignatureForSelector:@selector(setDisabled:)]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; - invocation.selector = @selector(setDisabled:); - invocation.target = [webCacheClass class]; - [invocation setArgument:&setDisabledValue atIndex:2]; - [invocation invoke]; -#pragma clang diagnostic pop - } + // The legacy WebView-cache-disabling workaround (private WebCache class, + // invoked reflectively) that used to live here is gone along with the + // WKWebView migration: WKWebView doesn't use WebKit1's WebCache at all, + // so the lookup would always resolve to nil and no-op now anyway. [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(openUrlSchemeAppleEvent:withReplyEvent:) diff --git a/MacDown/Code/Document/MPDocument.m b/MacDown/Code/Document/MPDocument.m index acbc77ffe..7fa5dcc97 100644 --- a/MacDown/Code/Document/MPDocument.m +++ b/MacDown/Code/Document/MPDocument.m @@ -19,7 +19,6 @@ #import "NSPasteboard+Types.h" #import "NSString+Lookup.h" #import "NSTextView+Autocomplete.h" -#import "DOMNode+Text.h" #import "MPPreferences.h" #import "MPDocumentSplitView.h" #import "MPEditorView.h" @@ -27,10 +26,7 @@ #import "MPPreferencesViewController.h" #import "MPEditorPreferencesViewController.h" #import "MPExportPanelAccessoryViewController.h" -#import "MPMathJaxListener.h" -#import "WebView+WebViewPrivateHeaders.h" #import "MPToolbarController.h" -#import static NSString * const kMPDefaultAutosaveName = @"Untitled"; @@ -85,20 +81,6 @@ return rectString; } -NS_INLINE NSColor *MPGetWebViewBackgroundColor(WebView *webview) -{ - DOMDocument *doc = webview.mainFrameDocument; - DOMNodeList *nodes = [doc getElementsByTagName:@"body"]; - if (!nodes.length) - return nil; - - id bodyNode = [nodes item:0]; - DOMCSSStyleDeclaration *style = [doc getComputedStyle:bodyNode - pseudoElement:nil]; - return [NSColor colorWithHTMLName:[style backgroundColor]]; -} - - @implementation NSURL (Convert) - (NSString *)absoluteBaseURLString @@ -113,16 +95,6 @@ - (NSString *)absoluteBaseURLString @end -@implementation WebView (Shortcut) - -- (NSScrollView *)enclosingScrollView -{ - return self.mainFrame.frameView.documentView.enclosingScrollView; -} - -@end - - @implementation MPPreferences (Hoedown) - (int)extensionFlags { @@ -172,9 +144,7 @@ - (int)rendererFlags @interface MPDocument () = 101100 - WebEditingDelegate, WebFrameLoadDelegate, WebPolicyDelegate, WebResourceLoadDelegate, -#endif + WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler, MPAutosaving, MPRendererDataSource, MPRendererDelegate> typedef NS_ENUM(NSUInteger, MPWordCountType) { @@ -188,7 +158,12 @@ typedef NS_ENUM(NSUInteger, MPWordCountType) { @property (weak) IBOutlet NSView *editorContainer; @property (unsafe_unretained) IBOutlet MPEditorView *editor; @property (weak) IBOutlet NSLayoutConstraint *editorPaddingBottom; -@property (weak) IBOutlet WebView *preview; +// Instantiated programmatically in -windowControllerDidLoadNib: and swapped +// into the split view in place of a plain NSView placeholder (see +// MPDocument.xib). This lets us attach a WKWebViewConfiguration with script +// message handlers before any content is loaded, which Interface Builder's +// nib-based WKWebView instantiation doesn't allow. +@property (weak) IBOutlet WKWebView *preview; @property (weak) IBOutlet NSPopUpButton *wordCountWidget; @property (strong) IBOutlet MPToolbarController *toolbarController; @property (copy, nonatomic) NSString *autosaveName; @@ -201,6 +176,10 @@ typedef NS_ENUM(NSUInteger, MPWordCountType) { @property BOOL shouldHandleBoundsChange; @property BOOL isPreviewReady; @property (strong) NSURL *currentBaseUrl; +// A per-document, stable-but-unique sidecar HTML file we render the preview +// into (see -renderer:didProduceHTMLOutput:), written next to the source +// document so relative asset paths keep resolving exactly as before. +@property (strong) NSURL *previewTempFileUrl; @property CGFloat lastPreviewScrollTop; @property (nonatomic, readonly) BOOL needsHtml; @property (nonatomic) NSUInteger totalWords; @@ -215,42 +194,20 @@ typedef NS_ENUM(NSUInteger, MPWordCountType) { @property (strong) NSArray *webViewHeaderLocations; @property (strong) NSArray *editorHeaderLocations; @property (nonatomic) BOOL inLiveScroll; +// Cached values fetched asynchronously from the preview's JS context, since +// WKWebView (unlike the legacy WebView) exposes no synchronous DOM/JS access. +@property CGFloat cachedPreviewContentHeight; +@property (strong) NSColor *cachedPreviewBackgroundColor; // Store file content in initializer until nib is loaded. @property (copy) NSString *loadedString; - (void)scaleWebview; - (void)syncScrollers; --(void) updateHeaderLocations; +- (void)updateHeaderLocationsWithCompletion:(void (^)(void))completion; @end -static void (^MPGetPreviewLoadingCompletionHandler(MPDocument *doc))() -{ - __weak MPDocument *weakObj = doc; - return ^{ - WebView *webView = weakObj.preview; - NSWindow *window = webView.window; - @synchronized(window) { - if (window.isFlushWindowDisabled) - [window enableFlushWindow]; - } - [weakObj scaleWebview]; - if (weakObj.preferences.editorSyncScrolling) - { - [weakObj updateHeaderLocations]; - [weakObj syncScrollers]; - } - else - { - NSClipView *contentView = webView.enclosingScrollView.contentView; - NSRect bounds = contentView.bounds; - bounds.origin.y = weakObj.lastPreviewScrollTop; - contentView.bounds = bounds; - } - }; -} - @implementation MPDocument @@ -396,10 +353,39 @@ - (void)windowControllerDidLoadNib:(NSWindowController *)controller } self.editor.postsFrameChangedNotifications = YES; - self.preview.frameLoadDelegate = self; - self.preview.policyDelegate = self; - self.preview.editingDelegate = self; - self.preview.resourceLoadDelegate = self; + + // The preview pane is a plain NSView placeholder in the nib (see + // MPDocument.xib). We create the real WKWebView here instead of via + // Interface Builder so we can attach a WKWebViewConfiguration with + // script message handlers (used for MathJax completion and preview + // scroll tracking) before any content is ever loaded into it. + NSView *previewPlaceholder = (NSView *)self.preview; + WKUserContentController *userContentController = + [[WKUserContentController alloc] init]; + [userContentController addScriptMessageHandler:self name:@"mathJaxListener"]; + [userContentController addScriptMessageHandler:self name:@"previewScroll"]; + NSString *scrollListenerSource = + @"window.addEventListener('scroll', function () {" + @" window.webkit.messageHandlers.previewScroll.postMessage(window.scrollY);" + @"}, { passive: true });"; + WKUserScript *scrollListenerScript = + [[WKUserScript alloc] initWithSource:scrollListenerSource + injectionTime:WKUserScriptInjectionTimeAtDocumentEnd + forMainFrameOnly:YES]; + [userContentController addUserScript:scrollListenerScript]; + + WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; + configuration.userContentController = userContentController; + + WKWebView *webView = [[WKWebView alloc] initWithFrame:previewPlaceholder.frame + configuration:configuration]; + webView.translatesAutoresizingMaskIntoConstraints = + previewPlaceholder.translatesAutoresizingMaskIntoConstraints; + webView.autoresizingMask = previewPlaceholder.autoresizingMask; + webView.navigationDelegate = self; + webView.UIDelegate = self; + [self.splitView replaceSubview:previewPlaceholder with:webView]; + self.preview = webView; NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(editorTextDidChange:) @@ -422,12 +408,10 @@ - (void)windowControllerDidLoadNib:(NSWindowController *)controller [center addObserver:self selector:@selector(didEndLiveScroll:) name:NSScrollViewDidEndLiveScrollNotification object:self.editor.enclosingScrollView]; - if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_9) - { - [center addObserver:self selector:@selector(previewDidLiveScroll:) - name:NSScrollViewDidEndLiveScrollNotification - object:self.preview.enclosingScrollView]; - } + // Preview scroll position is now tracked via the "previewScroll" script + // message handler (see -userContentController:didReceiveScriptMessage:) + // instead of an NSScrollView notification, since WKWebView exposes no + // public NSScrollView on macOS. self.needsToUnregister = YES; @@ -483,8 +467,22 @@ - (void)close self.highlighter.targetTextView = nil; self.highlighter = nil; self.renderer = nil; - self.preview.frameLoadDelegate = nil; - self.preview.policyDelegate = nil; + self.preview.navigationDelegate = nil; + self.preview.UIDelegate = nil; + // WKUserContentController retains its script message handlers + // strongly, which would otherwise keep this document alive forever + // via preview -> configuration -> userContentController -> self. + [self.preview.configuration.userContentController + removeScriptMessageHandlerForName:@"mathJaxListener"]; + [self.preview.configuration.userContentController + removeScriptMessageHandlerForName:@"previewScroll"]; + + if (self.previewTempFileUrl) + { + [[NSFileManager defaultManager] + removeItemAtURL:self.previewTempFileUrl error:NULL]; + self.previewTempFileUrl = nil; + } [[NSNotificationCenter defaultCenter] removeObserver:self]; @@ -625,8 +623,21 @@ - (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)printSettings NSPrintInfo *info = [self.printInfo copy]; [info.dictionary addEntriesFromDictionary:printSettings]; - WebFrameView *view = self.preview.mainFrame.frameView; - NSPrintOperation *op = [view printOperationWithPrintInfo:info]; + // WKWebView.printOperationWithPrintInfo: is public API since macOS 11; + // it's the only way to plug a web view into NSDocument's synchronous + // printOperationWithSettings:error: contract (the async replacement, + // -createPDFWithConfiguration:completionHandler:, doesn't fit this call + // site). Known limitation carried over from upstream WebKit: content + // scrolled out of view may not print. + // + // Undocumented but load-bearing gotcha (see + // https://developer.apple.com/forums/thread/705138): the returned + // operation's view has no frame set, and running it as-is crashes. + // It must be explicitly sized to the paper before NSDocument's own + // printing machinery (our caller's superclass) runs it. + NSPrintOperation *op = [self.preview printOperationWithPrintInfo:info]; + op.view.frame = NSMakeRect(0, 0, + info.paperSize.width, info.paperSize.height); return op; } @@ -830,61 +841,50 @@ - (BOOL)textViewShouldMoveToLeftEndOfLine:(NSTextView *)textView } -#pragma mark - WebResourceLoadDelegate +#pragma mark - Preview load completion -- (NSURLRequest *)webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource +// Shared tail-end of a preview reload: rescale for the editor's font size, +// and either re-sync scroll position with the editor or restore the +// previously-remembered preview scroll offset. Called directly from +// -webView:didFinishNavigation: when MathJax is off, or from the +// "mathJaxListener" script message once MathJax has finished typesetting +// (equation rendering can noticeably shift the layout, so we wait for it). +- (void)previewDidFinishLoadingContent { - - if ([[request.URL lastPathComponent] isEqualToString:@"MathJax.js"]) + [self scaleWebview]; + if (self.preferences.editorSyncScrolling) { - NSURLComponents *origComps = [NSURLComponents componentsWithURL:[request URL] resolvingAgainstBaseURL:YES]; - NSURLComponents *updatedComps = [NSURLComponents componentsWithURL:[[NSBundle mainBundle] URLForResource:@"MathJax" withExtension:@"js" subdirectory:@"MathJax"] resolvingAgainstBaseURL:NO]; - [updatedComps setQueryItems:[origComps queryItems]]; - - request = [NSURLRequest requestWithURL:[updatedComps URL]]; - } - - return request; -} - -#pragma mark - WebFrameLoadDelegate - -- (void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame -{ - NSWindow *window = sender.window; - @synchronized(window) { - if (!window.isFlushWindowDisabled) - [window disableFlushWindow]; + __weak typeof(self) weakSelf = self; + [self updateHeaderLocationsWithCompletion:^{ + [weakSelf syncScrollers]; + }]; } - - // If MathJax is off, the on-completion callback will be invoked directly - // when loading is done (in -webView:didFinishLoadForFrame:). - if (self.preferences.htmlMathJax) + else { - MPMathJaxListener *listener = [[MPMathJaxListener alloc] init]; - [listener addCallback:MPGetPreviewLoadingCompletionHandler(self) - forKey:@"End"]; - [sender.windowScriptObject setValue:listener forKey:@"MathJaxListener"]; + NSString *js = [NSString stringWithFormat: + @"window.scrollTo(0, %f);", self.lastPreviewScrollTop]; + [self.preview evaluateJavaScript:js completionHandler:nil]; } + [self updatePreviewBackgroundColorCache]; } -- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame +- (void)handlePreviewLoadFinishOrFailure { - // If MathJax is on, the on-completion callback will be invoked by the - // JavaScript handler injected in -webView:didCommitLoadForFrame:. if (!self.preferences.htmlMathJax) { - id callback = MPGetPreviewLoadingCompletionHandler(self); - NSOperationQueue *queue = [NSOperationQueue mainQueue]; - [queue addOperationWithBlock:callback]; + __weak typeof(self) weakSelf = self; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [weakSelf previewDidFinishLoadingContent]; + }]; } + // If MathJax is on, previewDidFinishLoadingContent is instead invoked by + // the "mathJaxListener" script message once MathJax finishes. self.isPreviewReady = YES; - // Update word count if (self.preferences.editorShowWordCount) [self updateWordCount]; - + self.alreadyRenderingInWeb = NO; if (self.renderToWebPending) @@ -893,78 +893,77 @@ - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame self.renderToWebPending = NO; } -- (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error - forFrame:(WebFrame *)frame -{ - [self webView:sender didFinishLoadForFrame:frame]; - - self.alreadyRenderingInWeb = NO; - if (self.renderToWebPending) - [self.renderer parseAndRenderNow]; +#pragma mark - WKNavigationDelegate - self.renderToWebPending = NO; +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation +{ + [self handlePreviewLoadFinishOrFailure]; } +- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation + withError:(NSError *)error +{ + [self handlePreviewLoadFinishOrFailure]; +} -#pragma mark - WebPolicyDelegate +- (void)webView:(WKWebView *)webView + didFailProvisionalNavigation:(WKNavigation *)navigation + withError:(NSError *)error +{ + [self handlePreviewLoadFinishOrFailure]; +} -- (void)webView:(WebView *)webView - decidePolicyForNavigationAction:(NSDictionary *)information - request:(NSURLRequest *)request frame:(WebFrame *)frame - decisionListener:(id)listener +- (void)webView:(WKWebView *)webView + decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction + decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - switch ([information[WebActionNavigationTypeKey] integerValue]) + NSURL *url = navigationAction.request.URL; + if (navigationAction.navigationType == WKNavigationTypeLinkActivated) { - case WebNavigationTypeLinkClicked: - // If the target is exactly as the current one, ignore. - if ([self.currentBaseUrl isEqual:request.URL]) - { - [listener ignore]; - return; - } - // If this is a different page, intercept and handle ourselves. - else if (![self isCurrentBaseUrl:request.URL]) - { - [listener ignore]; - [self openOrCreateFileForUrl:request.URL]; - return; - } - // Otherwise this is somewhere else on the same page. Jump there. - break; - default: - break; + // If the target is exactly as the current one, ignore. + if ([self.currentBaseUrl isEqual:url]) + { + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + // If this is a different page, intercept and handle ourselves. + if (![self isCurrentBaseUrl:url]) + { + decisionHandler(WKNavigationActionPolicyCancel); + [self openOrCreateFileForUrl:url]; + return; + } + // Otherwise this is somewhere else on the same page. Let it jump there. } - [listener use]; + decisionHandler(WKNavigationActionPolicyAllow); } -#pragma mark - WebEditingDelegate +#pragma mark - WKScriptMessageHandler -- (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)selector +- (void)userContentController:(WKUserContentController *)userContentController + didReceiveScriptMessage:(WKScriptMessage *)message { - if (selector == @selector(copy:)) + if ([message.name isEqualToString:@"mathJaxListener"]) { - NSString *html = webView.selectedDOMRange.markupString; - - // Inject the HTML content later so that it doesn't get cleared during - // the native copy operation. - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - NSPasteboard *pb = [NSPasteboard generalPasteboard]; - if (![pb stringForType:@"public.html"]) - [pb setString:html forType:@"public.html"]; - }]; + [self previewDidFinishLoadingContent]; + } + else if ([message.name isEqualToString:@"previewScroll"]) + { + if ([message.body isKindOfClass:[NSNumber class]]) + self.lastPreviewScrollTop = [(NSNumber *)message.body doubleValue]; } - return NO; } -#pragma mark - WebUIDelegate -- (NSUInteger)webView:(WebView *)webView - dragDestinationActionMaskForDraggingInfo:(id)info -{ - return WebDragDestinationActionNone; -} +#pragma mark - WKUIDelegate + +// Note: WKUIDelegate has no equivalent to the legacy WebUIDelegate's +// -webView:dragDestinationActionMaskForDraggingInfo:, which disabled drag +// destination actions on the preview. WKWebView's default drag handling is +// used instead; verify dropping a file onto the preview pane behaves +// reasonably. #pragma mark - MPRendererDataSource @@ -1069,51 +1068,83 @@ - (void)renderer:(MPRenderer *)renderer didProduceHTMLOutput:(NSString *)html self.manualRender = self.preferences.markdownManualRender; -#if 0 - // Unfortunately this DOM-replacing causes a lot of problems... - // 1. MathJax needs to be triggered. - // 2. Prism rendering is lost. - // 3. Potentially more. - // Essentially all JavaScript needs to be run again after we replace - // the DOM. I have no idea how many more problems there are, so we'll have - // to back off from the path for now... :( - - // If we're working on the same document, try not to reload. - if (self.isPreviewReady && [self.currentBaseUrl isEqualTo:baseUrl]) + // A previous in-place DOM-patching optimization (replace just the + // tag's innerHTML instead of reloading) was abandoned upstream + // long before this migration: it broke MathJax and Prism re-rendering, + // since essentially all page JavaScript needs to run again after a DOM + // replacement. A full reload is simpler and is what was actually in + // effect (the optimized path was already dead code, disabled via + // "#if 0"). + // + // We used to call -loadHTMLString:baseURL:, which exists identically on + // WKWebView -- but unlike the legacy WebView, WKWebView restricts + // file:// sub-resource loads under it to baseURL's own directory tree. + // Local stylesheet/script assets are now embedded inline by MPRenderer + // to route around that (see -[MPRenderer render]), but that trick + // doesn't apply to references the user's own Markdown points at, + // which can be an absolute path anywhere on disk (e.g. ~/Downloads) -- + // those still need a real file:// load to succeed. So instead we write + // the rendered HTML to a sidecar file next to the source document (same + // directory, so relative asset paths still resolve exactly as before) + // and load that via -loadFileURL:allowingReadAccessToURL:, which lets + // us explicitly grant broader read access. We grant access to "/" + // rather than trying to enumerate every directory a Markdown file might + // reference an image from (Desktop, Downloads, a Photos export, a + // network volume, ...); this merely restores WebView1's old behavior, + // which never sandboxed local file access in the first place. + NSFileManager *manager = [NSFileManager defaultManager]; + BOOL baseUrlIsDirectory = NO; + [manager fileExistsAtPath:baseUrl.path isDirectory:&baseUrlIsDirectory]; + NSURL *baseDirectory = + baseUrlIsDirectory ? baseUrl : baseUrl.URLByDeletingLastPathComponent; + + // If the document moved (Save As, rename) since we last created the + // sidecar file, its directory no longer matches baseDirectory -- drop + // the stale one so relative asset paths keep resolving against the + // document's *current* location instead of a stale unrelated folder. + if (self.previewTempFileUrl && + ![self.previewTempFileUrl.URLByDeletingLastPathComponent + isEqual:baseDirectory]) { - // HACK: Ideally we should only inject the parts that changed, and only - // get the parts we need. For now we only get a complete HTML codument, - // and rely on regex to get the parts we want in the DOM. - - // Use the existing tree if available, and replace the content. - DOMDocument *doc = self.preview.mainFrame.DOMDocument; - DOMNodeList *htmlNodes = [doc getElementsByTagName:@"html"]; - if (htmlNodes.length >= 1) - { - static NSString *pattern = @"(.*)"; - static int opts = NSRegularExpressionDotMatchesLineSeparators; - - // Find things inside the tag. - NSRegularExpression *regex = - [[NSRegularExpression alloc] initWithPattern:pattern - options:opts error:NULL]; - NSTextCheckingResult *result = - [regex firstMatchInString:html options:0 - range:NSMakeRange(0, html.length)]; - html = [html substringWithRange:[result rangeAtIndex:1]]; - - // Replace everything in the old tag. - DOMElement *htmlNode = (DOMElement *)[htmlNodes item:0]; - htmlNode.innerHTML = html; + [manager removeItemAtURL:self.previewTempFileUrl error:NULL]; + self.previewTempFileUrl = nil; + } - return; - } + if (!self.previewTempFileUrl) + { + NSString *name = + [NSString stringWithFormat:@".macdown-preview-%@.html", + [NSUUID UUID].UUIDString]; + self.previewTempFileUrl = + [baseDirectory URLByAppendingPathComponent:name]; } -#endif - // Reload the page if there's not valid tree to work with. - [self.preview.mainFrame loadHTMLString:html baseURL:baseUrl]; - self.currentBaseUrl = baseUrl; + NSError *writeError = nil; + BOOL wrote = [html writeToURL:self.previewTempFileUrl + atomically:YES + encoding:NSUTF8StringEncoding + error:&writeError]; + if (wrote) + { + [self.preview loadFileURL:self.previewTempFileUrl + allowingReadAccessToURL:[NSURL fileURLWithPath:@"/"]]; + // -isCurrentBaseUrl: (used by -decidePolicyForNavigationAction: to + // tell an in-page anchor jump from a link to a different file) needs + // to compare against whatever WKWebView actually thinks its current + // page URL is, which after -loadFileURL: is previewTempFileUrl, not + // the document's own path. + self.currentBaseUrl = self.previewTempFileUrl; + } + else + { + // Directory isn't writable (read-only volume, external drive + // mounted read-only, etc.) -- fall back to the old behavior rather + // than showing a blank preview. Cross-directory local images will + // still fail to load in this fallback, same as before this fix. + self.previewTempFileUrl = nil; + [self.preview loadHTMLString:html baseURL:baseUrl]; + self.currentBaseUrl = baseUrl; + } } @@ -1153,8 +1184,13 @@ - (void)editorFrameDidChange:(NSNotification *)notification - (void)willStartLiveScroll:(NSNotification *)notification { - [self updateHeaderLocations]; _inLiveScroll = YES; + // Fetch fresh header locations for the upcoming scroll gesture. This is + // now asynchronous (WKWebView has no synchronous JS bridge), so the very + // first couple of scroll-sync updates in a fast flick may briefly use + // slightly stale positions from the previous render -- acceptable, but + // worth an eyeball check against the pre-migration feel. + [self updateHeaderLocationsWithCompletion:nil]; } -(void)didEndLiveScroll:(NSNotification *)notification @@ -1167,17 +1203,24 @@ - (void)editorBoundsDidChange:(NSNotification *)notification if (!self.shouldHandleBoundsChange) return; - if (self.preferences.editorSyncScrolling) + if (!self.preferences.editorSyncScrolling) + return; + + self.shouldHandleBoundsChange = NO; + if (_inLiveScroll) { - @synchronized(self) { - self.shouldHandleBoundsChange = NO; - if(!_inLiveScroll){ - [self updateHeaderLocations]; - } - - [self syncScrollers]; - self.shouldHandleBoundsChange = YES; - } + // Header locations were already fetched in -willStartLiveScroll:; + // this is pure native geometry math, still fully synchronous. + [self syncScrollers]; + self.shouldHandleBoundsChange = YES; + } + else + { + __weak typeof(self) weakSelf = self; + [self updateHeaderLocationsWithCompletion:^{ + [weakSelf syncScrollers]; + weakSelf.shouldHandleBoundsChange = YES; + }]; } } @@ -1193,11 +1236,8 @@ - (void)didRequestPreviewReload:(NSNotification *)notification [self render:nil]; } -- (void)previewDidLiveScroll:(NSNotification *)notification -{ - NSClipView *contentView = self.preview.enclosingScrollView.contentView; - self.lastPreviewScrollTop = contentView.bounds.origin.y; -} +// Preview scroll position is now tracked via the "previewScroll" script +// message (see -userContentController:didReceiveScriptMessage:). #pragma mark - KVO @@ -1227,9 +1267,11 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object - (IBAction)copyHtml:(id)sender { - // Dis-select things in WebView so that it's more obvious we're NOT - // respecting the selection range. - [self.preview setSelectedDOMRange:nil affinity:NSSelectionAffinityUpstream]; + // Dis-select things in the preview so that it's more obvious we're NOT + // respecting the selection range (WKWebView has no selection-manipulation + // API, so this goes through JavaScript instead). + [self.preview evaluateJavaScript:@"window.getSelection().removeAllRanges();" + completionHandler:nil]; // If the preview is hidden, the HTML are not updating on text change. // Perform one extra rendering so that the HTML is up to date, and do the @@ -1666,13 +1708,11 @@ - (void)redrawDivider { if (!self.editorVisible) { - // If the editor is not visible, detect preview's background color via - // DOM query and use it instead. This is more expensive; we should try - // to avoid it. - // TODO: Is it possible to cache this until the user switches the style? - // Will need to take account of the user MODIFIES the style without - // switching. Complicated. This will do for now. - self.splitView.dividerColor = MPGetWebViewBackgroundColor(self.preview); + // Preview background color is fetched asynchronously (see + // -updatePreviewBackgroundColorCache) and cached, since WKWebView + // has no synchronous DOM access. Until the first fetch completes + // this falls back to nil (transparent divider). + self.splitView.dividerColor = self.cachedPreviewBackgroundColor; } else if (!self.previewVisible) { @@ -1688,6 +1728,28 @@ - (void)redrawDivider } } +// Fetches the preview's rendered body background color asynchronously and +// caches it for -redrawDivider to read synchronously. Called after every +// successful preview load (see -previewDidFinishLoadingContent). +- (void)updatePreviewBackgroundColorCache +{ + NSString *js = @"getComputedStyle(document.body).backgroundColor"; + __weak typeof(self) weakSelf = self; + [self.preview evaluateJavaScript:js completionHandler:^(id result, NSError *error) { + if (![result isKindOfClass:[NSString class]]) + return; + NSColor *color = [NSColor colorWithHTMLName:(NSString *)result]; + if (!color) + return; + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) + return; + strongSelf.cachedPreviewBackgroundColor = color; + if (!strongSelf.editorVisible) + [strongSelf redrawDivider]; + }]; +} + - (void)scaleWebview { if (!self.preferences.previewZoomRelativeToBaseFontSize) @@ -1699,38 +1761,17 @@ - (void)scaleWebview static const CGFloat defaultSize = 14.0; CGFloat scale = fontSize / defaultSize; - -#if 0 - // Sadly, this doesn’t work correctly. - // It looks fine, but selections are offset relative to the mouse cursor. - NSScrollView *previewScrollView = - self.preview.mainFrame.frameView.documentView.enclosingScrollView; - NSClipView *previewContentView = previewScrollView.contentView; - [previewContentView scaleUnitSquareToSize:NSMakeSize(scale, scale)]; - [previewContentView setNeedsDisplay:YES]; -#else - // Warning: this is private webkit API and NOT App Store-safe! - [self.preview setPageSizeMultiplier:scale]; -#endif -} - --(void) updateHeaderLocations -{ - CGFloat offset = NSMinY(self.preview.enclosingScrollView.contentView.bounds); - NSMutableArray *locations = [NSMutableArray array]; - _webViewHeaderLocations = [[self.preview.mainFrame.javaScriptContext evaluateScript:@"var arr = Array.prototype.slice.call(document.querySelectorAll(\"h1, h2, h3, h4, h5, h6, img:only-child\")); arr.map(function(n){ return n.getBoundingClientRect().top })"] toArray]; - - // add offset to all numbers - for (NSNumber *location in _webViewHeaderLocations) - { - [locations addObject:@([location floatValue] + offset)]; - } - - _webViewHeaderLocations = [locations copy]; - + // WKWebView exposes zoom as a public property (macOS 11+), unlike the + // legacy WebView which required the private -setPageSizeMultiplier:. + self.preview.pageZoom = scale; +} - // Next, cache the locations of all of the reference nodes in the editor view. +- (void)updateHeaderLocationsWithCompletion:(void (^)(void))completion +{ + // Cache the locations of all of the reference nodes in the editor view. + // This part is pure native geometry and stays fully synchronous. + NSMutableArray *locations = [NSMutableArray array]; NSInteger characterCount = 0; NSLayoutManager *layoutManager = [self.editor layoutManager]; NSArray *documentLines = [self.editor.string componentsSeparatedByString:@"\n"]; @@ -1772,14 +1813,47 @@ -(void) updateHeaderLocations } _editorHeaderLocations = [locations copy]; + + // Next, fetch the vertical positions of the same reference nodes (already + // adjusted for the preview's current scroll offset), plus the full + // scrollable document height, from the rendered preview. This requires a + // round trip into WKWebView's JS context, so it's asynchronous -- unlike + // the editor-side geometry above, which stays fully native and synchronous. + NSString *js = + @"(function () {" + @" var offset = window.scrollY;" + @" var nodes = Array.prototype.slice.call(document.querySelectorAll(" + @" 'h1, h2, h3, h4, h5, h6, img:only-child'));" + @" var headers = nodes.map(function (n) {" + @" return n.getBoundingClientRect().top + offset;" + @" });" + @" return { headers: headers, scrollHeight: document.documentElement.scrollHeight };" + @"})();"; + __weak typeof(self) weakSelf = self; + [self.preview evaluateJavaScript:js completionHandler:^(id result, NSError *error) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) + return; + if ([result isKindOfClass:[NSDictionary class]]) + { + NSDictionary *dict = (NSDictionary *)result; + if ([dict[@"headers"] isKindOfClass:[NSArray class]]) + strongSelf->_webViewHeaderLocations = dict[@"headers"]; + if ([dict[@"scrollHeight"] isKindOfClass:[NSNumber class]]) + strongSelf.cachedPreviewContentHeight = + [dict[@"scrollHeight"] doubleValue]; + } + if (completion) + completion(); + }]; } - (void)syncScrollers { CGFloat editorContentHeight = ceilf(NSHeight(self.editor.enclosingScrollView.documentView.bounds)); CGFloat editorVisibleHeight = ceilf(NSHeight(self.editor.enclosingScrollView.contentView.bounds)); - CGFloat previewContentHeight = ceilf(NSHeight(self.preview.enclosingScrollView.documentView.bounds)); - CGFloat previewVisibleHeight = ceilf(NSHeight(self.preview.enclosingScrollView.contentView.bounds)); + CGFloat previewContentHeight = ceilf(self.cachedPreviewContentHeight); + CGFloat previewVisibleHeight = ceilf(NSHeight(self.preview.bounds)); NSInteger relativeHeaderIndex = -1; // -1 is start of document, before any other header CGFloat currY = NSMinY(self.editor.enclosingScrollView.contentView.bounds); CGFloat minY = 0; @@ -1847,9 +1921,10 @@ - (void)syncScrollers // Now we scroll percentScrolledBetweenHeaders percent between those two positions in the webview CGFloat previewY = topHeaderY + (bottomHeaderY - topHeaderY) * percentScrolledBetweenHeaders; - NSRect contentBounds = self.preview.enclosingScrollView.contentView.bounds; - contentBounds.origin.y = previewY; - self.preview.enclosingScrollView.contentView.bounds = contentBounds; + // WKWebView exposes no NSScrollView on macOS, so the scroll offset is set + // via JavaScript instead of directly manipulating an NSClipView's bounds. + NSString *js = [NSString stringWithFormat:@"window.scrollTo(0, %f);", previewY]; + [self.preview evaluateJavaScript:js completionHandler:nil]; } - (void)setSplitViewDividerLocation:(CGFloat)ratio @@ -1891,16 +1966,70 @@ - (NSString *)presumedFileName return title; } +// Mirrors the counting rules of the old DOMNode+Text.h native DOM walk: +// text inside