From 83ce4b400097d5c4cc654622ee499d2c78232087 Mon Sep 17 00:00:00 2001 From: hrbrmstr Date: Fri, 26 Jun 2020 07:39:27 -0400 Subject: [PATCH] pkg done --- .Rbuildignore | 4 ++ DESCRIPTION | 12 ++--- LICENSE | 2 + LICENSE.md | 21 ++++++++ NAMESPACE | 4 +- R/slugify-package.R | 10 ++-- R/slugify.R | 38 +++++++++++++++ R/zzz.R | 12 +++++ README.Rmd | 4 ++ README.md | 71 +++++++++++++++++++++++++++ inst/js/bld | 4 ++ inst/js/bundle.js | 74 ++++++++++++++++++++++++++++ inst/js/in.js | 1 + inst/js/node_modules/slugify/LICENSE | 21 ++++++++ inst/js/node_modules/slugify/README.md | 81 +++++++++++++++++++++++++++++++ inst/js/node_modules/slugify/package.json | 73 ++++++++++++++++++++++++++++ inst/js/node_modules/slugify/slugify.d.ts | 22 +++++++++ inst/js/node_modules/slugify/slugify.js | 66 +++++++++++++++++++++++++ inst/js/package-lock.json | 11 +++++ inst/tinytest/test_slugify.R | 5 +- man/slugify.Rd | 24 +++++++-- 21 files changed, 542 insertions(+), 18 deletions(-) create mode 100644 LICENSE create mode 100644 LICENSE.md create mode 100644 R/slugify.R create mode 100644 R/zzz.R create mode 100644 README.md create mode 100755 inst/js/bld create mode 100644 inst/js/bundle.js create mode 100644 inst/js/in.js create mode 100644 inst/js/node_modules/slugify/LICENSE create mode 100644 inst/js/node_modules/slugify/README.md create mode 100644 inst/js/node_modules/slugify/package.json create mode 100644 inst/js/node_modules/slugify/slugify.d.ts create mode 100644 inst/js/node_modules/slugify/slugify.js create mode 100644 inst/js/package-lock.json diff --git a/.Rbuildignore b/.Rbuildignore index c9a5c92..0528004 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -19,3 +19,7 @@ ^CRAN-RELEASE$ ^appveyor\.yml$ ^tools$ +^LICENSE\.md$ +^bld$ +^node_modules^ +^package-lock\\.json$ \ No newline at end of file diff --git a/DESCRIPTION b/DESCRIPTION index 46ad007..1f2f962 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: slugify Type: Package -Title: slugify title goes here otherwise CRAN checks fail +Title: Create slug strings for SEO Version: 0.1.0 Date: 2020-06-26 Authors@R: c( @@ -8,17 +8,17 @@ Authors@R: c( comment = c(ORCID = "0000-0001-5670-2640")) ) Maintainer: Bob Rudis -Description: A good description goes here otherwise CRAN checks fail. +Description: Most blogging platforms turn title phrases into slugs + for better SEO. Tools are provided to turn phrases into slugs. URL: https://git.rud.is/hrbrmstr/slugify BugReports: https://git.rud.is/hrbrmstr/slugify/issues Encoding: UTF-8 -License: AGPL +License: MIT + file LICENSE Suggests: covr, tinytest Depends: - R (>= 3.5.0) + R (>= 3.6.0) Imports: - httr, - jsonlite + V8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.1.0 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..49d554e --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2020 +COPYRIGHT HOLDER: Bob Rudis diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..524f6aa --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2020 Bob Rudis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NAMESPACE b/NAMESPACE index 5b4b9ae..387c0c9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,4 +1,4 @@ # Generated by roxygen2: do not edit by hand -import(httr) -importFrom(jsonlite,fromJSON) +export(slugify) +import(V8) diff --git a/R/slugify-package.R b/R/slugify-package.R index 4200edf..1310f24 100644 --- a/R/slugify-package.R +++ b/R/slugify-package.R @@ -1,9 +1,11 @@ -#' ... -#' +#' Create slug strings for SEO +#' +#' Most blogging platforms turn title phrases into slugs +#' for better SEO. Tools are provided to turn phrases into slugs. +#' #' @md #' @name slugify #' @keywords internal #' @author Bob Rudis (bob@@rud.is) -#' @import httr -#' @importFrom jsonlite fromJSON +#' @import V8 "_PACKAGE" diff --git a/R/slugify.R b/R/slugify.R new file mode 100644 index 0000000..601a862 --- /dev/null +++ b/R/slugify.R @@ -0,0 +1,38 @@ +#' Slugif a string +#' +#' @param x string +#' @param replacement replace spaces with replacement character, defaults to `-` +#' @param remove remove characters that match this regex, defaults to `NULL` (no deleting) +#' @param lower convert to lower case, defaults to `TRUE` +#' @param strict strip special characters except replacement, defaults to `TRUE` +#' @export +#' @examples +#' slugify("R is great!") +slugify <- function(x, replacement = "-", remove = NULL, lower = TRUE, strict = TRUE) { + + if (!is.null(remove)) { + + .pkgenv$ctx$call( + "slugify", x[1], + list( + replacement = "-", + remove = remove[1], + lower = lower[1], + strict = strict[1] + ) + ) + + } else { + + .pkgenv$ctx$call( + "slugify", x[1], + list( + replacement = "-", + lower = lower[1], + strict = strict[1] + ) + ) + + } + +} \ No newline at end of file diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..1df0e76 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,12 @@ +.pkgenv <- new.env(parent=emptyenv()) + +.onLoad <- function(...) { + + ctx <- V8::v8() + ctx$source(system.file("js/bundle.js", package="slugify")) + + assign("ctx", ctx, envir=.pkgenv) + + cache <- list() + +} \ No newline at end of file diff --git a/README.Rmd b/README.Rmd index 389a61b..5668c74 100644 --- a/README.Rmd +++ b/README.Rmd @@ -39,6 +39,10 @@ packageVersion("slugify") ``` +```{r ex-01} +slugify("R is great!") +``` + ## slugify Metrics ```{r cloc, echo=FALSE} diff --git a/README.md b/README.md new file mode 100644 index 0000000..1893f54 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ + +[![Project Status: Active – The project has reached a stable, usable +state and is being actively +developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) +[![Signed +by](https://img.shields.io/badge/Keybase-Verified-brightgreen.svg)](https://keybase.io/hrbrmstr) +![Signed commit +%](https://img.shields.io/badge/Signed_Commits-100%25-lightgrey.svg) +[![Linux build +Status](https://travis-ci.org/hrbrmstr/slugify.svg?branch=master)](https://travis-ci.org/hrbrmstr/slugify) +![Minimal R +Version](https://img.shields.io/badge/R%3E%3D-3.6.0-blue.svg) +![License](https://img.shields.io/badge/License-MIT-blue.svg) + +# slugify + +Create slug strings for SEO + +## Description + +Most blogging platforms turn title phrases into slugs for better SEO. +Tools are provided to turn phrases into slugs. + +## What’s Inside The Tin + +The following functions are implemented: + +## Installation + +``` r +remotes::install_git("https://git.rud.is/hrbrmstr/slugify.git") +# or +remotes::install_git("https://git.sr.ht/~hrbrmstr/slugify") +# or +remotes::install_gitlab("hrbrmstr/slugify") +# or +remotes::install_bitbucket("hrbrmstr/slugify") +# or +remotes::install_github("hrbrmstr/slugify") +``` + +NOTE: To use the ‘remotes’ install options you will need to have the +[{remotes} package](https://github.com/r-lib/remotes) installed. + +## Usage + +``` r +library(slugify) + +# current version +packageVersion("slugify") +## [1] '0.1.0' +``` + +``` r +slugify("R is great!") +## [1] "r-is-great" +``` + +## slugify Metrics + +| Lang | \# Files | (%) | LoC | (%) | Blank lines | (%) | \# Lines | (%) | +| :--- | -------: | --: | --: | ---: | ----------: | ---: | -------: | --: | +| R | 4 | 0.8 | 33 | 0.79 | 13 | 0.45 | 20 | 0.4 | +| Rmd | 1 | 0.2 | 9 | 0.21 | 16 | 0.55 | 30 | 0.6 | + +## Code of Conduct + +Please note that this project is released with a Contributor Code of +Conduct. By participating in this project you agree to abide by its +terms. diff --git a/inst/js/bld b/inst/js/bld new file mode 100755 index 0000000..4407523 --- /dev/null +++ b/inst/js/bld @@ -0,0 +1,4 @@ +npm install slugify +echo "global.slugify = require('slugify');" > in.js +browserify in.js -o bundle.js +#webpack --mode="production" in.js -o bundle.js \ No newline at end of file diff --git a/inst/js/bundle.js b/inst/js/bundle.js new file mode 100644 index 0000000..739ecdb --- /dev/null +++ b/inst/js/bundle.js @@ -0,0 +1,74 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i":"greater","|":"or","¢":"cent","£":"pound","¤":"currency","¥":"yen","©":"(c)","ª":"a","®":"(r)","º":"o","À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","Æ":"AE","Ç":"C","È":"E","É":"E","Ê":"E","Ë":"E","Ì":"I","Í":"I","Î":"I","Ï":"I","Ð":"D","Ñ":"N","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","Ù":"U","Ú":"U","Û":"U","Ü":"U","Ý":"Y","Þ":"TH","ß":"ss","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","æ":"ae","ç":"c","è":"e","é":"e","ê":"e","ë":"e","ì":"i","í":"i","î":"i","ï":"i","ð":"d","ñ":"n","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","ù":"u","ú":"u","û":"u","ü":"u","ý":"y","þ":"th","ÿ":"y","Ā":"A","ā":"a","Ă":"A","ă":"a","Ą":"A","ą":"a","Ć":"C","ć":"c","Č":"C","č":"c","Ď":"D","ď":"d","Đ":"DJ","đ":"dj","Ē":"E","ē":"e","Ė":"E","ė":"e","Ę":"e","ę":"e","Ě":"E","ě":"e","Ğ":"G","ğ":"g","Ģ":"G","ģ":"g","Ĩ":"I","ĩ":"i","Ī":"i","ī":"i","Į":"I","į":"i","İ":"I","ı":"i","Ķ":"k","ķ":"k","Ļ":"L","ļ":"l","Ľ":"L","ľ":"l","Ł":"L","ł":"l","Ń":"N","ń":"n","Ņ":"N","ņ":"n","Ň":"N","ň":"n","Ō":"O","ō":"o","Ő":"O","ő":"o","Œ":"OE","œ":"oe","Ŕ":"R","ŕ":"r","Ř":"R","ř":"r","Ś":"S","ś":"s","Ş":"S","ş":"s","Š":"S","š":"s","Ţ":"T","ţ":"t","Ť":"T","ť":"t","Ũ":"U","ũ":"u","Ū":"u","ū":"u","Ů":"U","ů":"u","Ű":"U","ű":"u","Ų":"U","ų":"u","Ŵ":"W","ŵ":"w","Ŷ":"Y","ŷ":"y","Ÿ":"Y","Ź":"Z","ź":"z","Ż":"Z","ż":"z","Ž":"Z","ž":"z","ƒ":"f","Ơ":"O","ơ":"o","Ư":"U","ư":"u","Lj":"LJ","lj":"lj","Nj":"NJ","nj":"nj","Ș":"S","ș":"s","Ț":"T","ț":"t","˚":"o","Ά":"A","Έ":"E","Ή":"H","Ί":"I","Ό":"O","Ύ":"Y","Ώ":"W","ΐ":"i","Α":"A","Β":"B","Γ":"G","Δ":"D","Ε":"E","Ζ":"Z","Η":"H","Θ":"8","Ι":"I","Κ":"K","Λ":"L","Μ":"M","Ν":"N","Ξ":"3","Ο":"O","Π":"P","Ρ":"R","Σ":"S","Τ":"T","Υ":"Y","Φ":"F","Χ":"X","Ψ":"PS","Ω":"W","Ϊ":"I","Ϋ":"Y","ά":"a","έ":"e","ή":"h","ί":"i","ΰ":"y","α":"a","β":"b","γ":"g","δ":"d","ε":"e","ζ":"z","η":"h","θ":"8","ι":"i","κ":"k","λ":"l","μ":"m","ν":"n","ξ":"3","ο":"o","π":"p","ρ":"r","ς":"s","σ":"s","τ":"t","υ":"y","φ":"f","χ":"x","ψ":"ps","ω":"w","ϊ":"i","ϋ":"y","ό":"o","ύ":"y","ώ":"w","Ё":"Yo","Ђ":"DJ","Є":"Ye","І":"I","Ї":"Yi","Ј":"J","Љ":"LJ","Њ":"NJ","Ћ":"C","Џ":"DZ","А":"A","Б":"B","В":"V","Г":"G","Д":"D","Е":"E","Ж":"Zh","З":"Z","И":"I","Й":"J","К":"K","Л":"L","М":"M","Н":"N","О":"O","П":"P","Р":"R","С":"S","Т":"T","У":"U","Ф":"F","Х":"H","Ц":"C","Ч":"Ch","Ш":"Sh","Щ":"Sh","Ъ":"U","Ы":"Y","Ь":"","Э":"E","Ю":"Yu","Я":"Ya","а":"a","б":"b","в":"v","г":"g","д":"d","е":"e","ж":"zh","з":"z","и":"i","й":"j","к":"k","л":"l","м":"m","н":"n","о":"o","п":"p","р":"r","с":"s","т":"t","у":"u","ф":"f","х":"h","ц":"c","ч":"ch","ш":"sh","щ":"sh","ъ":"u","ы":"y","ь":"","э":"e","ю":"yu","я":"ya","ё":"yo","ђ":"dj","є":"ye","і":"i","ї":"yi","ј":"j","љ":"lj","њ":"nj","ћ":"c","ѝ":"u","џ":"dz","Ґ":"G","ґ":"g","Ғ":"GH","ғ":"gh","Қ":"KH","қ":"kh","Ң":"NG","ң":"ng","Ү":"UE","ү":"ue","Ұ":"U","ұ":"u","Һ":"H","һ":"h","Ә":"AE","ә":"ae","Ө":"OE","ө":"oe","฿":"baht","ა":"a","ბ":"b","გ":"g","დ":"d","ე":"e","ვ":"v","ზ":"z","თ":"t","ი":"i","კ":"k","ლ":"l","მ":"m","ნ":"n","ო":"o","პ":"p","ჟ":"zh","რ":"r","ს":"s","ტ":"t","უ":"u","ფ":"f","ქ":"k","ღ":"gh","ყ":"q","შ":"sh","ჩ":"ch","ც":"ts","ძ":"dz","წ":"ts","ჭ":"ch","ხ":"kh","ჯ":"j","ჰ":"h","Ẁ":"W","ẁ":"w","Ẃ":"W","ẃ":"w","Ẅ":"W","ẅ":"w","ẞ":"SS","Ạ":"A","ạ":"a","Ả":"A","ả":"a","Ấ":"A","ấ":"a","Ầ":"A","ầ":"a","Ẩ":"A","ẩ":"a","Ẫ":"A","ẫ":"a","Ậ":"A","ậ":"a","Ắ":"A","ắ":"a","Ằ":"A","ằ":"a","Ẳ":"A","ẳ":"a","Ẵ":"A","ẵ":"a","Ặ":"A","ặ":"a","Ẹ":"E","ẹ":"e","Ẻ":"E","ẻ":"e","Ẽ":"E","ẽ":"e","Ế":"E","ế":"e","Ề":"E","ề":"e","Ể":"E","ể":"e","Ễ":"E","ễ":"e","Ệ":"E","ệ":"e","Ỉ":"I","ỉ":"i","Ị":"I","ị":"i","Ọ":"O","ọ":"o","Ỏ":"O","ỏ":"o","Ố":"O","ố":"o","Ồ":"O","ồ":"o","Ổ":"O","ổ":"o","Ỗ":"O","ỗ":"o","Ộ":"O","ộ":"o","Ớ":"O","ớ":"o","Ờ":"O","ờ":"o","Ở":"O","ở":"o","Ỡ":"O","ỡ":"o","Ợ":"O","ợ":"o","Ụ":"U","ụ":"u","Ủ":"U","ủ":"u","Ứ":"U","ứ":"u","Ừ":"U","ừ":"u","Ử":"U","ử":"u","Ữ":"U","ữ":"u","Ự":"U","ự":"u","Ỳ":"Y","ỳ":"y","Ỵ":"Y","ỵ":"y","Ỷ":"Y","ỷ":"y","Ỹ":"Y","ỹ":"y","‘":"\'","’":"\'","“":"\\\"","”":"\\\"","†":"+","•":"*","…":"...","₠":"ecu","₢":"cruzeiro","₣":"french franc","₤":"lira","₥":"mill","₦":"naira","₧":"peseta","₨":"rupee","₩":"won","₪":"new shequel","₫":"dong","€":"euro","₭":"kip","₮":"tugrik","₯":"drachma","₰":"penny","₱":"peso","₲":"guarani","₳":"austral","₴":"hryvnia","₵":"cedi","₸":"kazakhstani tenge","₹":"indian rupee","₺":"turkish lira","₽":"russian ruble","₿":"bitcoin","℠":"sm","™":"tm","∂":"d","∆":"delta","∑":"sum","∞":"infinity","♥":"love","元":"yuan","円":"yen","﷼":"rial"}') + var locales = JSON.parse('{"vi":{"Đ":"D","đ":"d"}}') + + function replace (string, options) { + if (typeof string !== 'string') { + throw new Error('slugify: string argument expected') + } + + options = (typeof options === 'string') + ? {replacement: options} + : options || {} + + var locale = locales[options.locale] || {} + + var replacement = options.replacement || '-' + + var slug = string.split('') + // replace characters based on charMap + .reduce(function (result, ch) { + return result + (locale[ch] || charMap[ch] || ch) + }, '') + // remove not allowed characters + .replace(options.remove || /[^\w\s$*_+~.()'"!\-:@]+/g, '') + // trim leading/trailing spaces + .trim() + // convert spaces to replacement character + // also remove duplicates of the replacement character + .replace(new RegExp('[\\s' + replacement + ']+', 'g'), replacement) + + if (options.lower) { + slug = slug.toLowerCase() + } + + if (options.strict) { + // remove anything besides letters, numbers, and the replacement char + slug = slug + .replace(new RegExp('[^a-zA-Z0-9' + replacement + ']', 'g'), '') + // remove duplicates of the replacement character + .replace(new RegExp('[\\s' + replacement + ']+', 'g'), replacement) + } + + return slug + } + + replace.extend = function (customMap) { + for (var key in customMap) { + charMap[key] = customMap[key] + } + } + + return replace +})) + +},{}]},{},[1]); diff --git a/inst/js/in.js b/inst/js/in.js new file mode 100644 index 0000000..f782ff9 --- /dev/null +++ b/inst/js/in.js @@ -0,0 +1 @@ +global.slugify = require('slugify'); diff --git a/inst/js/node_modules/slugify/LICENSE b/inst/js/node_modules/slugify/LICENSE new file mode 100644 index 0000000..ebbb2c2 --- /dev/null +++ b/inst/js/node_modules/slugify/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Simeon Velichkov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/inst/js/node_modules/slugify/README.md b/inst/js/node_modules/slugify/README.md new file mode 100644 index 0000000..6b0e87d --- /dev/null +++ b/inst/js/node_modules/slugify/README.md @@ -0,0 +1,81 @@ + +# slugify + +[![npm-version]][npm] [![travis-ci]][travis] [![coveralls-status]][coveralls] + +```js +var slugify = require('slugify') + +slugify('some string') // some-string + +// if you prefer something other than '-' as separator +slugify('some string', '_') // some_string +``` + +- Vanilla ES5 JavaScript +- No dependencies +- Coerces foreign symbols to their English equivalent (check out the [charMap][charmap] for more details) +- Works in the browser (window.slugify) and AMD/CommonJS-flavored module loaders + +## Options + +```js +slugify('some string', { + replacement: '-', // replace spaces with replacement character, defaults to `-` + remove: undefined, // remove characters that match regex, defaults to `undefined` + lower: false, // convert to lower case, defaults to `false` + strict: false, // strip special characters except replacement, defaults to `false` +}) +``` + +For example, to remove `*+~.()'"!:@` from the result slug, you can use `slugify('..', {remove: /[*+~.()'"!:@]/g})`. + +## Extend + +Out of the box `slugify` comes with support for a handful of Unicode symbols. For example the `☢` (radioactive) symbol is not defined in the [`charMap`][charmap] and therefore it will be stripped by default: + +```js +slugify('unicode ♥ is ☢') // unicode-love-is +``` + +However you can extend the supported symbols, or override the existing ones with your own: + +```js +slugify.extend({'☢': 'radioactive'}) +slugify('unicode ♥ is ☢') // unicode-love-is-radioactive +``` + +Keep in mind that the `extend` method extends/overrides the default `charMap` for the entire process. In case you need a fresh instance of the slugify's `charMap` object you have to clean up the module cache first: + +```js +delete require.cache[require.resolve('slugify')] +var slugify = require('slugify') +``` + +## Contribute + +1. Add chars to `charmap.json` +2. Run tests `npm test` +3. The tests will build the charmap in `index.js` and will sort the `charmap.json` +4. Commit **all** modified files + +--- + +> This module was originally a vanilla javascript port of [node-slug][node-slug].
+> Note that the original [slug][slug] module has been ported to vanilla javascript too.
+> One major difference between the two modules is that `slugify` does not depend on the external [unicode][unicode] module. + + + [npm-version]: https://img.shields.io/npm/v/slugify.svg?style=flat-square (NPM Package Version) + [travis-ci]: https://img.shields.io/travis/simov/slugify/master.svg?style=flat-square (Build Status - Travis CI) + [coveralls-status]: https://img.shields.io/coveralls/simov/slugify.svg?style=flat-square (Test Coverage - Coveralls) + + [npm]: https://www.npmjs.com/package/slugify + [travis]: https://travis-ci.org/simov/slugify + [coveralls]: https://coveralls.io/r/simov/slugify?branch=master + + [node-slug]: https://github.com/dodo/node-slug + [slug]: https://www.npmjs.com/package/slug + [unicode]: https://www.npmjs.com/package/unicode + [index]: https://github.com/simov/slugify/blob/master/index.js + [charmap]: https://github.com/simov/slugify/blob/master/config/charmap.json diff --git a/inst/js/node_modules/slugify/package.json b/inst/js/node_modules/slugify/package.json new file mode 100644 index 0000000..0da8fbf --- /dev/null +++ b/inst/js/node_modules/slugify/package.json @@ -0,0 +1,73 @@ +{ + "_from": "slugify", + "_id": "slugify@1.4.2", + "_inBundle": false, + "_integrity": "sha512-7UfMG5rtkxfOI5jg/+f4DMQS3ikUqfWnfMvitrhwdTV4pibWXq9mN4RNLHSV3M1lR++x7z+AG7znsiozdBP+aA==", + "_location": "/slugify", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "slugify", + "name": "slugify", + "escapedName": "slugify", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.2.tgz", + "_shasum": "2c9b2b3321c43110a0058982eea7102b998d5068", + "_spec": "slugify", + "_where": "/Users/hrbrmstr/packages/slugify/inst/js", + "author": { + "name": "Simeon Velichkov", + "email": "simeonvelichkov@gmail.com", + "url": "https://simov.github.io" + }, + "bugs": { + "url": "https://github.com/simov/slugify/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Slugifies a String", + "devDependencies": { + "coveralls": "^3.1.0", + "mocha": "^7.2.0", + "nyc": "^15.1.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "files": [ + "LICENSE", + "README.md", + "slugify.d.ts", + "slugify.js" + ], + "homepage": "https://github.com/simov/slugify", + "keywords": [ + "slugify", + "slug", + "url", + "urlify" + ], + "license": "MIT", + "main": "./slugify.js", + "name": "slugify", + "repository": { + "type": "git", + "url": "git+https://github.com/simov/slugify.git" + }, + "scripts": { + "build": "node bin/build", + "test": "npm run build && npm run test:ci", + "test:ci": "npx mocha --recursive", + "test:cov": "npx nyc --reporter=lcov --reporter=text-summary mocha -- --recursive" + }, + "types": "slugify.d.ts", + "version": "1.4.2" +} diff --git a/inst/js/node_modules/slugify/slugify.d.ts b/inst/js/node_modules/slugify/slugify.d.ts new file mode 100644 index 0000000..3daed63 --- /dev/null +++ b/inst/js/node_modules/slugify/slugify.d.ts @@ -0,0 +1,22 @@ +declare module slugify { + type ExtendArgs = { + [key: string]: any; + } + + export function extend (args: ExtendArgs): void; +} + +declare function slugify( + string: string, + options?: + | { + replacement?: string; + remove?: RegExp; + lower?: boolean; + strict?: boolean; + } + | string, + +): string; + +export default slugify; diff --git a/inst/js/node_modules/slugify/slugify.js b/inst/js/node_modules/slugify/slugify.js new file mode 100644 index 0000000..ed7e432 --- /dev/null +++ b/inst/js/node_modules/slugify/slugify.js @@ -0,0 +1,66 @@ + +;(function (name, root, factory) { + if (typeof exports === 'object') { + module.exports = factory() + module.exports['default'] = factory() + } + /* istanbul ignore next */ + else if (typeof define === 'function' && define.amd) { + define(factory) + } + else { + root[name] = factory() + } +}('slugify', this, function () { + var charMap = JSON.parse('{"$":"dollar","%":"percent","&":"and","<":"less",">":"greater","|":"or","¢":"cent","£":"pound","¤":"currency","¥":"yen","©":"(c)","ª":"a","®":"(r)","º":"o","À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","Æ":"AE","Ç":"C","È":"E","É":"E","Ê":"E","Ë":"E","Ì":"I","Í":"I","Î":"I","Ï":"I","Ð":"D","Ñ":"N","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","Ù":"U","Ú":"U","Û":"U","Ü":"U","Ý":"Y","Þ":"TH","ß":"ss","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","æ":"ae","ç":"c","è":"e","é":"e","ê":"e","ë":"e","ì":"i","í":"i","î":"i","ï":"i","ð":"d","ñ":"n","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","ù":"u","ú":"u","û":"u","ü":"u","ý":"y","þ":"th","ÿ":"y","Ā":"A","ā":"a","Ă":"A","ă":"a","Ą":"A","ą":"a","Ć":"C","ć":"c","Č":"C","č":"c","Ď":"D","ď":"d","Đ":"DJ","đ":"dj","Ē":"E","ē":"e","Ė":"E","ė":"e","Ę":"e","ę":"e","Ě":"E","ě":"e","Ğ":"G","ğ":"g","Ģ":"G","ģ":"g","Ĩ":"I","ĩ":"i","Ī":"i","ī":"i","Į":"I","į":"i","İ":"I","ı":"i","Ķ":"k","ķ":"k","Ļ":"L","ļ":"l","Ľ":"L","ľ":"l","Ł":"L","ł":"l","Ń":"N","ń":"n","Ņ":"N","ņ":"n","Ň":"N","ň":"n","Ō":"O","ō":"o","Ő":"O","ő":"o","Œ":"OE","œ":"oe","Ŕ":"R","ŕ":"r","Ř":"R","ř":"r","Ś":"S","ś":"s","Ş":"S","ş":"s","Š":"S","š":"s","Ţ":"T","ţ":"t","Ť":"T","ť":"t","Ũ":"U","ũ":"u","Ū":"u","ū":"u","Ů":"U","ů":"u","Ű":"U","ű":"u","Ų":"U","ų":"u","Ŵ":"W","ŵ":"w","Ŷ":"Y","ŷ":"y","Ÿ":"Y","Ź":"Z","ź":"z","Ż":"Z","ż":"z","Ž":"Z","ž":"z","ƒ":"f","Ơ":"O","ơ":"o","Ư":"U","ư":"u","Lj":"LJ","lj":"lj","Nj":"NJ","nj":"nj","Ș":"S","ș":"s","Ț":"T","ț":"t","˚":"o","Ά":"A","Έ":"E","Ή":"H","Ί":"I","Ό":"O","Ύ":"Y","Ώ":"W","ΐ":"i","Α":"A","Β":"B","Γ":"G","Δ":"D","Ε":"E","Ζ":"Z","Η":"H","Θ":"8","Ι":"I","Κ":"K","Λ":"L","Μ":"M","Ν":"N","Ξ":"3","Ο":"O","Π":"P","Ρ":"R","Σ":"S","Τ":"T","Υ":"Y","Φ":"F","Χ":"X","Ψ":"PS","Ω":"W","Ϊ":"I","Ϋ":"Y","ά":"a","έ":"e","ή":"h","ί":"i","ΰ":"y","α":"a","β":"b","γ":"g","δ":"d","ε":"e","ζ":"z","η":"h","θ":"8","ι":"i","κ":"k","λ":"l","μ":"m","ν":"n","ξ":"3","ο":"o","π":"p","ρ":"r","ς":"s","σ":"s","τ":"t","υ":"y","φ":"f","χ":"x","ψ":"ps","ω":"w","ϊ":"i","ϋ":"y","ό":"o","ύ":"y","ώ":"w","Ё":"Yo","Ђ":"DJ","Є":"Ye","І":"I","Ї":"Yi","Ј":"J","Љ":"LJ","Њ":"NJ","Ћ":"C","Џ":"DZ","А":"A","Б":"B","В":"V","Г":"G","Д":"D","Е":"E","Ж":"Zh","З":"Z","И":"I","Й":"J","К":"K","Л":"L","М":"M","Н":"N","О":"O","П":"P","Р":"R","С":"S","Т":"T","У":"U","Ф":"F","Х":"H","Ц":"C","Ч":"Ch","Ш":"Sh","Щ":"Sh","Ъ":"U","Ы":"Y","Ь":"","Э":"E","Ю":"Yu","Я":"Ya","а":"a","б":"b","в":"v","г":"g","д":"d","е":"e","ж":"zh","з":"z","и":"i","й":"j","к":"k","л":"l","м":"m","н":"n","о":"o","п":"p","р":"r","с":"s","т":"t","у":"u","ф":"f","х":"h","ц":"c","ч":"ch","ш":"sh","щ":"sh","ъ":"u","ы":"y","ь":"","э":"e","ю":"yu","я":"ya","ё":"yo","ђ":"dj","є":"ye","і":"i","ї":"yi","ј":"j","љ":"lj","њ":"nj","ћ":"c","ѝ":"u","џ":"dz","Ґ":"G","ґ":"g","Ғ":"GH","ғ":"gh","Қ":"KH","қ":"kh","Ң":"NG","ң":"ng","Ү":"UE","ү":"ue","Ұ":"U","ұ":"u","Һ":"H","һ":"h","Ә":"AE","ә":"ae","Ө":"OE","ө":"oe","฿":"baht","ა":"a","ბ":"b","გ":"g","დ":"d","ე":"e","ვ":"v","ზ":"z","თ":"t","ი":"i","კ":"k","ლ":"l","მ":"m","ნ":"n","ო":"o","პ":"p","ჟ":"zh","რ":"r","ს":"s","ტ":"t","უ":"u","ფ":"f","ქ":"k","ღ":"gh","ყ":"q","შ":"sh","ჩ":"ch","ც":"ts","ძ":"dz","წ":"ts","ჭ":"ch","ხ":"kh","ჯ":"j","ჰ":"h","Ẁ":"W","ẁ":"w","Ẃ":"W","ẃ":"w","Ẅ":"W","ẅ":"w","ẞ":"SS","Ạ":"A","ạ":"a","Ả":"A","ả":"a","Ấ":"A","ấ":"a","Ầ":"A","ầ":"a","Ẩ":"A","ẩ":"a","Ẫ":"A","ẫ":"a","Ậ":"A","ậ":"a","Ắ":"A","ắ":"a","Ằ":"A","ằ":"a","Ẳ":"A","ẳ":"a","Ẵ":"A","ẵ":"a","Ặ":"A","ặ":"a","Ẹ":"E","ẹ":"e","Ẻ":"E","ẻ":"e","Ẽ":"E","ẽ":"e","Ế":"E","ế":"e","Ề":"E","ề":"e","Ể":"E","ể":"e","Ễ":"E","ễ":"e","Ệ":"E","ệ":"e","Ỉ":"I","ỉ":"i","Ị":"I","ị":"i","Ọ":"O","ọ":"o","Ỏ":"O","ỏ":"o","Ố":"O","ố":"o","Ồ":"O","ồ":"o","Ổ":"O","ổ":"o","Ỗ":"O","ỗ":"o","Ộ":"O","ộ":"o","Ớ":"O","ớ":"o","Ờ":"O","ờ":"o","Ở":"O","ở":"o","Ỡ":"O","ỡ":"o","Ợ":"O","ợ":"o","Ụ":"U","ụ":"u","Ủ":"U","ủ":"u","Ứ":"U","ứ":"u","Ừ":"U","ừ":"u","Ử":"U","ử":"u","Ữ":"U","ữ":"u","Ự":"U","ự":"u","Ỳ":"Y","ỳ":"y","Ỵ":"Y","ỵ":"y","Ỷ":"Y","ỷ":"y","Ỹ":"Y","ỹ":"y","‘":"\'","’":"\'","“":"\\\"","”":"\\\"","†":"+","•":"*","…":"...","₠":"ecu","₢":"cruzeiro","₣":"french franc","₤":"lira","₥":"mill","₦":"naira","₧":"peseta","₨":"rupee","₩":"won","₪":"new shequel","₫":"dong","€":"euro","₭":"kip","₮":"tugrik","₯":"drachma","₰":"penny","₱":"peso","₲":"guarani","₳":"austral","₴":"hryvnia","₵":"cedi","₸":"kazakhstani tenge","₹":"indian rupee","₺":"turkish lira","₽":"russian ruble","₿":"bitcoin","℠":"sm","™":"tm","∂":"d","∆":"delta","∑":"sum","∞":"infinity","♥":"love","元":"yuan","円":"yen","﷼":"rial"}') + var locales = JSON.parse('{"vi":{"Đ":"D","đ":"d"}}') + + function replace (string, options) { + if (typeof string !== 'string') { + throw new Error('slugify: string argument expected') + } + + options = (typeof options === 'string') + ? {replacement: options} + : options || {} + + var locale = locales[options.locale] || {} + + var replacement = options.replacement || '-' + + var slug = string.split('') + // replace characters based on charMap + .reduce(function (result, ch) { + return result + (locale[ch] || charMap[ch] || ch) + }, '') + // remove not allowed characters + .replace(options.remove || /[^\w\s$*_+~.()'"!\-:@]+/g, '') + // trim leading/trailing spaces + .trim() + // convert spaces to replacement character + // also remove duplicates of the replacement character + .replace(new RegExp('[\\s' + replacement + ']+', 'g'), replacement) + + if (options.lower) { + slug = slug.toLowerCase() + } + + if (options.strict) { + // remove anything besides letters, numbers, and the replacement char + slug = slug + .replace(new RegExp('[^a-zA-Z0-9' + replacement + ']', 'g'), '') + // remove duplicates of the replacement character + .replace(new RegExp('[\\s' + replacement + ']+', 'g'), replacement) + } + + return slug + } + + replace.extend = function (customMap) { + for (var key in customMap) { + charMap[key] = customMap[key] + } + } + + return replace +})) diff --git a/inst/js/package-lock.json b/inst/js/package-lock.json new file mode 100644 index 0000000..116a0a8 --- /dev/null +++ b/inst/js/package-lock.json @@ -0,0 +1,11 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "slugify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.2.tgz", + "integrity": "sha512-7UfMG5rtkxfOI5jg/+f4DMQS3ikUqfWnfMvitrhwdTV4pibWXq9mN4RNLHSV3M1lR++x7z+AG7znsiozdBP+aA==" + } + } +} diff --git a/inst/tinytest/test_slugify.R b/inst/tinytest/test_slugify.R index aa3f72e..9d913e9 100644 --- a/inst/tinytest/test_slugify.R +++ b/inst/tinytest/test_slugify.R @@ -1,4 +1,3 @@ +library(slugify) -# Placeholder with simple test -expect_equal(1 + 1, 2) - +expect_true(slugify("R is great!") == "r-is-great") \ No newline at end of file diff --git a/man/slugify.Rd b/man/slugify.Rd index 63554d4..a956a2c 100644 --- a/man/slugify.Rd +++ b/man/slugify.Rd @@ -1,12 +1,30 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/slugify-package.R +% Please edit documentation in R/slugify-package.R, R/slugify.R \docType{package} \name{slugify} \alias{slugify} \alias{slugify-package} -\title{...} +\title{Create slug strings for SEO} +\usage{ +slugify(x, replacement = "-", remove = NULL, lower = TRUE, strict = TRUE) +} +\arguments{ +\item{x}{string} + +\item{replacement}{replace spaces with replacement character, defaults to \code{-}} + +\item{remove}{remove characters that match this regex, defaults to \code{NULL} (no deleting)} + +\item{lower}{convert to lower case, defaults to \code{TRUE}} + +\item{strict}{strip special characters except replacement, defaults to \code{TRUE}} +} \description{ -A good description goes here otherwise CRAN checks fail. +Most blogging platforms turn title phrases into slugs +for better SEO. Tools are provided to turn phrases into slugs. +} +\examples{ +slugify("R is great!") } \seealso{ Useful links: