From 3f4a0d5370ae6c34afe180df96add3b8522f4af1 Mon Sep 17 00:00:00 2001 From: mattkae Date: Wed, 11 May 2022 09:23:58 -0400 Subject: initial commit --- elpa/skewer-mode-20200304.1142/skewer-bower.el | 217 +++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 elpa/skewer-mode-20200304.1142/skewer-bower.el (limited to 'elpa/skewer-mode-20200304.1142/skewer-bower.el') diff --git a/elpa/skewer-mode-20200304.1142/skewer-bower.el b/elpa/skewer-mode-20200304.1142/skewer-bower.el new file mode 100644 index 0000000..69bfbe6 --- /dev/null +++ b/elpa/skewer-mode-20200304.1142/skewer-bower.el @@ -0,0 +1,217 @@ +;;; skewer-bower.el --- dynamic library loading -*- lexical-binding: t; -*- + +;; This is free and unencumbered software released into the public domain. + +;;; Commentary: + +;; This package loads libraries into the current page using the bower +;; infrastructure. Note: bower is not actually used by this package +;; and so does *not* need to be installed. Only git is required (see +;; `skewer-bower-git-executable'). It will try to learn how to run git +;; from Magit if available. + +;; The interactive command for loading libraries is +;; `skewer-bower-load'. It will prompt for a library and a version, +;; automatically fetching it from the bower infrastructure if needed. +;; For example, I often find it handy to load some version of jQuery +;; when poking around at a page that doesn't already have it loaded. + +;; Caveat: unfortunately the bower infrastructure is a mess; many +;; packages are in some sort of broken state -- missing dependencies, +;; missing metadata, broken metadata, or an invalid repository URL. +;; Some of this is due to under-specification of the metadata by the +;; bower project. Broken packages are unlikely to be loadable by +;; skewer-bower. + +;;; Code: + +(require 'cl-lib) +(require 'skewer-mode) +(require 'simple-httpd) +(require 'magit nil t) ; optional + +(defcustom skewer-bower-cache-dir (locate-user-emacs-file "skewer-cache") + "Location of library cache (git repositories)." + :type 'string + :group 'skewer) + +(defcustom skewer-bower-endpoint "https://bower.herokuapp.com" + "Endpoint for accessing package information." + :type 'string + :group 'skewer) + +(defcustom skewer-bower-json '("bower.json" "package.json" "component.json") + "Files to search for package metadata." + :type 'list + :group 'skewer) + +; Try to match Magit's configuration if available +(defcustom skewer-bower-git-executable "git" + "Name of the git executable." + :type 'string + :group 'skewer) + +(defvar skewer-bower-packages nil + "Alist of all packages known to bower.") + +(defvar skewer-bower-refreshed nil + "List of packages that have been refreshed recently. This keeps +them from hitting the network frequently.") + +;;;###autoload +(defun skewer-bower-refresh () + "Update the package listing and packages synchronously." + (interactive) + (cl-declare (special url-http-end-of-headers)) + (setf skewer-bower-refreshed nil) + (with-current-buffer + (url-retrieve-synchronously (concat skewer-bower-endpoint "/packages")) + (setf (point) url-http-end-of-headers) + (setf skewer-bower-packages + (cl-sort + (cl-loop for package across (json-read) + collect (cons (cdr (assoc 'name package)) + (cdr (assoc 'url package)))) + #'string< :key #'car)))) + +;; Git functions + +(defun skewer-bower-cache (package) + "Return the cache repository directory for PACKAGE." + (unless (file-exists-p skewer-bower-cache-dir) + (make-directory skewer-bower-cache-dir t)) + (expand-file-name package skewer-bower-cache-dir)) + +(defun skewer-bower-git (package &rest args) + "Run git for PACKAGE's repository with ARGS." + (with-temp-buffer + (when (zerop (apply #'call-process skewer-bower-git-executable nil t nil + (format "--git-dir=%s" (skewer-bower-cache package)) + args)) + (buffer-string)))) + +(defun skewer-bower-git-clone (url package) + "Clone or fetch PACKAGE's repository from URL if needed." + (if (member package skewer-bower-refreshed) + t + (let* ((cache (skewer-bower-cache package)) + (status + (if (file-exists-p cache) + (when (skewer-bower-git package "fetch") + (push package skewer-bower-refreshed)) + (skewer-bower-git package "clone" "--bare" url cache)))) + (not (null status))))) + +(defun skewer-bower-git-show (package version file) + "Grab FILE from PACKAGE at version VERSION." + (when (string-match-p "^\\./" file) ; avoid relative paths + (setf file (substring file 2))) + (skewer-bower-git package "show" (format "%s:%s" version file))) + +(defun skewer-bower-git-tag (package) + "List all the tags in PACKAGE's repository." + (split-string (skewer-bower-git package "tag"))) + +;; Bower functions + +(defun skewer-bower-package-ensure (package) + "Ensure a package is installed in the cache and up to date. +Emit an error if the package could not be ensured." + (when (null skewer-bower-packages) (skewer-bower-refresh)) + (let ((url (cdr (assoc package skewer-bower-packages)))) + (when (null url) + (error "Unknown package: %s" package)) + (when (null (skewer-bower-git-clone url package)) + (error "Failed to fetch: %s" url)) + t)) + +(defun skewer-bower-package-versions (package) + "List the available versions for a package. Always returns at +least one version." + (skewer-bower-package-ensure package) + (or (sort (skewer-bower-git-tag package) #'string<) + (list "master"))) + +(defun skewer-bower-get-config (package &optional version) + "Get the configuration alist for PACKAGE at VERSION. Return nil +if no configuration could be found." + (skewer-bower-package-ensure package) + (unless version (setf version "master")) + (json-read-from-string + (cl-loop for file in skewer-bower-json + for config = (skewer-bower-git-show package version file) + when config return it + finally (return "null")))) + +;; Serving the library + +(defvar skewer-bower-history () + "Library selection history for `completing-read'.") + +(defun skewer-bowser--path (package version main) + "Return the simple-httpd hosted path for PACKAGE." + (format "/skewer/bower/%s/%s/%s" package (or version "master") main)) + +(defun skewer-bower-prompt-package () + "Prompt for a package and version from the user." + (when (null skewer-bower-packages) (skewer-bower-refresh)) + ;; ido-completing-read bug workaround: + (when (> (length skewer-bower-history) 32) + (setf skewer-bower-history (cl-subseq skewer-bower-history 0 16))) + (let* ((packages (mapcar #'car skewer-bower-packages)) + (selection (nconc skewer-bower-history packages)) + (package (completing-read "Library: " selection nil t nil + 'skewer-bower-history)) + (versions (reverse (skewer-bower-package-versions package))) + (version (completing-read "Version: " versions + nil t nil nil (car versions)))) + (list package version))) + +(defun skewer-bower--js-p (filename) + "Return non-nil if FILENAME looks like JavaScript." + (string-match "\\.js$" filename)) + +(defun skewer-bower-guess-main (package version config) + "Attempt to determine the main entrypoints from a potentially +incomplete or incorrect bower configuration. Returns nil if +guessing failed." + (let ((check (apply-partially #'skewer-bower-git-show package version)) + (main (cdr (assoc 'main config)))) + (cond ((and (vectorp main) (cl-some check main)) + (cl-coerce (cl-remove-if-not #'skewer-bower--js-p main) 'list)) + ((and (stringp main) (funcall check main)) + (list main)) + ((funcall check (concat package ".js")) + (list (concat package ".js"))) + ((funcall check package) + (list package))))) + +;;;###autoload +(defun skewer-bower-load (package &optional version) + "Dynamically load a library from bower into the current page." + (interactive (skewer-bower-prompt-package)) + (let* ((config (skewer-bower-get-config package version)) + (deps (cdr (assoc 'dependencies config))) + (main (skewer-bower-guess-main package version config))) + (when (null main) + (error "Could not load %s (%s): no \"main\" entrypoint specified" + package version)) + (cl-loop for (dep . version) in deps + do (skewer-bower-load (format "%s" dep) version)) + (cl-loop for entrypoint in main + for path = (skewer-bowser--path package version entrypoint) + do (skewer-eval path nil :type "script")))) + +(defservlet skewer/bower "application/javascript; charset=utf-8" (path) + "Serve a script from the local bower repository cache." + (cl-destructuring-bind (_ _skewer _bower package version . parts) + (split-string path "/") + (let* ((file (mapconcat #'identity parts "/")) + (contents (skewer-bower-git-show package version file))) + (if contents + (insert contents) + (httpd-error t 404))))) + +(provide 'skewer-bower) + +;;; skewer-bower.el ends here -- cgit v1.2.1