diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 000000000..4278d97e1 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,993 @@ +{ + "files": [ + "README.md" + ], + "imageSize": 100, + "commit": false, + "contributors": [ + { + "login": "jaredpalmer", + "name": "Jared Palmer", + "avatar_url": "https://avatars2.githubusercontent.com/u/4060187?v=4", + "profile": "https://jaredpalmer.com", + "contributions": [ + "doc", + "design", + "review", + "tool", + "test", + "maintenance", + "code" + ] + }, + { + "login": "sw-yx", + "name": "swyx", + "avatar_url": "https://avatars1.githubusercontent.com/u/6764957?v=4", + "profile": "https://twitter.com/swyx", + "contributions": [ + "bug", + "code", + "doc", + "design", + "ideas", + "infra", + "maintenance", + "review" + ] + }, + { + "login": "JasonEtco", + "name": "Jason Etcovitch", + "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", + "profile": "https://jasonet.co", + "contributions": [ + "bug", + "test" + ] + }, + { + "login": "skvale", + "name": "Sam Kvale", + "avatar_url": "https://avatars0.githubusercontent.com/u/5314713?v=4", + "profile": "https://github.com/skvale", + "contributions": [ + "code", + "test", + "bug", + "doc", + "review", + "ideas", + "question" + ] + }, + { + "login": "lpolito", + "name": "Lucas Polito", + "avatar_url": "https://avatars3.githubusercontent.com/u/41299650?v=4", + "profile": "https://lucaspolito.dev/", + "contributions": [ + "code", + "doc", + "question" + ] + }, + { + "login": "SKalt", + "name": "Steven Kalt", + "avatar_url": "https://avatars0.githubusercontent.com/u/10438373?v=4", + "profile": "https://skalt.github.io", + "contributions": [ + "code" + ] + }, + { + "login": "hedgerh", + "name": "Harry Hedger", + "avatar_url": "https://avatars2.githubusercontent.com/u/2524280?v=4", + "profile": "https://twitter.com/harry_hedger", + "contributions": [ + "ideas", + "doc", + "code", + "question" + ] + }, + { + "login": "arthurdenner", + "name": "Arthur Denner", + "avatar_url": "https://avatars0.githubusercontent.com/u/13774309?v=4", + "profile": "https://github.com/arthurdenner", + "contributions": [ + "bug", + "code", + "question" + ] + }, + { + "login": "Carl-Foster", + "name": "Carl", + "avatar_url": "https://avatars2.githubusercontent.com/u/5793483?v=4", + "profile": "https://carlfoster.io", + "contributions": [ + "ideas", + "doc", + "code", + "test", + "question" + ] + }, + { + "login": "LoicMahieu", + "name": "Loïc Mahieu", + "avatar_url": "https://avatars0.githubusercontent.com/u/900947?v=4", + "profile": "http://iGLOO.be", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "sebald", + "name": "Sebastian Sebald", + "avatar_url": "https://avatars3.githubusercontent.com/u/985701?v=4", + "profile": "https://github.com/sebald", + "contributions": [ + "doc", + "code", + "test" + ] + }, + { + "login": "karlhorky", + "name": "Karl Horky", + "avatar_url": "https://avatars2.githubusercontent.com/u/1935696?v=4", + "profile": "https://twitter.com/karlhorky", + "contributions": [ + "doc", + "ideas" + ] + }, + { + "login": "jamesgeorge007", + "name": "James George", + "avatar_url": "https://avatars2.githubusercontent.com/u/25279263?v=4", + "profile": "https://ghuser.io/jamesgeorge007", + "contributions": [ + "doc" + ] + }, + { + "login": "agilgur5", + "name": "Anton Gilgur", + "avatar_url": "https://avatars3.githubusercontent.com/u/4970083?v=4", + "profile": "https://twitter.com/agilgur5", + "contributions": [ + "maintenance", + "doc", + "code", + "bug", + "example", + "ideas", + "question", + "review", + "test" + ] + }, + { + "login": "kylemh", + "name": "Kyle Holmberg", + "avatar_url": "https://avatars1.githubusercontent.com/u/9523719?v=4", + "profile": "https://kylemh.com", + "contributions": [ + "code", + "example", + "test", + "review", + "question" + ] + }, + { + "login": "sisp", + "name": "Sigurd Spieckermann", + "avatar_url": "https://avatars1.githubusercontent.com/u/2206639?v=4", + "profile": "https://github.com/sisp", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "selbekk", + "name": "Kristofer Giltvedt Selbekk", + "avatar_url": "https://avatars1.githubusercontent.com/u/1307267?v=4", + "profile": "https://www.selbekk.io", + "contributions": [ + "code" + ] + }, + { + "login": "tricoder42", + "name": "Tomáš Ehrlich", + "avatar_url": "https://avatars2.githubusercontent.com/u/827862?v=4", + "profile": "https://tomasehrlich.cz", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "kyle-johnson", + "name": "Kyle Johnson", + "avatar_url": "https://avatars3.githubusercontent.com/u/1007162?v=4", + "profile": "https://github.com/kyle-johnson", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "etienne-dldc", + "name": "Etienne Dldc", + "avatar_url": "https://avatars3.githubusercontent.com/u/14336608?v=4", + "profile": "http://www.etiennedeladonchamps.fr/", + "contributions": [ + "bug", + "code", + "test" + ] + }, + { + "login": "fknop", + "name": "Florian Knop", + "avatar_url": "https://avatars2.githubusercontent.com/u/6775689?v=4", + "profile": "https://github.com/fknop", + "contributions": [ + "bug" + ] + }, + { + "login": "gndelia", + "name": "Gonzalo D'Elia", + "avatar_url": "https://avatars1.githubusercontent.com/u/352474?v=4", + "profile": "https://github.com/gndelia", + "contributions": [ + "code" + ] + }, + { + "login": "aleclarson", + "name": "Alec Larson", + "avatar_url": "https://avatars2.githubusercontent.com/u/1925840?v=4", + "profile": "https://patreon.com/aleclarson", + "contributions": [ + "code", + "review", + "ideas", + "question" + ] + }, + { + "login": "justingrant", + "name": "Justin Grant", + "avatar_url": "https://avatars2.githubusercontent.com/u/277214?v=4", + "profile": "http://cantaloupesys.com/", + "contributions": [ + "bug", + "ideas", + "question" + ] + }, + { + "login": "n3tr", + "name": "Jirat Ki.", + "avatar_url": "https://avatars3.githubusercontent.com/u/155392?v=4", + "profile": "http://n3tr.com", + "contributions": [ + "code", + "test", + "bug" + ] + }, + { + "login": "natemoo-re", + "name": "Nate Moore", + "avatar_url": "https://avatars0.githubusercontent.com/u/7118177?v=4", + "profile": "http://natemoo.re", + "contributions": [ + "code", + "ideas" + ] + }, + { + "login": "diegohaz", + "name": "Haz", + "avatar_url": "https://avatars3.githubusercontent.com/u/3068563?v=4", + "profile": "https://twitter.com/diegohaz", + "contributions": [ + "doc" + ] + }, + { + "login": "bastibuck", + "name": "Basti Buck", + "avatar_url": "https://avatars1.githubusercontent.com/u/6306291?v=4", + "profile": "http://bastibuck.de", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "PabloSzx", + "name": "Pablo Saez", + "avatar_url": "https://avatars3.githubusercontent.com/u/8672915?v=4", + "profile": "https://pablosz.tech", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "jakegavin", + "name": "Jake Gavin", + "avatar_url": "https://avatars2.githubusercontent.com/u/5965895?v=4", + "profile": "http://www.twitter.com/jake_gavin", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "a-type", + "name": "Grant Forrest", + "avatar_url": "https://avatars1.githubusercontent.com/u/2829772?v=4", + "profile": "https://grantforrest.dev", + "contributions": [ + "code", + "test", + "bug" + ] + }, + { + "login": "slorber", + "name": "Sébastien Lorber", + "avatar_url": "https://avatars0.githubusercontent.com/u/749374?v=4", + "profile": "https://sebastienlorber.com/", + "contributions": [ + "code" + ] + }, + { + "login": "kirjai", + "name": "Kirils Ladovs", + "avatar_url": "https://avatars1.githubusercontent.com/u/9858620?v=4", + "profile": "https://kirjai.com", + "contributions": [ + "doc" + ] + }, + { + "login": "enesTufekci", + "name": "Enes Tüfekçi", + "avatar_url": "https://avatars3.githubusercontent.com/u/16020295?v=4", + "profile": "https://github.com/enesTufekci", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "TrySound", + "name": "Bogdan Chadkin", + "avatar_url": "https://avatars0.githubusercontent.com/u/5635476?v=4", + "profile": "https://twitter.com/IAmTrySound", + "contributions": [ + "review", + "question", + "ideas" + ] + }, + { + "login": "FredyC", + "name": "Daniel K.", + "avatar_url": "https://avatars0.githubusercontent.com/u/1096340?v=4", + "profile": "https://github.com/FredyC", + "contributions": [ + "code", + "doc", + "test", + "ideas", + "bug" + ] + }, + { + "login": "quentin-sommer", + "name": "Quentin Sommer", + "avatar_url": "https://avatars2.githubusercontent.com/u/9129496?v=4", + "profile": "http://www.quentin-sommer.com", + "contributions": [ + "doc" + ] + }, + { + "login": "hyanmandian", + "name": "Hyan Mandian", + "avatar_url": "https://avatars3.githubusercontent.com/u/5044101?v=4", + "profile": "https://hyan.com.br", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "dance2die", + "name": "Sung M. Kim", + "avatar_url": "https://avatars1.githubusercontent.com/u/8465237?v=4", + "profile": "https://twitter.com/dance2die", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "johnrjj", + "name": "John Johnson", + "avatar_url": "https://avatars0.githubusercontent.com/u/1103963?v=4", + "profile": "https://github.com/johnrjj", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "jooohn", + "name": "Jun Tomioka", + "avatar_url": "https://avatars0.githubusercontent.com/u/2661835?v=4", + "profile": "https://github.com/jooohn", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "leonardodino", + "name": "Leonardo Dino", + "avatar_url": "https://avatars2.githubusercontent.com/u/8649362?v=4", + "profile": "https://kunst.com.br", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "honzabrecka", + "name": "Honza Břečka", + "avatar_url": "https://avatars3.githubusercontent.com/u/1021827?v=4", + "profile": "https://honzabrecka.com", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "wrdls", + "name": "Ward Loos", + "avatar_url": "https://avatars1.githubusercontent.com/u/4059732?v=4", + "profile": "http://chatlayer.ai", + "contributions": [ + "code", + "ideas" + ] + }, + { + "login": "bbugh", + "name": "Brian Bugh", + "avatar_url": "https://avatars3.githubusercontent.com/u/438465?v=4", + "profile": "https://github.com/bbugh", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "ccarse", + "name": "Cody Carse", + "avatar_url": "https://avatars2.githubusercontent.com/u/1965943?v=4", + "profile": "https://github.com/ccarse", + "contributions": [ + "doc" + ] + }, + { + "login": "sadsa", + "name": "Josh Biddick", + "avatar_url": "https://avatars0.githubusercontent.com/u/3200576?v=4", + "profile": "http://sadsa.github.io", + "contributions": [ + "code" + ] + }, + { + "login": "albizures", + "name": "Jose Albizures", + "avatar_url": "https://avatars3.githubusercontent.com/u/6843073?v=4", + "profile": "http://albizures.com", + "contributions": [ + "code", + "test", + "bug" + ] + }, + { + "login": "netzwerg", + "name": "Rahel Lüthy", + "avatar_url": "https://avatars3.githubusercontent.com/u/439387?v=4", + "profile": "https://netzwerg.ch", + "contributions": [ + "doc" + ] + }, + { + "login": "medelman17", + "name": "Michael Edelman ", + "avatar_url": "https://avatars1.githubusercontent.com/u/14793389?v=4", + "profile": "https://fabulas.io", + "contributions": [ + "code", + "ideas" + ] + }, + { + "login": "tunnckoCore", + "name": "Charlike Mike Reagent", + "avatar_url": "https://avatars3.githubusercontent.com/u/5038030?v=4", + "profile": "https://tunnckoCore.com", + "contributions": [ + "review", + "code", + "ideas" + ] + }, + { + "login": "wessberg", + "name": "Frederik Wessberg", + "avatar_url": "https://avatars0.githubusercontent.com/u/20454213?v=4", + "profile": "https://github.com/wessberg", + "contributions": [ + "question" + ] + }, + { + "login": "elado", + "name": "Elad Ossadon", + "avatar_url": "https://avatars0.githubusercontent.com/u/51488?v=4", + "profile": "http://elad.ossadon.com", + "contributions": [ + "code", + "test", + "bug" + ] + }, + { + "login": "third774", + "name": "Kevin Kipp", + "avatar_url": "https://avatars3.githubusercontent.com/u/8732191?v=4", + "profile": "https://github.com/third774", + "contributions": [ + "code" + ] + }, + { + "login": "mfolnovic", + "name": "Matija Folnovic", + "avatar_url": "https://avatars3.githubusercontent.com/u/20919?v=4", + "profile": "https://github.com/mfolnovic", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "Aidurber", + "name": "Andrew", + "avatar_url": "https://avatars1.githubusercontent.com/u/5732291?v=4", + "profile": "https://github.com/Aidurber", + "contributions": [ + "code" + ] + }, + { + "login": "audiolion", + "name": "Ryan Castner", + "avatar_url": "https://avatars1.githubusercontent.com/u/2430381?v=4", + "profile": "http://audiolion.github.io", + "contributions": [ + "code", + "test", + "ideas" + ] + }, + { + "login": "yordis", + "name": "Yordis Prieto", + "avatar_url": "https://avatars0.githubusercontent.com/u/4237280?v=4", + "profile": "https://github.com/yordis", + "contributions": [ + "code" + ] + }, + { + "login": "ncphillips", + "name": "NCPhillips", + "avatar_url": "https://avatars2.githubusercontent.com/u/824015?v=4", + "profile": "http://www.ncphi.com", + "contributions": [ + "doc" + ] + }, + { + "login": "ArnaudBarre", + "name": "Arnaud Barré", + "avatar_url": "https://avatars1.githubusercontent.com/u/14235743?v=4", + "profile": "https://github.com/ArnaudBarre", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "techieshark", + "name": "Peter W", + "avatar_url": "https://avatars2.githubusercontent.com/u/1072292?v=4", + "profile": "http://twitter.com/techieshark", + "contributions": [ + "doc" + ] + }, + { + "login": "joeflateau", + "name": "Joe Flateau", + "avatar_url": "https://avatars0.githubusercontent.com/u/643331?v=4", + "profile": "http://joeflateau.net", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "goznauk", + "name": "H.John Choi", + "avatar_url": "https://avatars0.githubusercontent.com/u/4438903?v=4", + "profile": "http://goznauk.github.io", + "contributions": [ + "doc" + ] + }, + { + "login": "lookfirst", + "name": "Jon Stevens", + "avatar_url": "https://avatars0.githubusercontent.com/u/85355?v=4", + "profile": "https://brave.com/loo095", + "contributions": [ + "doc", + "ideas", + "bug" + ] + }, + { + "login": "greenkeeper[bot]", + "name": "greenkeeper[bot]", + "avatar_url": "https://avatars3.githubusercontent.com/in/505?v=4", + "profile": "https://github.com/apps/greenkeeper", + "contributions": [ + "infra", + "code" + ] + }, + { + "login": "allcontributors[bot]", + "name": "allcontributors[bot]", + "avatar_url": "https://avatars0.githubusercontent.com/in/23186?v=4", + "profile": "https://github.com/apps/allcontributors", + "contributions": [ + "infra", + "doc" + ] + }, + { + "login": "dependabot[bot]", + "name": "dependabot[bot]", + "avatar_url": "https://avatars0.githubusercontent.com/in/29110?v=4", + "profile": "https://github.com/apps/dependabot", + "contributions": [ + "infra", + "security", + "code" + ] + }, + { + "login": "github", + "name": "GitHub", + "avatar_url": "https://avatars1.githubusercontent.com/u/9919?v=4", + "profile": "https://github.com/about", + "contributions": [ + "infra" + ] + }, + { + "login": "ambroseus", + "name": "Eugene Samonenko", + "avatar_url": "https://avatars0.githubusercontent.com/u/380645?v=4", + "profile": "http://linkedin.com/in/ambroseus", + "contributions": [ + "test", + "example", + "question", + "ideas" + ] + }, + { + "login": "rockmandash", + "name": "Joseph Wang", + "avatar_url": "https://avatars2.githubusercontent.com/u/7580792?v=4", + "profile": "https://github.com/rockmandash", + "contributions": [ + "bug" + ] + }, + { + "login": "kotarella1110", + "name": "Kotaro Sugawara", + "avatar_url": "https://avatars1.githubusercontent.com/u/12913947?v=4", + "profile": "https://qiita.com/kotarella1110", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "Semperia", + "name": "Semesse", + "avatar_url": "https://avatars1.githubusercontent.com/u/13726406?v=4", + "profile": "https://blog.semesse.me", + "contributions": [ + "code" + ] + }, + { + "login": "bmihelac", + "name": "Bojan Mihelac", + "avatar_url": "https://avatars0.githubusercontent.com/u/13813?v=4", + "profile": "https://informatikamihelac.com", + "contributions": [ + "code" + ] + }, + { + "login": "dandv", + "name": "Dan Dascalescu", + "avatar_url": "https://avatars3.githubusercontent.com/u/33569?v=4", + "profile": "https://dandascalescu.com/", + "contributions": [ + "doc" + ] + }, + { + "login": "yuriy636", + "name": "Yuriy Burychka", + "avatar_url": "https://avatars3.githubusercontent.com/u/6631050?v=4", + "profile": "https://github.com/yuriy636", + "contributions": [ + "code" + ] + }, + { + "login": "jssee", + "name": "Jesse Hoyos", + "avatar_url": "https://avatars1.githubusercontent.com/u/2642936?v=4", + "profile": "https://github.com/jssee", + "contributions": [ + "code" + ] + }, + { + "login": "devrelm", + "name": "Mike Deverell", + "avatar_url": "https://avatars0.githubusercontent.com/u/2008333?v=4", + "profile": "https://twitter.com/devrelm", + "contributions": [ + "code" + ] + }, + { + "login": "HipsterBrown", + "name": "Nick Hehr", + "avatar_url": "https://avatars3.githubusercontent.com/u/3051193?v=4", + "profile": "https://hipsterbrown.com", + "contributions": [ + "code", + "doc", + "example" + ] + }, + { + "login": "Bnaya", + "name": "Bnaya Peretz", + "avatar_url": "https://avatars0.githubusercontent.com/u/1304862?v=4", + "profile": "https://github.com/Bnaya", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "andresz1", + "name": "Andres Alvarez", + "avatar_url": "https://avatars2.githubusercontent.com/u/6877967?v=4", + "profile": "https://github.com/andresz1", + "contributions": [ + "code", + "doc", + "example" + ] + }, + { + "login": "kyarik", + "name": "Yaroslav K.", + "avatar_url": "https://avatars2.githubusercontent.com/u/33955898?v=4", + "profile": "https://github.com/kyarik", + "contributions": [ + "doc" + ] + }, + { + "login": "strdr4605", + "name": "Dragoș Străinu", + "avatar_url": "https://avatars3.githubusercontent.com/u/16056918?v=4", + "profile": "https://strdr4605.github.io", + "contributions": [ + "ideas" + ] + }, + { + "login": "georgevarghese185", + "name": "George Varghese M.", + "avatar_url": "https://avatars1.githubusercontent.com/u/20477438?v=4", + "profile": "https://www.linkedin.com/in/george-varghese-m/", + "contributions": [ + "code", + "doc", + "test" + ] + }, + { + "login": "slikts", + "name": "Reinis Ivanovs", + "avatar_url": "https://avatars2.githubusercontent.com/u/137872?v=4", + "profile": "https://nelabs.dev/", + "contributions": [ + "ideas", + "question" + ] + }, + { + "login": "orta", + "name": "Orta Therox", + "avatar_url": "https://avatars2.githubusercontent.com/u/49038?v=4", + "profile": "https://orta.io", + "contributions": [ + "question", + "doc" + ] + }, + { + "login": "thany", + "name": "Martijn Saly", + "avatar_url": "https://avatars1.githubusercontent.com/u/152227?v=4", + "profile": "https://github.com/thany", + "contributions": [ + "bug" + ] + }, + { + "login": "KATT", + "name": "Alex Johansson", + "avatar_url": "https://avatars1.githubusercontent.com/u/459267?v=4", + "profile": "http://kattcorp.com", + "contributions": [ + "doc" + ] + }, + { + "login": "hb-seb", + "name": "hb-seb", + "avatar_url": "https://avatars1.githubusercontent.com/u/69623566?v=4", + "profile": "https://github.com/hb-seb", + "contributions": [ + "code" + ] + }, + { + "login": "seungdols", + "name": "seungdols", + "avatar_url": "https://avatars3.githubusercontent.com/u/16032614?v=4", + "profile": "http://seungdols.tistory.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "CyriacBr", + "name": "Béré Cyriac", + "avatar_url": "https://avatars3.githubusercontent.com/u/38442110?v=4", + "profile": "https://github.com/CyriacBr", + "contributions": [ + "bug" + ] + }, + { + "login": "in19farkt", + "name": "Dmitriy Serdtsev", + "avatar_url": "https://avatars3.githubusercontent.com/u/12945918?v=4", + "profile": "https://github.com/in19farkt", + "contributions": [ + "bug" + ] + }, + { + "login": "vladdy-moses", + "name": "Vladislav Moiseev", + "avatar_url": "https://avatars3.githubusercontent.com/u/3105477?v=4", + "profile": "http://formoses.ru/", + "contributions": [ + "code" + ] + }, + { + "login": "felixmosh", + "name": "Felix Mosheev", + "avatar_url": "https://avatars3.githubusercontent.com/u/9304194?v=4", + "profile": "https://github.com/felixmosh", + "contributions": [ + "bug" + ] + }, + { + "login": "ludofischer", + "name": "Ludovico Fischer", + "avatar_url": "https://avatars1.githubusercontent.com/u/43557?v=4", + "profile": "http://www.ludofischer.com", + "contributions": [ + "code" + ] + }, + { + "login": "altrim", + "name": "Altrim Beqiri", + "avatar_url": "https://avatars0.githubusercontent.com/u/602300?v=4", + "profile": "http://www.altrimbeqiri.com", + "contributions": [ + "bug", + "code", + "test" + ] + }, + { + "login": "tanem", + "name": "Tane Morgan", + "avatar_url": "https://avatars3.githubusercontent.com/u/464864?v=4", + "profile": "https://github.com/tanem", + "contributions": [ + "bug", + "code" + ] + } + ], + "contributorsPerLine": 7, + "projectName": "tsdx", + "projectOwner": "formium", + "repoType": "github", + "repoHost": "https://github.com", + "skipCi": true +} diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..23d99f204 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +/node_modules/ +dist/ diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9b4562384..65a8af7b4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -25,12 +25,14 @@ assignees: '' ### Your environment - - -| Software | Version(s) | -| ---------------- | ---------- | -| TSDX | -| TypeScript | -| Browser | -| npm/Yarn | -| Operating System | + + +```text + +``` diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..ad8a3c9be --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,39 @@ +version: 2 + +updates: + # configuration for / + - package-ecosystem: npm + directory: '/' + schedule: + interval: weekly # don't spam daily + commit-message: + prefix: 'deps:' # prefix commit with deps: for consistency + # only increase version when required, don't bump every patch or minor + versioning-strategy: increase-if-necessary + allow: + # only upgrade prod deps (not devDeps) + - dependency-name: '*' + dependency-type: production + # temporarily disable dep upgrade PRs for / as they're being updated + open-pull-requests-limit: 0 + + # configuration for /website + - package-ecosystem: npm + directory: /website + schedule: + interval: weekly # don't spam daily + commit-message: + prefix: 'deps:' # prefix commit with deps: for consistency + # only increase version when required, don't bump every patch or minor + versioning-strategy: increase-if-necessary + allow: + # only upgrade prod deps (not devDeps) + - dependency-name: '*' + dependency-type: production + # /website is not a published package and doesn't really have an attack + # surface area, should only be updated as needed, not as soon as deps change + ignore: + # no security PRs for /website + - dependency-name: '*' + # disable dep upgrade PRs for /website + open-pull-requests-limit: 0 diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 1fd4e44fe..6b02c7eae 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,30 +1,51 @@ name: Node CI -on: [push] +on: + pull_request: + types: [opened, synchronize, reopened] jobs: + lint-and-dedupe: + runs-on: ubuntu-latest + + name: Lint & Deduplicate deps on node 10.x and ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Use Node 10.x + uses: actions/setup-node@v1 + with: + node-version: 10.x + + - name: Install deps and build (with cache) + uses: bahmutov/npm-install@v1 + + - name: Lint codebase + run: yarn lint:post-build + + - name: Deduplicate dependencies + run: yarn deduplicate:check + test: - runs-on: ${{ matrix.os }} + name: Test on Node ${{ matrix.node }} and ${{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: matrix: - node: ['8.x', '10.x', '12.x'] + node: ['10.x', '12.x', '14.x'] os: [ubuntu-latest, windows-latest, macOS-latest] - name: Test on node ${{ matrix.node }} and ${{ matrix.os }} - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node }} + - name: Checkout repo + uses: actions/checkout@v2 + - name: Use Node ${{ matrix.node }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node }} - - name: Install yarn package manager - run: npm install -g yarn - - - name: Install deps and build - run: yarn install --frozen-lockfile + - name: Install deps and build (with cache) + uses: bahmutov/npm-install@v1 - name: Test package - run: yarn test + run: yarn test:post-build diff --git a/.gitignore b/.gitignore index fac11a26c..d2f6a9155 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,9 @@ *.log .DS_Store node_modules -.rts2_cache_cjs -.rts2_cache_esm -.rts2_cache_umd dist tester tester-react +package-lock.json # Local Netlify folder .netlify diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..2f0479305 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at hello@formium.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8544bf8e2..ea14025f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ Thanks for your interest in TSDX! You are very welcome to contribute. If you are cd tsdx ``` -1. Install the dependencies and build the Typescript files to Javascript: +1. Install the dependencies and build the TypeScript files to JavaScript: ``` yarn && yarn build diff --git a/README.md b/README.md index 879610665..d008aa1e6 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,20 @@ ![tsdx](https://user-images.githubusercontent.com/4060187/56918426-fc747600-6a8b-11e9-806d-2da0b49e89e4.png) -[![Blazing Fast](https://badgen.now.sh/badge/speed/blazing%20%F0%9F%94%A5/green)](https://npm.im/tsdx) [![Blazing Fast](https://badgen.now.sh/badge/speed/blazing%20%F0%9F%94%A5/green)](https://npm.im/tsdx) [![Blazing Fast](https://badgen.now.sh/badge/speed/blazing%20%F0%9F%94%A5/green)](https://npm.im/tsdx) +[![Blazing Fast](https://badgen.now.sh/badge/speed/blazing%20%F0%9F%94%A5/green)](https://npm.im/tsdx) [![Blazing Fast](https://badgen.now.sh/badge/speed/blazing%20%F0%9F%94%A5/green)](https://npm.im/tsdx) [![Blazing Fast](https://badgen.now.sh/badge/speed/blazing%20%F0%9F%94%A5/green)](https://npm.im/tsdx) [![Greenkeeper badge](https://badges.greenkeeper.io/jaredpalmer/tsdx.svg)](https://greenkeeper.io/) Despite all the recent hype, setting up a new TypeScript (x React) library can be tough. Between [Rollup](https://github.com/rollup/rollup), [Jest](https://github.com/facebook/jest), `tsconfig`, [Yarn resolutions](https://yarnpkg.com/en/docs/selective-version-resolutions), ESLint, and getting VSCode to play nicely....there is just a whole lot of stuff to do (and things to screw up). TSDX is a zero-config CLI that helps you develop, test, and publish modern TypeScript packages with ease--so you can focus on your awesome new library and not waste another afternoon on the configuration. + - [Features](#features) - [Quick Start](#quick-start) - [`npm start` or `yarn start`](#npm-start-or-yarn-start) - [`npm run build` or `yarn build`](#npm-run-build-or-yarn-build) - [`npm test` or `yarn test`](#npm-test-or-yarn-test) - [`npm run lint` or `yarn lint`](#npm-run-lint-or-yarn-lint) + - [`prepare` script](#prepare-script) - [Optimizations](#optimizations) - [Development-only Expressions + Treeshaking](#development-only-expressions--treeshaking) - [Rollup Treeshaking](#rollup-treeshaking) @@ -26,15 +28,20 @@ Despite all the recent hype, setting up a new TypeScript (x React) library can b - [Rollup](#rollup) - [Example: Adding Postcss](#example-adding-postcss) - [Babel](#babel) + - [Jest](#jest) + - [ESLint](#eslint) + - [`patch-package`](#patch-package) - [Inspiration](#inspiration) - - [Comparison to Microbundle](#comparison-to-microbundle) + - [Comparison with Microbundle](#comparison-with-microbundle) - [API Reference](#api-reference) - [`tsdx watch`](#tsdx-watch) - [`tsdx build`](#tsdx-build) - [`tsdx test`](#tsdx-test) - [`tsdx lint`](#tsdx-lint) +- [Contributing](#contributing) - [Author](#author) - [License](#license) +- [Contributors ✨](#contributors-) @@ -50,17 +57,19 @@ TSDX comes with the "battery-pack included" and is part of a complete TypeScript - Bundle size snapshots - Opt-in to extract `invariant` error codes - Jest test runner setup with sensible defaults via `tsdx test` +- ESLint with Prettier setup with sensible defaults via `tsdx lint` - Zero-config, single dependency +- Escape hatches for customization via `.babelrc.js`, `jest.config.js`, `.eslintrc.js`, and `tsdx.config.js` ## Quick Start -``` +```bash npx tsdx create mylib cd mylib yarn start ``` -That's it. You don't need to worry about setting up Typescript or Rollup or Jest or other plumbing. Just start editing `src/index.ts` and go! +That's it. You don't need to worry about setting up TypeScript or Rollup or Jest or other plumbing. Just start editing `src/index.ts` and go! Below is a list of commands you will probably find useful: @@ -81,14 +90,18 @@ The package is optimized and bundled with Rollup into multiple formats (CommonJS ### `npm test` or `yarn test` -Runs the test watcher (Jest) in an interactive mode. -By default, runs tests related to files changed since the last commit. +Runs your tests using Jest. ### `npm run lint` or `yarn lint` Runs Eslint with Prettier on .ts and .tsx files. If you want to customize eslint you can add an `eslint` block to your package.json, or you can run `yarn lint --write-file` and edit the generated `.eslintrc.js` file. +### `prepare` script + +Bundles and packages to the `dist` folder. +Runs automatically when you run either `npm publish` or `yarn publish`. The `prepare` script will run the equivalent of `npm run build` or `yarn build`. It will also be run if your module is installed as a git dependency (ie: `"mymodule": "github:myuser/mymodule#some-branch"`) so it can be depended on without checking the transpiled code into git. + ## Optimizations Aside from just bundling your module into different formats, TSDX comes with some optimizations for your convenience. They yield objectively better code and smaller bundle sizes. @@ -185,7 +198,7 @@ if (process.env.NODE_ENV !== 'production') { } ``` -**IMPORTANT:** To use `__DEV__` in TypeScript, you need add `declare var __DEV__: boolean` somewhere in your project's type path (e.g. `./types/index.d.ts`). +**IMPORTANT:** To use `__DEV__` in TypeScript, you need to add `declare var __DEV__: boolean` somewhere in your project's type path (e.g. `./types/index.d.ts`). ```ts // ./types/index.d.ts @@ -291,6 +304,9 @@ _TODO: Simple guide to host error codes to be completed_ ### Rollup +> **❗⚠️❗ Warning**:
+> These modifications will override the default behavior and configuration of TSDX. As such they can invalidate internal guarantees and assumptions. These types of changes can break internal behavior and can be very fragile against updates. Use with discretion! + TSDX uses Rollup under the hood. The defaults are solid for most packages (Formik uses the defaults!). However, if you do wish to alter the rollup configuration, you can do so by creating a file called `tsdx.config.js` at the root of your project like so: ```js @@ -309,22 +325,24 @@ The `options` object contains the following: export interface TsdxOptions { // path to file input: string; - // Safe name (for UMD) + // Name of package name: string; // JS target target: 'node' | 'browser'; // Module format - format: 'cjs' | 'umd' | 'esm'; + format: 'cjs' | 'umd' | 'esm' | 'system'; // Environment env: 'development' | 'production'; // Path to tsconfig file tsconfig?: string; - // Is opt-in invariant error extraction active? + // Is error extraction running? extractErrors?: boolean; // Is minifying? minify?: boolean; // Is this the very first rollup config (and thus should one-off metadata be extracted)? writeMeta?: boolean; + // Only transpile, do not type check (makes compilation faster) + transpileOnly?: boolean; } ``` @@ -357,17 +375,35 @@ module.exports = { ### Babel -You can add your own `.babelrc` to the root of your project and TSDX will **merge** it with its own babel transforms (which are mostly for optimization). +You can add your own `.babelrc` to the root of your project and TSDX will **merge** it with [its own Babel transforms](./src/babelPluginTsdx.ts) (which are mostly for optimization), putting any new presets and plugins at the end of its list. + +### Jest + +You can add your own `jest.config.js` to the root of your project and TSDX will **shallow merge** it with [its own Jest config](./src/createJestConfig.ts). + +### ESLint + +You can add your own `.eslintrc.js` to the root of your project and TSDX will **deep merge** it with [its own ESLint config](./src/createEslintConfig.ts). + +### `patch-package` + +If you still need more customizations, we recommend using [`patch-package`](https://github.com/ds300/patch-package) so you don't need to fork. +Keep in mind that these types of changes may be quite fragile against version updates. ## Inspiration -TSDX is ripped out of [Formik's](https://github.com/jaredpalmer/formik) build tooling. TSDX is very similar to [@developit/microbundle](https://github.com/developit/microbundle), but that is because Formik's Rollup configuration and Microbundle's internals have converged around similar plugins over the last year or so. +TSDX was originally ripped out of [Formik's](https://github.com/jaredpalmer/formik) build tooling. +TSDX has several similarities to [@developit/microbundle](https://github.com/developit/microbundle), but that is because Formik's Rollup configuration and Microbundle's internals had converged around similar plugins. + +### Comparison with Microbundle -### Comparison to Microbundle +Some key differences include: - TSDX includes out-of-the-box test running via Jest -- TSDX includes a bootstrap command and default package template -- TSDX is 100% TypeScript focused. While yes, TSDX does use Babel to run a few optimizations (related to treeshaking and lodash), it does not support custom babel configurations. +- TSDX includes out-of-the-box linting and formatting via ESLint and Prettier +- TSDX includes a bootstrap command with a few package templates +- TSDX allows for some lightweight customization +- TSDX is TypeScript focused, but also supports plain JavaScript - TSDX outputs distinct development and production builds (like React does) for CJS and UMD builds. This means you can include rich error messages and other dev-friendly goodies without sacrificing final bundle size. ## API Reference @@ -382,12 +418,17 @@ Usage $ tsdx watch [options] Options - -i, --entry Entry module(s) + -i, --entry Entry module --target Specify your target environment (default web) --name Specify name exposed in UMD builds --format Specify module format(s) (default cjs,esm) --tsconfig Specify your custom tsconfig path (default /tsconfig.json) --verbose Keep outdated console output in watch mode instead of clearing the screen + --onFirstSuccess Run a command on the first successful build + --onSuccess Run a command on a successful build + --onFailure Run a command on a failed build + --noClean Don't clean the dist folder + --transpileOnly Skip type checking -h, --help Displays this message Examples @@ -395,7 +436,12 @@ Examples $ tsdx watch --target node $ tsdx watch --name Foo $ tsdx watch --format cjs,esm,umd - $ tsdx build --tsconfig ./tsconfig.foo.json + $ tsdx watch --tsconfig ./tsconfig.foo.json + $ tsdx watch --noClean + $ tsdx watch --onFirstSuccess "echo The first successful build!" + $ tsdx watch --onSuccess "echo Successful build!" + $ tsdx watch --onFailure "echo The build failed!" + $ tsdx watch --transpileOnly ``` ### `tsdx build` @@ -408,12 +454,13 @@ Usage $ tsdx build [options] Options - -i, --entry Entry module(s) + -i, --entry Entry module --target Specify your target environment (default web) --name Specify name exposed in UMD builds --format Specify module format(s) (default cjs,esm) --extractErrors Opt-in to extracting invariant error codes --tsconfig Specify your custom tsconfig path (default /tsconfig.json) + --transpileOnly Skip type checking -h, --help Displays this message Examples @@ -423,11 +470,22 @@ Examples $ tsdx build --format cjs,esm,umd $ tsdx build --extractErrors $ tsdx build --tsconfig ./tsconfig.foo.json + $ tsdx build --transpileOnly ``` ### `tsdx test` -This runs Jest v24.x in watch mode. See [https://jestjs.io](https://jestjs.io) for options. If you are using the React template, jest uses the flag `--env=jsdom` by default. +This runs Jest, forwarding all CLI flags to it. See [https://jestjs.io](https://jestjs.io) for options. For example, if you would like to run in watch mode, you can run `tsdx test --watch`. So you could set up your `package.json` `scripts` like: + +```json +{ + "scripts": { + "test": "tsdx test", + "test:watch": "tsdx test --watch", + "test:coverage": "tsdx test --coverage" + } +} +``` ### `tsdx lint` @@ -441,16 +499,24 @@ Usage Options --fix Fixes fixable errors and warnings --ignore-pattern Ignore a pattern + --max-warnings Exits with non-zero error code if number of warnings exceed this number (default Infinity) --write-file Write the config file locally + --report-file Write JSON report to file locally -h, --help Displays this message Examples $ tsdx lint src $ tsdx lint src --fix $ tsdx lint src test --ignore-pattern test/foo.ts + $ tsdx lint src test --max-warnings 10 $ tsdx lint src --write-file + $ tsdx lint src --report-file report.json ``` +## Contributing + +Please see the [Contributing Guidelines](./CONTRIBUTING.md). + ## Author - [Jared Palmer](https://twitter.com/jaredpalmer) @@ -458,3 +524,143 @@ Examples ## License [MIT](https://oss.ninja/mit/jaredpalmer/) + +## Contributors ✨ + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Jared Palmer

📖 🎨 👀 🔧 ⚠️ 🚧 💻

swyx

🐛 💻 📖 🎨 🤔 🚇 🚧 👀

Jason Etcovitch

🐛 ⚠️

Sam Kvale

💻 ⚠️ 🐛 📖 👀 🤔 💬

Lucas Polito

💻 📖 💬

Steven Kalt

💻

Harry Hedger

🤔 📖 💻 💬

Arthur Denner

🐛 💻 💬

Carl

🤔 📖 💻 ⚠️ 💬

Loïc Mahieu

💻 ⚠️

Sebastian Sebald

📖 💻 ⚠️

Karl Horky

📖 🤔

James George

📖

Anton Gilgur

🚧 📖 💻 🐛 💡 🤔 💬 👀 ⚠️

Kyle Holmberg

💻 💡 ⚠️ 👀 💬

Sigurd Spieckermann

🐛 💻

Kristofer Giltvedt Selbekk

💻

Tomáš Ehrlich

🐛 💻

Kyle Johnson

🐛 💻

Etienne Dldc

🐛 💻 ⚠️

Florian Knop

🐛

Gonzalo D'Elia

💻

Alec Larson

💻 👀 🤔 💬

Justin Grant

🐛 🤔 💬

Jirat Ki.

💻 ⚠️ 🐛

Nate Moore

💻 🤔

Haz

📖

Basti Buck

💻 🐛

Pablo Saez

💻 🐛

Jake Gavin

🐛 💻

Grant Forrest

💻 ⚠️ 🐛

Sébastien Lorber

💻

Kirils Ladovs

📖

Enes Tüfekçi

💻 📖

Bogdan Chadkin

👀 💬 🤔

Daniel K.

💻 📖 ⚠️ 🤔 🐛

Quentin Sommer

📖

Hyan Mandian

💻 ⚠️

Sung M. Kim

🐛 💻

John Johnson

💻 📖

Jun Tomioka

💻 ⚠️

Leonardo Dino

💻 🐛

Honza Břečka

💻 🐛

Ward Loos

💻 🤔

Brian Bugh

💻 🐛

Cody Carse

📖

Josh Biddick

💻

Jose Albizures

💻 ⚠️ 🐛

Rahel Lüthy

📖

Michael Edelman

💻 🤔

Charlike Mike Reagent

👀 💻 🤔

Frederik Wessberg

💬

Elad Ossadon

💻 ⚠️ 🐛

Kevin Kipp

💻

Matija Folnovic

💻 📖

Andrew

💻

Ryan Castner

💻 ⚠️ 🤔

Yordis Prieto

💻

NCPhillips

📖

Arnaud Barré

💻 📖

Peter W

📖

Joe Flateau

💻 📖

H.John Choi

📖

Jon Stevens

📖 🤔 🐛

greenkeeper[bot]

🚇 💻

allcontributors[bot]

🚇 📖

dependabot[bot]

🚇 🛡️ 💻

GitHub

🚇

Eugene Samonenko

⚠️ 💡 💬 🤔

Joseph Wang

🐛

Kotaro Sugawara

🐛 💻

Semesse

💻

Bojan Mihelac

💻

Dan Dascalescu

📖

Yuriy Burychka

💻

Jesse Hoyos

💻

Mike Deverell

💻

Nick Hehr

💻 📖 💡

Bnaya Peretz

🐛 💻

Andres Alvarez

💻 📖 💡

Yaroslav K.

📖

Dragoș Străinu

🤔

George Varghese M.

💻 📖 ⚠️

Reinis Ivanovs

🤔 💬

Orta Therox

💬 📖

Martijn Saly

🐛

Alex Johansson

📖

hb-seb

💻

seungdols

🐛

Béré Cyriac

🐛

Dmitriy Serdtsev

🐛

Vladislav Moiseev

💻

Felix Mosheev

🐛

Ludovico Fischer

💻

Altrim Beqiri

🐛 💻 ⚠️

Tane Morgan

🐛 💻
+ + + + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..c39fb5316 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + testEnvironment: 'node', + testMatch: ['/**/*(*.)@(test).[tj]s?(x)'], + testPathIgnorePatterns: [ + '/node_modules/', // default + '/templates/', // don't run tests in the templates + '/test/.*/fixtures/', // don't run tests in fixtures + '/stage-.*/', // don't run tests in auto-generated (and auto-removed) test dirs + ], +}; diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index a7dfe48c7..000000000 --- a/netlify.toml +++ /dev/null @@ -1,5 +0,0 @@ -# example netlify.toml -[build] - command = "yarn run build" - publish = "build" - base = "website" diff --git a/package.json b/package.json index e560b6dc5..6b596eb54 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "tsdx", - "version": "0.9.2", + "version": "0.14.1", "author": "Jared Palmer ", "description": "Zero-config TypeScript package development", "license": "MIT", + "homepage": "https://github.com/formium/tsdx", "repository": { "type": "git", - "url": "git+https://github.com/jaredpalmer/tsdx.git" + "url": "git+https://github.com/formium/tsdx.git" }, "keywords": [ "react", @@ -15,7 +16,7 @@ "rollup" ], "bugs": { - "url": "https://github.com/jaredpalmer/tsdx/issues" + "url": "https://github.com/formium/tsdx/issues" }, "bin": { "tsdx": "./dist/index.js" @@ -24,97 +25,109 @@ "prepare": "tsc -p tsconfig.json", "build": "tsc -p tsconfig.json", "lint": "yarn build && yarn lint:post-build", - "lint:post-build": "node dist/index.js lint src test --ignore-pattern 'test/tests/lint'", - "test": "jest --config ./test/jest.config.json" + "lint:post-build": "node dist/index.js lint ./ --ignore-pattern 'test/e2e/fixtures/lint'", + "test": "yarn build && yarn test:post-build", + "test:post-build": "node dist/index.js test", + "start": "tsc -p tsconfig.json --watch", + "release": "np", + "deduplicate": "yarn-deduplicate -s fewer yarn.lock", + "deduplicate:check": "yarn-deduplicate -s fewer yarn.lock --list --fail" }, "files": [ "dist", "templates" ], + "engines": { + "node": ">=10" + }, "dependencies": { "@babel/core": "^7.4.4", "@babel/helper-module-imports": "^7.0.0", + "@babel/parser": "^7.11.5", "@babel/plugin-proposal-class-properties": "^7.4.4", - "@babel/plugin-transform-regenerator": "^7.4.5", - "@babel/polyfill": "^7.4.4", - "@babel/preset-env": "^7.4.4", - "@typescript-eslint/eslint-plugin": "^1.13.0", - "@typescript-eslint/parser": "^1.13.0", - "ansi-escapes": "^3.2.0", + "@babel/preset-env": "^7.11.0", + "@babel/traverse": "^7.11.5", + "@rollup/plugin-babel": "^5.1.0", + "@rollup/plugin-commonjs": "^11.0.0", + "@rollup/plugin-json": "^4.0.0", + "@rollup/plugin-node-resolve": "^9.0.0", + "@rollup/plugin-replace": "^2.2.1", + "@types/jest": "^25.2.1", + "@typescript-eslint/eslint-plugin": "^2.12.0", + "@typescript-eslint/parser": "^2.12.0", + "ansi-escapes": "^4.2.1", "asyncro": "^3.0.0", + "babel-eslint": "^10.0.3", "babel-plugin-annotate-pure-calls": "^0.4.0", "babel-plugin-dev-expression": "^0.2.1", "babel-plugin-macros": "^2.6.1", - "babel-plugin-transform-async-to-promises": "^0.8.14", + "babel-plugin-polyfill-regenerator": "^0.0.4", "babel-plugin-transform-rename-import": "^2.3.0", - "babel-traverse": "^6.26.0", - "babylon": "^6.18.0", - "camelcase": "^5.0.0", - "chalk": "^2.4.2", - "cross-env": "5.2.0", - "enquirer": "^2.3.0", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "enquirer": "^2.3.4", "eslint": "^6.1.0", "eslint-config-prettier": "^6.0.0", - "eslint-config-react-app": "^5.0.1", - "eslint-plugin-flowtype": "^4.2.0", + "eslint-config-react-app": "^5.2.1", + "eslint-plugin-flowtype": "^3.13.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-prettier": "^3.1.0", "eslint-plugin-react": "^7.14.3", - "eslint-plugin-react-hooks": "^1.7.0", - "execa": "^1.0.0", - "fs-extra": "^8.0.1", - "jest": "^24.8.0", - "jest-watch-typeahead": "^0.3.1", + "eslint-plugin-react-hooks": "^2.2.0", + "execa": "^4.0.3", + "fs-extra": "^9.0.0", + "jest": "^25.3.0", + "jest-watch-typeahead": "^0.5.0", "jpjs": "^1.2.1", "lodash.merge": "^4.6.2", - "mkdirp": "^0.5.1", - "ora": "^3.4.0", - "pascal-case": "^2.0.1", - "prettier": "^1.18.2", + "ora": "^4.0.3", + "pascal-case": "^3.1.1", + "prettier": "^1.19.1", "progress-estimator": "^0.2.2", - "rollup": "^1.12.0", - "rollup-plugin-babel": "^4.3.2", - "rollup-plugin-commonjs": "^10.0.0", - "rollup-plugin-json": "^4.0.0", - "rollup-plugin-node-resolve": "^5.0.0", - "rollup-plugin-replace": "^2.2.0", - "rollup-plugin-size-snapshot": "^0.8.0", - "rollup-plugin-sourcemaps": "^0.4.2", - "rollup-plugin-terser": "^4.0.4", - "rollup-plugin-typescript2": "^0.21.1", + "regenerator-runtime": "^0.13.7", + "rollup": "^1.32.1", + "rollup-plugin-sourcemaps": "^0.6.2", + "rollup-plugin-terser": "^5.1.2", + "rollup-plugin-typescript2": "^0.27.3", "sade": "^1.4.2", + "semver": "^7.1.1", + "shelljs": "^0.8.3", "tiny-glob": "^0.2.6", - "ts-jest": "^24.0.2", - "tslib": "^1.9.3" + "ts-jest": "^25.3.1", + "tslib": "^1.9.3", + "typescript": "^3.7.3" }, "devDependencies": { - "@types/ansi-escapes": "^4.0.0", - "@types/camelcase": "^5.2.0", - "@types/eslint": "^4.16.8", - "@types/execa": "^0.9.0", - "@types/fs-extra": "^8.0.0", - "@types/jest": "^24.0.15", - "@types/mkdirp": "^0.5.2", - "@types/ms": "^0.7.30", - "@types/node": "^12.0.2", - "@types/ora": "^3.2.0", - "@types/react": "^16.8.17", + "@types/eslint": "^6.1.2", + "@types/fs-extra": "^9.0.1", + "@types/lodash": "^4.14.161", + "@types/node": "^14.11.1", + "@types/react": "^16.9.11", "@types/rollup-plugin-json": "^3.0.2", - "@types/rollup-plugin-sourcemaps": "^0.4.2", - "husky": "^2.3.0", - "pretty-quick": "^1.10.0", - "ps-tree": "^1.2.0", + "@types/sade": "^1.6.0", + "@types/semver": "^7.1.0", + "@types/shelljs": "^0.8.5", + "@types/styled-components": "^5.0.1", + "autoprefixer": "^9.7.4", + "babel-plugin-replace-identifiers": "^0.1.1", + "cssnano": "^4.1.10", + "doctoc": "^1.4.0", + "husky": "^4.2.2", + "np": "^6.4.0", + "pretty-quick": "^2.0.0", "react": "^16.8.6", - "shelljs": "^0.8.3", - "typescript": "^3.6.2" - }, - "peerDependencies": { - "typescript": "3" + "react-dom": "^16.13.0", + "react-is": "^16.13.0", + "rollup-plugin-postcss": "^2.5.0", + "styled-components": "^5.0.1", + "tiny-invariant": "^1.1.0", + "tiny-warning": "^1.0.3", + "yarn-deduplicate": "^2.1.1" }, "husky": { "hooks": { - "pre-commit": "pretty-quick --staged --pattern '!test/tests/lint/**' && yarn lint" + "pre-commit": "pretty-quick --staged --pattern '!test/tests/lint/**' && yarn lint && yarn deduplicate:check && doctoc README.md" } }, "prettier": { diff --git a/src/babelPluginTsdx.ts b/src/babelPluginTsdx.ts index 628efe13f..86999b600 100644 --- a/src/babelPluginTsdx.ts +++ b/src/babelPluginTsdx.ts @@ -1,5 +1,5 @@ import { createConfigItem } from '@babel/core'; -import babelPlugin from 'rollup-plugin-babel'; +import { createBabelInputPluginFactory } from '@rollup/plugin-babel'; import merge from 'lodash.merge'; export const isTruthy = (obj?: any) => { @@ -10,7 +10,8 @@ export const isTruthy = (obj?: any) => { return obj.constructor !== Object || Object.keys(obj).length > 0; }; -const replacements = [{ original: 'lodash', replacement: 'lodash-es' }]; +// replace lodash with lodash-es, but not lodash/fp +const replacements = [{ original: 'lodash(?!/fp)', replacement: 'lodash-es' }]; export const mergeConfigItems = (type: any, ...configItemsToMerge: any[]) => { const mergedItems: any[] = []; @@ -47,7 +48,7 @@ export const createConfigItems = (type: any, items: any[]) => { }); }; -export const babelPluginTsdx = babelPlugin.custom((babelCore: any) => ({ +export const babelPluginTsdx = createBabelInputPluginFactory(() => ({ // Passed the plugin options. options({ custom: customOptions, ...pluginOptions }: any) { return { @@ -67,32 +68,22 @@ export const babelPluginTsdx = babelPlugin.custom((babelCore: any) => ({ // pragma: customOptions.jsx || 'h', // pragmaFrag: customOptions.jsxFragment || 'Fragment', // }, + { name: 'babel-plugin-macros' }, { name: 'babel-plugin-annotate-pure-calls' }, { name: 'babel-plugin-dev-expression' }, customOptions.format !== 'cjs' && { name: 'babel-plugin-transform-rename-import', replacements, }, - isTruthy(customOptions.defines) && { - name: 'babel-plugin-transform-replace-expressions', - replace: customOptions.defines, - }, { - name: 'babel-plugin-transform-async-to-promises', - inlineHelpers: true, - externalHelpers: true, + name: 'babel-plugin-polyfill-regenerator', + // don't pollute global env as this is being used in a library + method: 'usage-pure', }, { name: '@babel/plugin-proposal-class-properties', loose: true, }, - { - name: '@babel/plugin-transform-regenerator', - async: false, - }, - { - name: 'babel-plugin-macros', - }, isTruthy(customOptions.extractErrors) && { name: './errors/transformErrorMessages', }, @@ -100,28 +91,26 @@ export const babelPluginTsdx = babelPlugin.custom((babelCore: any) => ({ ); const babelOptions = config.options || {}; + babelOptions.presets = babelOptions.presets || []; - const envIdx = (babelOptions.presets || []).findIndex((preset: any) => + const presetEnvIdx = babelOptions.presets.findIndex((preset: any) => preset.file.request.includes('@babel/preset-env') ); - if (envIdx !== -1) { - const preset = babelOptions.presets[envIdx]; - babelOptions.presets[envIdx] = createConfigItem( + // if they use preset-env, merge their options with ours + if (presetEnvIdx !== -1) { + const presetEnv = babelOptions.presets[presetEnvIdx]; + babelOptions.presets[presetEnvIdx] = createConfigItem( [ - preset.file.resolved, + presetEnv.file.resolved, merge( { loose: true, targets: customOptions.targets, }, - preset.options, + presetEnv.options, { modules: false, - exclude: merge( - ['transform-async-to-generator', 'transform-regenerator'], - preset.options.exclude || [] - ), } ), ], @@ -130,15 +119,21 @@ export const babelPluginTsdx = babelPlugin.custom((babelCore: any) => ({ } ); } else { - babelOptions.presets = createConfigItems('preset', [ + // if no preset-env, add it & merge with their presets + const defaultPresets = createConfigItems('preset', [ { name: '@babel/preset-env', targets: customOptions.targets, modules: false, loose: true, - exclude: ['transform-async-to-generator', 'transform-regenerator'], }, ]); + + babelOptions.presets = mergeConfigItems( + 'preset', + defaultPresets, + babelOptions.presets + ); } // Merge babelrc & our plugins together diff --git a/src/constants.ts b/src/constants.ts index a83adb24d..f448e6cae 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,6 +2,7 @@ import { resolveApp } from './utils'; export const paths = { appPackageJson: resolveApp('package.json'), + tsconfigJson: resolveApp('tsconfig.json'), testsSetup: resolveApp('test/setupTests.ts'), appRoot: resolveApp('.'), appSrc: resolveApp('src'), @@ -9,4 +10,6 @@ export const paths = { appErrors: resolveApp('errors'), appDist: resolveApp('dist'), appConfig: resolveApp('tsdx.config.js'), + jestConfig: resolveApp('jest.config.js'), + progressEstimatorCache: resolveApp('node_modules/.cache/.progress-estimator'), }; diff --git a/src/createBuildConfigs.ts b/src/createBuildConfigs.ts new file mode 100644 index 000000000..261c4f755 --- /dev/null +++ b/src/createBuildConfigs.ts @@ -0,0 +1,89 @@ +import { RollupOptions, OutputOptions } from 'rollup'; +import * as fs from 'fs-extra'; +import { concatAllArray } from 'jpjs'; + +import { paths } from './constants'; +import { TsdxOptions, NormalizedOpts } from './types'; + +import { createRollupConfig } from './createRollupConfig'; + +// check for custom tsdx.config.js +let tsdxConfig = { + rollup(config: RollupOptions, _options: TsdxOptions): RollupOptions { + return config; + }, +}; + +if (fs.existsSync(paths.appConfig)) { + tsdxConfig = require(paths.appConfig); +} + +export async function createBuildConfigs( + opts: NormalizedOpts +): Promise> { + const allInputs = concatAllArray( + opts.input.map((input: string) => + createAllFormats(opts, input).map( + (options: TsdxOptions, index: number) => ({ + ...options, + // We want to know if this is the first run for each entryfile + // for certain plugins (e.g. css) + writeMeta: index === 0, + }) + ) + ) + ); + + return await Promise.all( + allInputs.map(async (options: TsdxOptions, index: number) => { + // pass the full rollup config to tsdx.config.js override + const config = await createRollupConfig(options, index); + return tsdxConfig.rollup(config, options); + }) + ); +} + +function createAllFormats( + opts: NormalizedOpts, + input: string +): [TsdxOptions, ...TsdxOptions[]] { + return [ + opts.format.includes('cjs') && { + ...opts, + format: 'cjs', + env: 'development', + input, + }, + opts.format.includes('cjs') && { + ...opts, + format: 'cjs', + env: 'production', + input, + }, + opts.format.includes('esm') && { ...opts, format: 'esm', input }, + opts.format.includes('umd') && { + ...opts, + format: 'umd', + env: 'development', + input, + }, + opts.format.includes('umd') && { + ...opts, + format: 'umd', + env: 'production', + input, + }, + opts.format.includes('system') && { + ...opts, + format: 'system', + env: 'development', + input, + }, + opts.format.includes('system') && { + ...opts, + format: 'system', + env: 'production', + input, + }, + ].filter(Boolean) as [TsdxOptions, ...TsdxOptions[]]; +} diff --git a/src/createEslintConfig.ts b/src/createEslintConfig.ts index 1b7ce51d3..14fbb0382 100644 --- a/src/createEslintConfig.ts +++ b/src/createEslintConfig.ts @@ -1,41 +1,56 @@ -import fs from 'fs'; +import fs from 'fs-extra'; import path from 'path'; import { CLIEngine } from 'eslint'; +import { PackageJson } from './types'; +import { getReactVersion } from './utils'; interface CreateEslintConfigArgs { + pkg: PackageJson; rootDir: string; writeFile: boolean; } -export function createEslintConfig({ +export async function createEslintConfig({ + pkg, rootDir, writeFile, -}: CreateEslintConfigArgs): CLIEngine.Options['baseConfig'] { +}: CreateEslintConfigArgs): Promise { + const isReactLibrary = Boolean(getReactVersion(pkg)); + const config = { extends: [ 'react-app', 'prettier/@typescript-eslint', 'plugin:prettier/recommended', ], + settings: { + react: { + // Fix for https://github.com/jaredpalmer/tsdx/issues/279 + version: isReactLibrary ? 'detect' : '999.999.999', + }, + }, }; - if (writeFile) { - const file = path.join(rootDir, '.eslintrc.js'); - if (fs.existsSync(file)) { + if (!writeFile) { + return config; + } + + const file = path.join(rootDir, '.eslintrc.js'); + try { + await fs.writeFile( + file, + `module.exports = ${JSON.stringify(config, null, 2)}`, + { flag: 'wx' } + ); + } catch (e) { + if (e.code === 'EEXIST') { console.error( 'Error trying to save the Eslint configuration file:', `${file} already exists.` ); } else { - try { - fs.writeFileSync( - file, - `module.exports = ${JSON.stringify(config, null, 2)}` - ); - } catch (e) { - console.error(e); - } + console.error(e); } - } - return config; + return config; + } } diff --git a/src/createJestConfig.ts b/src/createJestConfig.ts index 2a05fd78c..dfb922673 100644 --- a/src/createJestConfig.ts +++ b/src/createJestConfig.ts @@ -1,15 +1,20 @@ +import { Config } from '@jest/types'; + +export type JestConfigOptions = Partial; + export function createJestConfig( _: (relativePath: string) => void, rootDir: string -) { - const config = { +): JestConfigOptions { + const config: JestConfigOptions = { transform: { - '.(ts|tsx)': require.resolve('ts-jest/dist'), + '.(ts|tsx)$': require.resolve('ts-jest/dist'), + '.(js|jsx)$': require.resolve('babel-jest'), // jest's default }, transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - collectCoverageFrom: ['src/**/*.{ts,tsx}'], - testMatch: ['/**/*.(spec|test).{ts,tsx}'], + collectCoverageFrom: ['src/**/*.{ts,tsx,js,jsx}'], + testMatch: ['/**/*.(spec|test).{ts,tsx,js,jsx}'], testURL: 'http://localhost', rootDir, watchPlugins: [ diff --git a/src/createProgressEstimator.ts b/src/createProgressEstimator.ts new file mode 100644 index 000000000..e3eba5ce3 --- /dev/null +++ b/src/createProgressEstimator.ts @@ -0,0 +1,13 @@ +import fs from 'fs-extra'; + +import { paths } from './constants'; + +const progressEstimator = require('progress-estimator'); + +export async function createProgressEstimator() { + await fs.ensureDir(paths.progressEstimatorCache); + return progressEstimator({ + // All configuration keys are optional, but it's recommended to specify a storage location. + storagePath: paths.progressEstimatorCache, + }); +} diff --git a/src/createRollupConfig.ts b/src/createRollupConfig.ts index 816682e0c..2697c2df3 100644 --- a/src/createRollupConfig.ts +++ b/src/createRollupConfig.ts @@ -1,14 +1,18 @@ import { safeVariableName, safePackageName, external } from './utils'; import { paths } from './constants'; +import { RollupOptions } from 'rollup'; import { terser } from 'rollup-plugin-terser'; -import { DEFAULT_EXTENSIONS } from '@babel/core'; -// import babel from 'rollup-plugin-babel'; -import commonjs from 'rollup-plugin-commonjs'; -import json from 'rollup-plugin-json'; -import replace from 'rollup-plugin-replace'; -import resolve from 'rollup-plugin-node-resolve'; +import { DEFAULT_EXTENSIONS as DEFAULT_BABEL_EXTENSIONS } from '@babel/core'; +import commonjs from '@rollup/plugin-commonjs'; +import json from '@rollup/plugin-json'; +import replace from '@rollup/plugin-replace'; +import resolve, { + DEFAULTS as RESOLVE_DEFAULTS, +} from '@rollup/plugin-node-resolve'; import sourceMaps from 'rollup-plugin-sourcemaps'; import typescript from 'rollup-plugin-typescript2'; +import ts from 'typescript'; + import { extractErrors } from './errors/extractErrors'; import { babelPluginTsdx } from './babelPluginTsdx'; import { TsdxOptions } from './types'; @@ -20,8 +24,11 @@ const errorCodeOpts = { // shebang cache map thing because the transform only gets run once let shebang: any = {}; -export function createRollupConfig(opts: TsdxOptions) { - const findAndRecordErrorCodes = extractErrors({ +export async function createRollupConfig( + opts: TsdxOptions, + outputNum: number +): Promise { + const findAndRecordErrorCodes = await extractErrors({ ...errorCodeOpts, ...opts, }); @@ -39,16 +46,49 @@ export function createRollupConfig(opts: TsdxOptions) { .filter(Boolean) .join('.'); + const tsconfigPath = opts.tsconfig || paths.tsconfigJson; + // borrowed from https://github.com/facebook/create-react-app/pull/7248 + const tsconfigJSON = ts.readConfigFile(tsconfigPath, ts.sys.readFile).config; + // borrowed from https://github.com/ezolenko/rollup-plugin-typescript2/blob/42173460541b0c444326bf14f2c8c27269c4cb11/src/parse-tsconfig.ts#L48 + const tsCompilerOptions = ts.parseJsonConfigFileContent( + tsconfigJSON, + ts.sys, + './' + ).options; + return { // Tell Rollup the entry point to the package input: opts.input, // Tell Rollup which packages to ignore external: (id: string) => { - if (id === 'babel-plugin-transform-async-to-promises/helpers') { + // bundle in polyfills as TSDX can't (yet) ensure they're installed as deps + if (id.startsWith('regenerator-runtime')) { return false; } + return external(id); }, + // Rollup has treeshaking by default, but we can optimize it further... + treeshake: { + // We assume reading a property of an object never has side-effects. + // This means tsdx WILL remove getters and setters defined directly on objects. + // Any getters or setters defined on classes will not be effected. + // + // @example + // + // const foo = { + // get bar() { + // console.log('effect'); + // return 'bar'; + // } + // } + // + // const result = foo.bar; + // const illegalAccess = foo.quux.tooDeep; + // + // Punchline....Don't use getters and setters + propertyReadSideEffects: false, + }, // Establish Rollup output output: { // Set filenames of the consumer's package @@ -58,28 +98,8 @@ export function createRollupConfig(opts: TsdxOptions) { // Do not let Rollup call Object.freeze() on namespace import objects // (i.e. import * as namespaceImportObject from...) that are accessed dynamically. freeze: false, - // Do not let Rollup add a `__esModule: true` property when generating exports for non-ESM formats. - esModule: false, - // Rollup has treeshaking by default, but we can optimize it further... - treeshake: { - // We assume reading a property of an object never has side-effects. - // This means tsdx WILL remove getters and setters on objects. - // - // @example - // - // const foo = { - // get bar() { - // console.log('effect'); - // return 'bar'; - // } - // } - // - // const result = foo.bar; - // const illegalAccess = foo.quux.tooDeep; - // - // Punchline....Don't use getters and setters - propertyReadSideEffects: false, - }, + // Respect tsconfig esModuleInterop when setting __esModule. + esModule: Boolean(tsCompilerOptions?.esModuleInterop), name: opts.name || safeVariableName(opts.name), sourcemap: true, globals: { react: 'React', 'react-native': 'ReactNative' }, @@ -87,8 +107,8 @@ export function createRollupConfig(opts: TsdxOptions) { }, plugins: [ !!opts.extractErrors && { - transform(source: any) { - findAndRecordErrorCodes(source); + async transform(source: any) { + await findAndRecordErrorCodes(source); return source; }, }, @@ -98,12 +118,16 @@ export function createRollupConfig(opts: TsdxOptions) { 'main', opts.target !== 'node' ? 'browser' : undefined, ].filter(Boolean) as string[], + extensions: [...RESOLVE_DEFAULTS.extensions, '.jsx'], + }), + // all bundled external modules need to be converted from CJS to ESM + commonjs({ + // use a regex to make sure to include eventual hoisted packages + include: + opts.format === 'umd' + ? /\/node_modules\// + : /\/regenerator-runtime\//, }), - opts.format === 'umd' && - commonjs({ - // use a regex to make sure to include eventual hoisted packages - include: /\/node_modules\//, - }), json(), { // Custom plugin that removes shebang from code because newer @@ -126,10 +150,21 @@ export function createRollupConfig(opts: TsdxOptions) { }, }, typescript({ - typescript: require('typescript'), - cacheRoot: `./.rts2_cache_${opts.format}`, + typescript: ts, tsconfig: opts.tsconfig, tsconfigDefaults: { + exclude: [ + // all TS test files, regardless whether co-located or in test/ etc + '**/*.spec.ts', + '**/*.test.ts', + '**/*.spec.tsx', + '**/*.test.tsx', + // TS defaults below + 'node_modules', + 'bower_components', + 'jspm_packages', + paths.appDist, + ], compilerOptions: { sourceMap: true, declaration: true, @@ -138,29 +173,33 @@ export function createRollupConfig(opts: TsdxOptions) { }, tsconfigOverride: { compilerOptions: { + // TS -> esnext, then leave the rest to babel-preset-env target: 'esnext', + // don't output declarations more than once + ...(outputNum > 0 + ? { declaration: false, declarationMap: false } + : {}), }, }, + check: !opts.transpileOnly && outputNum === 0, + useTsconfigDeclarationDir: Boolean(tsCompilerOptions?.declarationDir), }), babelPluginTsdx({ exclude: 'node_modules/**', - extensions: [...DEFAULT_EXTENSIONS, 'ts', 'tsx'], + extensions: [...DEFAULT_BABEL_EXTENSIONS, 'ts', 'tsx'], passPerPreset: true, custom: { - targets: opts.target === 'node' ? { node: '8' } : undefined, + targets: opts.target === 'node' ? { node: '10' } : undefined, extractErrors: opts.extractErrors, format: opts.format, - // defines: opts.defines, }, + babelHelpers: 'bundled', }), opts.env !== undefined && replace({ 'process.env.NODE_ENV': JSON.stringify(opts.env), }), sourceMaps(), - // sizeSnapshot({ - // printInfo: false, - // }), shouldMinify && terser({ sourcemap: true, diff --git a/src/deprecated.ts b/src/deprecated.ts new file mode 100644 index 000000000..eae8ad3f6 --- /dev/null +++ b/src/deprecated.ts @@ -0,0 +1,34 @@ +import * as fs from 'fs-extra'; + +import { paths } from './constants'; + +/* + This was originally needed because the default + tsconfig.compilerOptions.rootDir was set to './' instead of './src'. + Now that it's set to './src', this is now deprecated. + To ensure a stable upgrade path for users, leave the warning in for + 6 months - 1 year, then change it to an error in a breaking bump and leave + that in for some time too. +*/ +export async function moveTypes() { + const appDistSrc = paths.appDist + '/src'; + + const pathExists = await fs.pathExists(appDistSrc); + if (!pathExists) return; + + // see note above about deprecation window + console.warn( + '[tsdx]: Your rootDir is currently set to "./". Please change your ' + + 'rootDir to "./src".\n' + + 'TSDX has deprecated setting tsconfig.compilerOptions.rootDir to ' + + '"./" as it caused buggy output for declarationMaps and more.\n' + + 'You may also need to change your include to remove "test", which also ' + + 'caused declarations to be unnecessarily created for test files.' + ); + + // Move the type declarations to the base of the ./dist folder + await fs.copy(appDistSrc, paths.appDist, { + overwrite: true, + }); + await fs.remove(appDistSrc); +} diff --git a/src/env.d.ts b/src/env.d.ts index 2ee987ed1..9cd3924d8 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -1,27 +1,18 @@ -declare module 'asyncro'; -declare module 'enquirer'; -declare module 'jpjs'; -declare module 'ora'; -declare module 'sade'; -declare module 'tiny-glob/sync'; -declare module 'ansi-escapes'; -declare module 'eslint-config-react-app'; +declare module 'asyncro'; // doesn't have types (unmerged 2+ year old PR: https://github.com/developit/asyncro/pull/10) +declare module 'enquirer'; // doesn't have types for Input or Select +declare module 'jpjs'; // doesn't ship types (written in TS though) +declare module 'tiny-glob/sync'; // /sync isn't typed (but maybe we can use async?) // Patch Babel // @see line 226 of https://unpkg.com/@babel/core@7.4.4/lib/index.js declare module '@babel/core' { export const DEFAULT_EXTENSIONS: string[]; - export function createConfigItem(boop: any[], options: any) {} + export function createConfigItem(boop: any[], options: any): any[]; } // Rollup plugins -declare module '@jaredpalmer/rollup-plugin-preserve-shebang'; -declare module 'rollup-plugin-babel'; -declare module 'rollup-plugin-size-snapshot'; declare module 'rollup-plugin-terser'; -declare module 'camelcase'; -declare module 'babel-traverse'; -declare module 'babylon'; +declare module '@babel/traverse'; declare module '@babel/helper-module-imports'; declare module 'lodash.merge'; diff --git a/src/errors/evalToString.ts b/src/errors/evalToString.ts index da9e68192..fb819ae26 100644 --- a/src/errors/evalToString.ts +++ b/src/errors/evalToString.ts @@ -1,6 +1,15 @@ +// largely borrowed from https://github.com/facebook/react/blob/8b2d3783e58d1acea53428a10d2035a8399060fe/scripts/shared/evalToString.js +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + export function evalToString(ast: any): string { switch (ast.type) { case 'StringLiteral': + case 'Literal': // ESLint return ast.value; case 'BinaryExpression': // `+` if (ast.operator !== '+') { diff --git a/src/errors/extractErrors.ts b/src/errors/extractErrors.ts index 878a2b74c..9e6ca168e 100644 --- a/src/errors/extractErrors.ts +++ b/src/errors/extractErrors.ts @@ -1,3 +1,4 @@ +// largely borrowed from https://github.com/facebook/react/blob/8b2d3783e58d1acea53428a10d2035a8399060fe/scripts/error-codes/extract-errors.js /** * Copyright (c) Facebook, Inc. and its affiliates. * @@ -5,17 +6,17 @@ * LICENSE file in the root directory of this source tree. */ import fs from 'fs-extra'; -import * as babylon from 'babylon'; -import traverse from 'babel-traverse'; +import { parse, ParserOptions } from '@babel/parser'; +import traverse from '@babel/traverse'; import { invertObject } from './invertObject'; import { evalToString } from './evalToString'; import { paths } from '../constants'; import { safeVariableName } from '../utils'; -import pascalCase from 'pascal-case'; +import { pascalCase } from 'pascal-case'; -const babylonOptions = { +const babelParserOptions: ParserOptions = { sourceType: 'module', - // As a parser, babylon has its own options and we can't directly + // As a parser, @babel/parser has its own options and we can't directly // import/require a babel preset. It should be kept **the same** as // the `babel-plugin-syntax-*` ones specified in // https://github.com/facebook/fbjs/blob/master/packages/babel-preset-fbjs/configure.js @@ -26,9 +27,9 @@ const babylonOptions = { 'trailingFunctionCommas', 'objectRestSpread', ], -}; +} as ParserOptions; // workaround for trailingFunctionCommas syntax -export function extractErrors(opts: any) { +export async function extractErrors(opts: any) { if (!opts || !('errorMapFilePath' in opts)) { throw new Error( 'Missing options. Ensure you pass an object with `errorMapFilePath`.' @@ -42,10 +43,10 @@ export function extractErrors(opts: any) { const errorMapFilePath = opts.errorMapFilePath; let existingErrorMap: any; try { - // Using `fs.readFileSync` instead of `require` here, because `require()` + // Using `fs.readFile` instead of `require` here, because `require()` // calls are cached, and the cache map is not properly invalidated after // file changes. - existingErrorMap = JSON.parse(fs.readFileSync(errorMapFilePath, 'utf8')); + existingErrorMap = JSON.parse(await fs.readFile(errorMapFilePath, 'utf8')); } catch (e) { existingErrorMap = {}; } @@ -64,7 +65,7 @@ export function extractErrors(opts: any) { existingErrorMap = invertObject(existingErrorMap); function transform(source: string) { - const ast = babylon.parse(source, babylonOptions); + const ast = parse(source, babelParserOptions); traverse(ast, { CallExpression: { @@ -89,20 +90,20 @@ export function extractErrors(opts: any) { existingErrorMap[errorMsgLiteral] = '' + currentID++; } - function flush(cb?: any) { + async function flush() { const prettyName = pascalCase(safeVariableName(opts.name)); // Ensure that the ./src/errors directory exists or create it - fs.ensureDirSync(paths.appErrors); + await fs.ensureDir(paths.appErrors); // Output messages to ./errors/codes.json - fs.writeFileSync( + await fs.writeFile( errorMapFilePath, JSON.stringify(invertObject(existingErrorMap), null, 2) + '\n', 'utf-8' ); // Write the error files, unless they already exist - fs.writeFileSync( + await fs.writeFile( paths.appErrors + '/ErrorDev.js', ` function ErrorDev(message) { @@ -111,12 +112,12 @@ function ErrorDev(message) { return error; } -export default ErrorDev; +export default ErrorDev; `, 'utf-8' ); - fs.writeFileSync( + await fs.writeFile( paths.appErrors + '/ErrorProd.js', ` function ErrorProd(code) { @@ -138,8 +139,8 @@ export default ErrorProd; ); } - return function extractErrors(source: any) { + return async function extractErrors(source: any) { transform(source); - flush(); + await flush(); }; } diff --git a/src/errors/invertObject.ts b/src/errors/invertObject.ts index 23fd54f53..13959e9e9 100644 --- a/src/errors/invertObject.ts +++ b/src/errors/invertObject.ts @@ -1,3 +1,12 @@ +// largely borrowed from https://github.com/facebook/react/blob/8b2d3783e58d1acea53428a10d2035a8399060fe/scripts/error-codes/invertObject.js + +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + /** * turns * { 'MUCH ERROR': '0', 'SUCH WRONG': '1' } @@ -7,9 +16,7 @@ type Dict = { [key: string]: any }; -export function invertObject( - targetObj: Dict /* : ErrorMap */ -) /* : ErrorMap */ { +export function invertObject(targetObj: Dict) { const result: Dict = {}; const mapKeys = Object.keys(targetObj); diff --git a/src/errors/transformErrorMessages.ts b/src/errors/transformErrorMessages.ts index 08d144234..acea7088a 100644 --- a/src/errors/transformErrorMessages.ts +++ b/src/errors/transformErrorMessages.ts @@ -1,3 +1,11 @@ +// largely borrowed from https://github.com/facebook/react/blob/2c8832075b05009bd261df02171bf9888ac76350/scripts/error-codes/transform-error-messages.js +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import fs from 'fs'; import { invertObject } from './invertObject'; import { evalToString } from './evalToString'; diff --git a/src/getInstallCmd.ts b/src/getInstallCmd.ts index 0e74f7013..093f8f28a 100644 --- a/src/getInstallCmd.ts +++ b/src/getInstallCmd.ts @@ -4,13 +4,13 @@ let cmd: InstallCommand; export type InstallCommand = 'yarn' | 'npm'; -export default function getInstallCmd(): InstallCommand { +export default async function getInstallCmd(): Promise { if (cmd) { return cmd; } try { - execa.sync('yarnpkg', ['--version']); + await execa('yarnpkg', ['--version']); cmd = 'yarn'; } catch (e) { cmd = 'npm'; diff --git a/src/index.ts b/src/index.ts index 44ff41270..c9b0399eb 100755 --- a/src/index.ts +++ b/src/index.ts @@ -12,58 +12,51 @@ import { } from 'rollup'; import asyncro from 'asyncro'; import chalk from 'chalk'; -import util from 'util'; import * as fs from 'fs-extra'; -import jest from 'jest'; +import * as jest from 'jest'; import { CLIEngine } from 'eslint'; import logError from './logError'; import path from 'path'; -import mkdirp from 'mkdirp'; import execa from 'execa'; +import shell from 'shelljs'; import ora from 'ora'; +import semver from 'semver'; import { paths } from './constants'; import * as Messages from './messages'; -import { createRollupConfig } from './createRollupConfig'; -import { createJestConfig } from './createJestConfig'; +import { createBuildConfigs } from './createBuildConfigs'; +import { createJestConfig, JestConfigOptions } from './createJestConfig'; import { createEslintConfig } from './createEslintConfig'; -import { resolveApp, safePackageName, clearConsole } from './utils'; +import { + resolveApp, + safePackageName, + clearConsole, + getNodeEngineRequirement, +} from './utils'; import { concatAllArray } from 'jpjs'; import getInstallCmd from './getInstallCmd'; import getInstallArgs from './getInstallArgs'; import { Input, Select } from 'enquirer'; -import { TsdxOptions } from './types'; +import { + PackageJson, + WatchOpts, + BuildOpts, + ModuleFormat, + NormalizedOpts, +} from './types'; +import { createProgressEstimator } from './createProgressEstimator'; +import { templates } from './templates'; +import { composePackageJson } from './templates/utils'; +import * as deprecated from './deprecated'; const pkg = require('../package.json'); -const createLogger = require('progress-estimator'); -// All configuration keys are optional, but it's recommended to specify a storage location. -// Learn more about configuration options below. -const logger = createLogger({ - storagePath: path.join(__dirname, '.progress-estimator'), -}); const prog = sade('tsdx'); -let appPackageJson: { - name: string; - source?: string; - jest?: any; - eslint?: any; -}; +let appPackageJson: PackageJson; try { - appPackageJson = fs.readJSONSync(resolveApp('package.json')); + appPackageJson = fs.readJSONSync(paths.appPackageJson); } catch (e) {} -// check for custom tsdx.config.js -let tsdxConfig = { - rollup(config: any, _options: any) { - return config; - }, -}; - -if (fs.existsSync(paths.appConfig)) { - tsdxConfig = require(paths.appConfig); -} - export const isDir = (name: string) => fs .stat(name) @@ -81,89 +74,40 @@ async function jsOrTs(filename: string) { ? '.ts' : (await isFile(resolveApp(filename + '.tsx'))) ? '.tsx' + : (await isFile(resolveApp(filename + '.jsx'))) + ? '.jsx' : '.js'; return resolveApp(`${filename}${extension}`); } -async function getInputs(entries: string[], source?: string) { - let inputs: any[] = []; - let stub: any[] = []; - stub - .concat( - entries && entries.length - ? entries - : (source && resolveApp(source)) || - ((await isDir(resolveApp('src'))) && (await jsOrTs('src/index'))) - ) - .map(file => glob(file)) - .forEach(input => inputs.push(input)); - - return concatAllArray(inputs); -} - -function createBuildConfigs( - opts: any -): Array { +async function getInputs( + entries?: string | string[], + source?: string +): Promise { return concatAllArray( - opts.input.map((input: string) => - [ - opts.format.includes('cjs') && { - ...opts, - format: 'cjs', - env: 'development', - input, - }, - opts.format.includes('cjs') && { - ...opts, - format: 'cjs', - env: 'production', - input, - }, - opts.format.includes('esm') && { ...opts, format: 'esm', input }, - opts.format.includes('umd') && { - ...opts, - format: 'umd', - env: 'development', - input, - }, - opts.format.includes('umd') && { - ...opts, - format: 'umd', - env: 'production', - input, - }, - ] - .filter(Boolean) - .map((options: TsdxOptions, index: number) => ({ - ...options, - // We want to know if this is the first run for each entryfile - // for certain plugins (e.g. css) - writeMeta: index === 0, - })) - ) - ).map((options: TsdxOptions) => - // pass the full rollup config to tsdx.config.js override - tsdxConfig.rollup(createRollupConfig(options), options) + ([] as any[]) + .concat( + entries && entries.length + ? entries + : (source && resolveApp(source)) || + ((await isDir(resolveApp('src'))) && (await jsOrTs('src/index'))) + ) + .map(file => glob(file)) ); } -async function moveTypes() { - try { - // Move the typescript types to the base of the ./dist folder - await fs.copy(paths.appDist + '/src', paths.appDist, { - overwrite: true, - }); - await fs.remove(paths.appDist + '/src'); - } catch (e) {} -} - prog .version(pkg.version) .command('create ') .describe('Create a new package with TSDX') .example('create mypackage') - .option('--template', 'Specify a template. Allowed choices: [basic, react]') + .option( + '--template', + `Specify a template. Allowed choices: [${Object.keys(templates).join( + ', ' + )}]` + ) .example('create --template react mypackage') .action(async (pkg: string, opts: any) => { console.log( @@ -182,33 +126,34 @@ prog // Helper fn to prompt the user for a different // folder name if one already exists async function getProjectPath(projectPath: string): Promise { - if (fs.existsSync(projectPath)) { - bootSpinner.fail(`Failed to create ${chalk.bold.red(pkg)}`); - const prompt = new Input({ - message: `A folder named ${chalk.bold.red( - pkg - )} already exists! ${chalk.bold('Choose a different name')}`, - initial: pkg + '-1', - result: (v: string) => v.trim(), - }); - pkg = await prompt.run(); - projectPath = fs.realpathSync(process.cwd()) + '/' + pkg; - bootSpinner.start(`Creating ${chalk.bold.green(pkg)}...`); - return getProjectPath(projectPath); // recursion! - } else { + const exists = await fs.pathExists(projectPath); + if (!exists) { return projectPath; } + + bootSpinner.fail(`Failed to create ${chalk.bold.red(pkg)}`); + const prompt = new Input({ + message: `A folder named ${chalk.bold.red( + pkg + )} already exists! ${chalk.bold('Choose a different name')}`, + initial: pkg + '-1', + result: (v: string) => v.trim(), + }); + + pkg = await prompt.run(); + projectPath = (await fs.realpath(process.cwd())) + '/' + pkg; + bootSpinner.start(`Creating ${chalk.bold.green(pkg)}...`); + return await getProjectPath(projectPath); // recursion! } try { // get the project path - let projectPath = await getProjectPath( - fs.realpathSync(process.cwd()) + '/' + pkg - ); + const realPath = await fs.realpath(process.cwd()); + let projectPath = await getProjectPath(realPath + '/' + pkg); const prompt = new Select({ message: 'Choose a template', - choices: ['basic', 'react'], + choices: Object.keys(templates), }); if (opts.template) { @@ -235,62 +180,70 @@ prog path.resolve(projectPath, './gitignore'), path.resolve(projectPath, './.gitignore') ); + + // update license year and author + let license: string = await fs.readFile( + path.resolve(projectPath, 'LICENSE'), + { encoding: 'utf-8' } + ); + + license = license.replace(//, `${new Date().getFullYear()}`); + + // attempt to automatically derive author name + let author = getAuthorName(); + + if (!author) { + bootSpinner.stop(); + const licenseInput = new Input({ + name: 'author', + message: 'Who is the package author?', + }); + author = await licenseInput.run(); + setAuthorName(author); + bootSpinner.start(); + } + + license = license.replace(//, author.trim()); + + await fs.writeFile(path.resolve(projectPath, 'LICENSE'), license, { + encoding: 'utf-8', + }); + + const templateConfig = templates[template as keyof typeof templates]; + const generatePackageJson = composePackageJson(templateConfig); + // Install deps process.chdir(projectPath); const safeName = safePackageName(pkg); - const pkgJson = { - name: safeName, - version: '0.1.0', - main: 'dist/index.js', - module: `dist/${safeName}.esm.js`, - typings: 'dist/index.d.ts', - files: ['dist'], - scripts: { - start: 'tsdx watch', - build: 'tsdx build', - test: template === 'react' ? 'tsdx test --env=jsdom' : 'tsdx test', - lint: 'tsdx lint', - }, - peerDependencies: template === 'react' ? { react: '>=16' } : {}, - husky: { - hooks: { - 'pre-commit': 'tsdx lint', - }, - }, - prettier: { - printWidth: 80, - semi: true, - singleQuote: true, - trailingComma: 'es5', - }, - }; + const pkgJson = generatePackageJson({ name: safeName, author }); + + const nodeVersionReq = getNodeEngineRequirement(pkgJson); + if ( + nodeVersionReq && + !semver.satisfies(process.version, nodeVersionReq) + ) { + bootSpinner.fail(Messages.incorrectNodeVersion(nodeVersionReq)); + process.exit(1); + } + await fs.outputJSON(path.resolve(projectPath, 'package.json'), pkgJson); bootSpinner.succeed(`Created ${chalk.bold.green(pkg)}`); - Messages.start(pkg); + await Messages.start(pkg); } catch (error) { bootSpinner.fail(`Failed to create ${chalk.bold.red(pkg)}`); logError(error); process.exit(1); } - let deps = ['@types/jest', 'husky', 'tsdx', 'tslib', 'typescript'].sort(); - - if (template === 'react') { - deps = [ - ...deps, - '@types/react', - '@types/react-dom', - 'react', - 'react-dom', - ].sort(); - } + const templateConfig = templates[template as keyof typeof templates]; + const { dependencies: deps } = templateConfig; - const installSpinner = ora(Messages.installing(deps)).start(); + const installSpinner = ora(Messages.installing(deps.sort())).start(); try { - const cmd = getInstallCmd(); + const cmd = await getInstallCmd(); await execa(cmd, getInstallArgs(cmd, deps)); installSpinner.succeed('Installed dependencies'); - console.log(Messages.start(pkg)); + console.log(await Messages.start(pkg)); } catch (error) { installSpinner.fail('Failed to install dependencies'); logError(error); @@ -301,9 +254,9 @@ prog prog .command('watch') .describe('Rebuilds on any change') - .option('--entry, -i', 'Entry module(s)') + .option('--entry, -i', 'Entry module') .example('watch --entry src/foo.tsx') - .option('--target', 'Specify your target environment', 'web') + .option('--target', 'Specify your target environment', 'browser') .example('watch --target node') .option('--name', 'Specify name exposed in UMD builds') .example('watch --name Foo') @@ -314,19 +267,56 @@ prog 'Keep outdated console output in watch mode instead of clearing the screen' ) .example('watch --verbose') + .option('--noClean', "Don't clean the dist folder") + .example('watch --noClean') .option('--tsconfig', 'Specify custom tsconfig path') .example('watch --tsconfig ./tsconfig.foo.json') + .option('--onFirstSuccess', 'Run a command on the first successful build') + .example('watch --onFirstSuccess "echo The first successful build!"') + .option('--onSuccess', 'Run a command on a successful build') + .example('watch --onSuccess "echo Successful build!"') + .option('--onFailure', 'Run a command on a failed build') + .example('watch --onFailure "The build failed!"') + .option('--transpileOnly', 'Skip type checking') + .example('watch --transpileOnly') .option('--extractErrors', 'Extract invariant errors to ./errors/codes.json.') - .example('build --extractErrors') - .action(async (dirtyOpts: any) => { + .example('watch --extractErrors') + .action(async (dirtyOpts: WatchOpts) => { const opts = await normalizeOpts(dirtyOpts); - const buildConfigs = createBuildConfigs(opts); - await ensureDistFolder(); + const buildConfigs = await createBuildConfigs(opts); + if (!opts.noClean) { + await cleanDistFolder(); + } if (opts.format.includes('cjs')) { await writeCjsEntryFile(opts.name); } + + type Killer = execa.ExecaChildProcess | null; + + let firstTime = true; + let successKiller: Killer = null; + let failureKiller: Killer = null; + + function run(command?: string) { + if (!command) { + return null; + } + + const [exec, ...args] = command.split(' '); + return execa(exec, args, { + stdio: 'inherit', + }); + } + + function killHooks() { + return Promise.all([ + successKiller ? successKiller.kill('SIGTERM') : null, + failureKiller ? failureKiller.kill('SIGTERM') : null, + ]); + } + const spinner = ora().start(); - await watch( + watch( (buildConfigs as RollupWatchOptions[]).map(inputOptions => ({ watch: { silent: true, @@ -336,6 +326,9 @@ prog ...inputOptions, })) ).on('event', async event => { + // clear previous onSuccess/onFailure hook processes so they don't pile up + await killHooks(); + if (event.code === 'START') { if (!opts.verbose) { clearConsole(); @@ -345,18 +338,23 @@ prog if (event.code === 'ERROR') { spinner.fail(chalk.bold.red('Failed to compile')); logError(event.error); - } - if (event.code === 'FATAL') { - spinner.fail(chalk.bold.red('Failed to compile')); - logError(event.error); + failureKiller = run(opts.onFailure); } if (event.code === 'END') { spinner.succeed(chalk.bold.green('Compiled successfully')); console.log(` ${chalk.dim('Watching for changes')} `); + try { - await moveTypes(); + await deprecated.moveTypes(); + + if (firstTime && opts.onFirstSuccess) { + firstTime = false; + run(opts.onFirstSuccess); + } else { + successKiller = run(opts.onSuccess); + } } catch (_error) {} } }); @@ -365,9 +363,9 @@ prog prog .command('build') .describe('Build your project once and exit') - .option('--entry, -i', 'Entry module(s)') + .option('--entry, -i', 'Entry module') .example('build --entry src/foo.tsx') - .option('--target', 'Specify your target environment', 'web') + .option('--target', 'Specify your target environment', 'browser') .example('build --target node') .option('--name', 'Specify name exposed in UMD builds') .example('build --name Foo') @@ -375,6 +373,8 @@ prog .example('build --format cjs,esm') .option('--tsconfig', 'Specify custom tsconfig path') .example('build --tsconfig ./tsconfig.foo.json') + .option('--transpileOnly', 'Skip type checking') + .example('build --transpileOnly') .option( '--extractErrors', 'Extract errors to ./errors/codes.json and provide a url for decoding.' @@ -382,10 +382,11 @@ prog .example( 'build --extractErrors=https://reactjs.org/docs/error-decoder.html?invariant=' ) - .action(async (dirtyOpts: any) => { + .action(async (dirtyOpts: BuildOpts) => { const opts = await normalizeOpts(dirtyOpts); - const buildConfigs = createBuildConfigs(opts); - await ensureDistFolder(); + const buildConfigs = await createBuildConfigs(opts); + await cleanDistFolder(); + const logger = await createProgressEstimator(); if (opts.format.includes('cjs')) { const promise = writeCjsEntryFile(opts.name).catch(logError); logger(promise, 'Creating entry file'); @@ -397,11 +398,13 @@ prog async (inputOptions: RollupOptions & { output: OutputOptions }) => { let bundle = await rollup(inputOptions); await bundle.write(inputOptions.output); - await moveTypes(); } ) .catch((e: any) => { throw e; + }) + .then(async () => { + await deprecated.moveTypes(); }); logger(promise, 'Building modules'); await promise; @@ -411,7 +414,7 @@ prog } }); -async function normalizeOpts(opts: any) { +async function normalizeOpts(opts: WatchOpts): Promise { return { ...opts, name: opts.name || appPackageJson.name, @@ -421,12 +424,12 @@ async function normalizeOpts(opts: any) { return 'esm'; } return format; - }), + }) as [ModuleFormat, ...ModuleFormat[]], }; } -function ensureDistFolder() { - return util.promisify(mkdirp)(resolveApp('dist')); +async function cleanDistFolder() { + await fs.remove(paths.appDist); } function writeCjsEntryFile(name: string) { @@ -440,15 +443,46 @@ if (process.env.NODE_ENV === 'production') { ${baseLine}.cjs.development.js') } `; - return fs.writeFile(resolveApp(`./dist/index.js`), contents); + return fs.outputFile(path.join(paths.appDist, 'index.js'), contents); +} + +function getAuthorName() { + let author = ''; + + author = shell + .exec('npm config get init-author-name', { silent: true }) + .stdout.trim(); + if (author) return author; + + author = shell + .exec('git config --global user.name', { silent: true }) + .stdout.trim(); + if (author) { + setAuthorName(author); + return author; + } + + author = shell + .exec('npm config get init-author-email', { silent: true }) + .stdout.trim(); + if (author) return author; + + author = shell + .exec('git config --global user.email', { silent: true }) + .stdout.trim(); + if (author) return author; + + return author; +} + +function setAuthorName(author: string) { + shell.exec(`npm config set init-author-name "${author}"`, { silent: true }); } prog .command('test') - .describe( - 'Run jest test runner in watch mode. Passes through all flags directly to Jest' - ) - .action(async () => { + .describe('Run jest test runner. Passes through all flags directly to Jest') + .action(async (opts: { config?: string }) => { // Do this as the first thing so that any code reading it knows the right env. process.env.BABEL_ENV = 'test'; process.env.NODE_ENV = 'test'; @@ -460,15 +494,42 @@ prog }); const argv = process.argv.slice(2); + let jestConfig: JestConfigOptions = { + ...createJestConfig( + relativePath => path.resolve(__dirname, '..', relativePath), + opts.config ? path.dirname(opts.config) : paths.appRoot + ), + ...appPackageJson.jest, + }; + + // Allow overriding with jest.config + const defaultPathExists = await fs.pathExists(paths.jestConfig); + if (opts.config || defaultPathExists) { + const jestConfigPath = resolveApp(opts.config || paths.jestConfig); + const jestConfigContents: JestConfigOptions = require(jestConfigPath); + jestConfig = { ...jestConfig, ...jestConfigContents }; + } + + // if custom path, delete the arg as it's already been merged + if (opts.config) { + let configIndex = argv.indexOf('--config'); + if (configIndex !== -1) { + // case of "--config path", delete both args + argv.splice(configIndex, 2); + } else { + // case of "--config=path", only one arg to delete + const configRegex = /--config=.+/; + configIndex = argv.findIndex(arg => arg.match(configRegex)); + if (configIndex !== -1) { + argv.splice(configIndex, 1); + } + } + } argv.push( '--config', JSON.stringify({ - ...createJestConfig( - relativePath => path.resolve(__dirname, '..', relativePath), - paths.appRoot - ), - ...appPackageJson.jest, + ...jestConfig, }) ); @@ -484,30 +545,48 @@ prog .example('lint src test --fix') .option('--ignore-pattern', 'Ignore a pattern') .example('lint src test --ignore-pattern test/foobar.ts') + .option( + '--max-warnings', + 'Exits with non-zero error code if number of warnings exceed this number', + Infinity + ) + .example('lint src test --max-warnings 10') .option('--write-file', 'Write the config file locally') .example('lint --write-file') + .option('--report-file', 'Write JSON report to file locally') + .example('lint --report-file eslint-report.json') .action( async (opts: { fix: boolean; 'ignore-pattern': string; 'write-file': boolean; + 'report-file': string; + 'max-warnings': number; _: string[]; }) => { if (opts['_'].length === 0 && !opts['write-file']) { - prog.help('lint'); - console.log(chalk.red('No input files specified.')); - process.exit(1); + const defaultInputs = ['src', 'test'].filter(fs.existsSync); + opts['_'] = defaultInputs; + console.log( + chalk.yellow( + `Defaulting to "tsdx lint ${defaultInputs.join(' ')}"`, + '\nYou can override this in the package.json scripts, like "lint": "tsdx lint src otherDir"' + ) + ); } + const config = await createEslintConfig({ + pkg: appPackageJson, + rootDir: paths.appRoot, + writeFile: opts['write-file'], + }); + const cli = new CLIEngine({ baseConfig: { - ...createEslintConfig({ - rootDir: paths.appRoot, - writeFile: opts['write-file'], - }), + ...config, ...appPackageJson.eslint, }, - extensions: ['.ts', '.tsx'], + extensions: ['.ts', '.tsx', '.js', '.jsx'], fix: opts.fix, ignorePattern: opts['ignore-pattern'], }); @@ -516,9 +595,18 @@ prog CLIEngine.outputFixes(report); } console.log(cli.getFormatter()(report.results)); + if (opts['report-file']) { + await fs.outputFile( + opts['report-file'], + cli.getFormatter('json')(report.results) + ); + } if (report.errorCount) { process.exit(1); } + if (report.warningCount > opts['max-warnings']) { + process.exit(1); + } } ); diff --git a/src/messages.ts b/src/messages.ts index 5a949a96e..627aeee88 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -11,7 +11,7 @@ export const help = function() { return ` Only ${chalk.green('')} is required. If you have any problems, do not hesitate to file an issue: - ${chalk.cyan('https://github.com/jaredpalmer/tsdx/issues/new')} + ${chalk.cyan('https://github.com/formium/tsdx/issues/new')} `; }; @@ -60,8 +60,8 @@ Creating ${chalk.bold(chalk.green(projectName))}... `; }; -export const start = function(projectName: string) { - const cmd = getInstallCmd(); +export const start = async function(projectName: string) { + const cmd = await getInstallCmd(); const commands = { install: cmd === 'npm' ? 'npm install' : 'yarn install', @@ -86,6 +86,12 @@ export const start = function(projectName: string) { ${Output.cmd(commands.test)} Questions? Feedback? Please let me know! - ${chalk.green('https://github.com/jaredpalmer/tsdx/issues')} + ${chalk.green('https://github.com/formium/tsdx/issues')} `; }; + +export const incorrectNodeVersion = function(requiredVersion: string) { + return `Unsupported Node version! Your current Node version (${chalk.red( + process.version + )}) does not satisfy the requirement of Node ${chalk.cyan(requiredVersion)}.`; +}; diff --git a/src/templates/basic.ts b/src/templates/basic.ts new file mode 100644 index 000000000..7d077e353 --- /dev/null +++ b/src/templates/basic.ts @@ -0,0 +1,61 @@ +import { Template } from './template'; + +const basicTemplate: Template = { + name: 'basic', + dependencies: [ + 'husky', + 'tsdx', + 'tslib', + 'typescript', + 'size-limit', + '@size-limit/preset-small-lib', + ], + packageJson: { + // name: safeName, + version: '0.1.0', + license: 'MIT', + // author: author, + main: 'dist/index.js', + // module: `dist/${safeName}.esm.js`, + typings: `dist/index.d.ts`, + files: ['dist', 'src'], + engines: { + node: '>=10', + }, + scripts: { + start: 'tsdx watch', + build: 'tsdx build', + test: 'tsdx test', + lint: 'tsdx lint', + prepare: 'tsdx build', + size: 'size-limit', + analyze: 'size-limit --why', + }, + peerDependencies: {}, + /* + 'size-limit': [ + { + path: `dist/${safeName}.cjs.production.min.js`, + limit: '10 KB', + }, + { + path: `dist/${safeName}.esm.js`, + limit: '10 KB', + }, + ], + */ + husky: { + hooks: { + 'pre-commit': 'tsdx lint', + }, + }, + prettier: { + printWidth: 80, + semi: true, + singleQuote: true, + trailingComma: 'es5', + }, + }, +}; + +export default basicTemplate; diff --git a/src/templates/index.ts b/src/templates/index.ts new file mode 100644 index 000000000..f6c9c7d76 --- /dev/null +++ b/src/templates/index.ts @@ -0,0 +1,9 @@ +import reactTemplate from './react'; +import basicTemplate from './basic'; +import storybookTemplate from './react-with-storybook'; + +export const templates = { + basic: basicTemplate, + react: reactTemplate, + 'react-with-storybook': storybookTemplate, +}; diff --git a/src/templates/react-with-storybook.ts b/src/templates/react-with-storybook.ts new file mode 100644 index 000000000..70263d540 --- /dev/null +++ b/src/templates/react-with-storybook.ts @@ -0,0 +1,28 @@ +import { Template } from './template'; +import reactTemplate from './react'; +import { PackageJson } from 'type-fest'; + +const storybookTemplate: Template = { + dependencies: [ + ...reactTemplate.dependencies, + '@babel/core', + '@storybook/addon-essentials', + '@storybook/addon-links', + '@storybook/addon-info', + '@storybook/addons', + '@storybook/react', + 'react-is', + 'babel-loader', + ], + name: 'react-with-storybook', + packageJson: { + ...reactTemplate.packageJson, + scripts: { + ...reactTemplate.packageJson.scripts, + storybook: 'start-storybook -p 6006', + 'build-storybook': 'build-storybook', + } as PackageJson['scripts'], + }, +}; + +export default storybookTemplate; diff --git a/src/templates/react.ts b/src/templates/react.ts new file mode 100644 index 000000000..ebb892788 --- /dev/null +++ b/src/templates/react.ts @@ -0,0 +1,27 @@ +import { Template } from './template'; + +import basicTemplate from './basic'; +import { PackageJson } from 'type-fest'; + +const reactTemplate: Template = { + name: 'react', + dependencies: [ + ...basicTemplate.dependencies, + '@types/react', + '@types/react-dom', + 'react', + 'react-dom', + ], + packageJson: { + ...basicTemplate.packageJson, + peerDependencies: { + react: '>=16', + }, + scripts: { + ...basicTemplate.packageJson.scripts, + test: 'tsdx test --passWithNoTests', + } as PackageJson['scripts'], + }, +}; + +export default reactTemplate; diff --git a/src/templates/template.d.ts b/src/templates/template.d.ts new file mode 100644 index 000000000..1dd784ca0 --- /dev/null +++ b/src/templates/template.d.ts @@ -0,0 +1,7 @@ +import { PackageJson } from 'type-fest'; + +interface Template { + dependencies: string[]; + name: string; + packageJson: PackageJson; +} diff --git a/src/templates/utils/index.ts b/src/templates/utils/index.ts new file mode 100644 index 000000000..4f157a077 --- /dev/null +++ b/src/templates/utils/index.ts @@ -0,0 +1,27 @@ +import { Template } from '../template'; + +interface ProjectArgs { + name: string; + author: string; +} +export const composePackageJson = (template: Template) => ({ + name, + author, +}: ProjectArgs) => { + return { + ...template.packageJson, + name, + author, + module: `dist/${name}.esm.js`, + 'size-limit': [ + { + path: `dist/${name}.cjs.production.min.js`, + limit: '10 KB', + }, + { + path: `dist/${name}.esm.js`, + limit: '10 KB', + }, + ], + }; +}; diff --git a/src/types.ts b/src/types.ts index 907ea4550..e6878897e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,20 +1,62 @@ -export interface TsdxOptions { - // path to file - input: string; - // Name of package - name: string; +interface SharedOpts { // JS target target: 'node' | 'browser'; - // Module format - format: 'cjs' | 'umd' | 'esm'; - // Environment - env: 'development' | 'production'; // Path to tsconfig file tsconfig?: string; // Is error extraction running? extractErrors?: boolean; +} + +export type ModuleFormat = 'cjs' | 'umd' | 'esm' | 'system'; + +export interface BuildOpts extends SharedOpts { + name?: string; + entry?: string | string[]; + format: 'cjs,esm'; + target: 'browser'; +} + +export interface WatchOpts extends BuildOpts { + verbose?: boolean; + noClean?: boolean; + // callback hooks + onFirstSuccess?: string; + onSuccess?: string; + onFailure?: string; +} + +export interface NormalizedOpts + extends Omit { + name: string; + input: string[]; + format: [ModuleFormat, ...ModuleFormat[]]; +} + +export interface TsdxOptions extends SharedOpts { + // Name of package + name: string; + // path to file + input: string; + // Environment + env: 'development' | 'production'; + // Module format + format: ModuleFormat; // Is minifying? minify?: boolean; // Is this the very first rollup config (and thus should one-off metadata be extracted)? writeMeta?: boolean; + // Only transpile, do not type check (makes compilation faster) + transpileOnly?: boolean; +} + +export interface PackageJson { + name: string; + source?: string; + jest?: any; + eslint?: any; + dependencies?: { [packageName: string]: string }; + devDependencies?: { [packageName: string]: string }; + engines?: { + node?: string; + }; } diff --git a/src/utils.ts b/src/utils.ts index 85385a83e..a6cbd6da2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,8 @@ import fs from 'fs-extra'; import path from 'path'; import camelCase from 'camelcase'; +import { PackageJson } from './types'; + // Remove the package name scope if it exists export const removeScope = (name: string) => name.replace(/^@.*\//, ''); @@ -35,3 +37,17 @@ export function clearConsole() { process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H' ); } + +export function getReactVersion({ + dependencies, + devDependencies, +}: PackageJson) { + return ( + (dependencies && dependencies.react) || + (devDependencies && devDependencies.react) + ); +} + +export function getNodeEngineRequirement({ engines }: PackageJson) { + return engines && engines.node; +} diff --git a/templates/basic/.github/workflows/main.yml b/templates/basic/.github/workflows/main.yml new file mode 100644 index 000000000..535e4b7c9 --- /dev/null +++ b/templates/basic/.github/workflows/main.yml @@ -0,0 +1,32 @@ +name: CI +on: [push] +jobs: + build: + name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }} + + runs-on: ${{ matrix.os }} + strategy: + matrix: + node: ['10.x', '12.x', '14.x'] + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + + - name: Use Node ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + + - name: Install deps and build (with cache) + uses: bahmutov/npm-install@v1 + + - name: Lint + run: yarn lint + + - name: Test + run: yarn test --ci --coverage --maxWorkers=2 + + - name: Build + run: yarn build diff --git a/templates/basic/.github/workflows/size.yml b/templates/basic/.github/workflows/size.yml new file mode 100644 index 000000000..6021cda3e --- /dev/null +++ b/templates/basic/.github/workflows/size.yml @@ -0,0 +1,12 @@ +name: size +on: [pull_request] +jobs: + size: + runs-on: ubuntu-latest + env: + CI_JOB_NUMBER: 1 + steps: + - uses: actions/checkout@v1 + - uses: andresz1/size-limit-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/templates/basic/LICENSE b/templates/basic/LICENSE new file mode 100644 index 000000000..ac4f03896 --- /dev/null +++ b/templates/basic/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) + +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. \ No newline at end of file diff --git a/templates/basic/README.md b/templates/basic/README.md index 7555d824d..93eb55df4 100644 --- a/templates/basic/README.md +++ b/templates/basic/README.md @@ -1,27 +1,103 @@ -# TSDX Bootstrap +# TSDX User Guide -This project was bootstrapped with [TSDX](https://github.com/jaredpalmer/tsdx). +Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and how to use it. -## Local Development +> This TSDX setup is meant for developing libraries (not apps!) that can be published to NPM. If you’re looking to build a Node app, you could use `ts-node-dev`, plain `ts-node`, or simple `tsc`. -Below is a list of commands you will probably find useful. +> If you’re new to TypeScript, checkout [this handy cheatsheet](https://devhints.io/typescript) -### `npm start` or `yarn start` +## Commands -Runs the project in development/watch mode. Your project will be rebuilt upon changes. TSDX has a special logger for you convenience. Error messages are pretty printed and formatted for compatibility VS Code's Problems tab. +TSDX scaffolds your new library inside `/src`. - +To run TSDX, use: -Your library will be rebuilt if you make edits. +```bash +npm start # or yarn start +``` -### `npm run build` or `yarn build` +This builds to `/dist` and runs the project in watch mode so any edits you save inside `src` causes a rebuild to `/dist`. -Bundles the package to the `dist` folder. -The package is optimized and bundled with Rollup into multiple formats (CommonJS, UMD, and ES Module). +To do a one-off build, use `npm run build` or `yarn build`. - +To run tests, use `npm test` or `yarn test`. -### `npm test` or `yarn test` +## Configuration -Runs the test watcher (Jest) in an interactive mode. -By default, runs tests related to files changed since the last commit. +Code quality is set up for you with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly. + +### Jest + +Jest tests are set up to run with `npm test` or `yarn test`. + +### Bundle Analysis + +[`size-limit`](https://github.com/ai/size-limit) is set up to calculate the real cost of your library with `npm run size` and visualize the bundle with `npm run analyze`. + +#### Setup Files + +This is the folder structure we set up for you: + +```txt +/src + index.tsx # EDIT THIS +/test + blah.test.tsx # EDIT THIS +.gitignore +package.json +README.md # EDIT THIS +tsconfig.json +``` + +### Rollup + +TSDX uses [Rollup](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details. + +### TypeScript + +`tsconfig.json` is set up to interpret `dom` and `esnext` types, as well as `react` for `jsx`. Adjust according to your needs. + +## Continuous Integration + +### GitHub Actions + +Two actions are added by default: + +- `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix +- `size` which comments cost comparison of your library on every pull request using [`size-limit`](https://github.com/ai/size-limit) + +## Optimizations + +Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations: + +```js +// ./types/index.d.ts +declare var __DEV__: boolean; + +// inside your code... +if (__DEV__) { + console.log('foo'); +} +``` + +You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions. + +## Module Formats + +CJS, ESModules, and UMD module formats are supported. + +The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found. + +## Named Exports + +Per Palmer Group guidelines, [always use named exports.](https://github.com/palmerhq/typescript#exports) Code split inside your React app instead of your React library. + +## Including Styles + +There are many ways to ship styles, including with CSS-in-JS. TSDX has no opinion on this, configure how you like. + +For vanilla CSS, you can include it at the root directory and add it to the `files` section in your `package.json`, so that it can be imported separately by your users and run through their bundler's loader. + +## Publishing to NPM + +We recommend using [np](https://github.com/sindresorhus/np). diff --git a/templates/basic/gitignore b/templates/basic/gitignore index 6b47fb81f..4c9d7c35a 100644 --- a/templates/basic/gitignore +++ b/templates/basic/gitignore @@ -1,7 +1,4 @@ *.log .DS_Store node_modules -.rts2_cache_cjs -.rts2_cache_esm -.rts2_cache_umd dist diff --git a/templates/basic/tsconfig.json b/templates/basic/tsconfig.json index d7e223bd3..2c85b2d99 100644 --- a/templates/basic/tsconfig.json +++ b/templates/basic/tsconfig.json @@ -1,30 +1,35 @@ { + // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs "include": ["src", "types"], "compilerOptions": { - "target": "es5", "module": "esnext", "lib": ["dom", "esnext"], "importHelpers": true, + // output .d.ts declaration files for consumers "declaration": true, + // output .js.map sourcemap files for consumers "sourceMap": true, - "rootDir": "./", + // match output dir to input dir. e.g. dist/index instead of dist/src/index + "rootDir": "./src", + // stricter type-checking for stronger correctness. Recommended by TS "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, + // linter checks for common issues "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, + // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative + "noUnusedLocals": true, + "noUnusedParameters": true, + // use Node's module resolution algorithm, instead of the legacy TS one "moduleResolution": "node", - "baseUrl": "./", - "paths": { - "*": ["src/*", "node_modules/*"] - }, + // transpile JSX to React.createElement "jsx": "react", - "esModuleInterop": true + // interop between ESM and CJS modules. Recommended by TS + "esModuleInterop": true, + // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS + "skipLibCheck": true, + // error out if import and file system have a casing mismatch. Recommended by TS + "forceConsistentCasingInFileNames": true, + // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` + "noEmit": true, } } diff --git a/templates/react-with-storybook/.github/workflows/main.yml b/templates/react-with-storybook/.github/workflows/main.yml new file mode 100644 index 000000000..535e4b7c9 --- /dev/null +++ b/templates/react-with-storybook/.github/workflows/main.yml @@ -0,0 +1,32 @@ +name: CI +on: [push] +jobs: + build: + name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }} + + runs-on: ${{ matrix.os }} + strategy: + matrix: + node: ['10.x', '12.x', '14.x'] + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + + - name: Use Node ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + + - name: Install deps and build (with cache) + uses: bahmutov/npm-install@v1 + + - name: Lint + run: yarn lint + + - name: Test + run: yarn test --ci --coverage --maxWorkers=2 + + - name: Build + run: yarn build diff --git a/templates/react-with-storybook/.github/workflows/size.yml b/templates/react-with-storybook/.github/workflows/size.yml new file mode 100644 index 000000000..6021cda3e --- /dev/null +++ b/templates/react-with-storybook/.github/workflows/size.yml @@ -0,0 +1,12 @@ +name: size +on: [pull_request] +jobs: + size: + runs-on: ubuntu-latest + env: + CI_JOB_NUMBER: 1 + steps: + - uses: actions/checkout@v1 + - uses: andresz1/size-limit-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/templates/react-with-storybook/.storybook/main.js b/templates/react-with-storybook/.storybook/main.js new file mode 100644 index 000000000..cf1ac4a9f --- /dev/null +++ b/templates/react-with-storybook/.storybook/main.js @@ -0,0 +1,8 @@ +module.exports = { + stories: ['../stories/**/*.stories.@(ts|tsx|js|jsx)'], + addons: ['@storybook/addon-links', '@storybook/addon-essentials'], + // https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration + typescript: { + check: true, // type-check stories during Storybook build + } +}; diff --git a/templates/react-with-storybook/.storybook/preview.js b/templates/react-with-storybook/.storybook/preview.js new file mode 100644 index 000000000..29ae5f2e4 --- /dev/null +++ b/templates/react-with-storybook/.storybook/preview.js @@ -0,0 +1,5 @@ +// https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters +export const parameters = { + // https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args + actions: { argTypesRegex: '^on.*' }, +}; diff --git a/templates/react-with-storybook/LICENSE b/templates/react-with-storybook/LICENSE new file mode 100644 index 000000000..ac4f03896 --- /dev/null +++ b/templates/react-with-storybook/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) + +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. \ No newline at end of file diff --git a/templates/react-with-storybook/README.md b/templates/react-with-storybook/README.md new file mode 100644 index 000000000..134273f32 --- /dev/null +++ b/templates/react-with-storybook/README.md @@ -0,0 +1,181 @@ +# TSDX React w/ Storybook User Guide + +Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and how to use it. + +> This TSDX setup is meant for developing React component libraries (not apps!) that can be published to NPM. If you’re looking to build a React-based app, you should use `create-react-app`, `razzle`, `nextjs`, `gatsby`, or `react-static`. + +> If you’re new to TypeScript and React, checkout [this handy cheatsheet](https://github.com/sw-yx/react-typescript-cheatsheet/) + +## Commands + +TSDX scaffolds your new library inside `/src`, and also sets up a [Parcel-based](https://parceljs.org) playground for it inside `/example`. + +The recommended workflow is to run TSDX in one terminal: + +```bash +npm start # or yarn start +``` + +This builds to `/dist` and runs the project in watch mode so any edits you save inside `src` causes a rebuild to `/dist`. + +Then run either Storybook or the example playground: + +### Storybook + +Run inside another terminal: + +```bash +yarn storybook +``` + +This loads the stories from `./stories`. + +> NOTE: Stories should reference the components as if using the library, similar to the example playground. This means importing from the root project directory. This has been aliased in the tsconfig and the storybook webpack config as a helper. + +### Example + +Then run the example inside another: + +```bash +cd example +npm i # or yarn to install dependencies +npm start # or yarn start +``` + +The default example imports and live reloads whatever is in `/dist`, so if you are seeing an out of date component, make sure TSDX is running in watch mode like we recommend above. **No symlinking required**, we use [Parcel's aliasing](https://parceljs.org/module_resolution.html#aliases). + +To do a one-off build, use `npm run build` or `yarn build`. + +To run tests, use `npm test` or `yarn test`. + +## Configuration + +Code quality is set up for you with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly. + +### Jest + +Jest tests are set up to run with `npm test` or `yarn test`. + +### Bundle analysis + +Calculates the real cost of your library using [size-limit](https://github.com/ai/size-limit) with `npm run size` and visulize it with `npm run analyze`. + +#### Setup Files + +This is the folder structure we set up for you: + +```txt +/example + index.html + index.tsx # test your component here in a demo app + package.json + tsconfig.json +/src + index.tsx # EDIT THIS +/test + blah.test.tsx # EDIT THIS +/stories + Thing.stories.tsx # EDIT THIS +/.storybook + main.js + preview.js +.gitignore +package.json +README.md # EDIT THIS +tsconfig.json +``` + +#### React Testing Library + +We do not set up `react-testing-library` for you yet, we welcome contributions and documentation on this. + +### Rollup + +TSDX uses [Rollup](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details. + +### TypeScript + +`tsconfig.json` is set up to interpret `dom` and `esnext` types, as well as `react` for `jsx`. Adjust according to your needs. + +## Continuous Integration + +### GitHub Actions + +Two actions are added by default: + +- `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix +- `size` which comments cost comparison of your library on every pull request using [size-limit](https://github.com/ai/size-limit) + +## Optimizations + +Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations: + +```js +// ./types/index.d.ts +declare var __DEV__: boolean; + +// inside your code... +if (__DEV__) { + console.log('foo'); +} +``` + +You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions. + +## Module Formats + +CJS, ESModules, and UMD module formats are supported. + +The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found. + +## Deploying the Example Playground + +The Playground is just a simple [Parcel](https://parceljs.org) app, you can deploy it anywhere you would normally deploy that. Here are some guidelines for **manually** deploying with the Netlify CLI (`npm i -g netlify-cli`): + +```bash +cd example # if not already in the example folder +npm run build # builds to dist +netlify deploy # deploy the dist folder +``` + +Alternatively, if you already have a git repo connected, you can set up continuous deployment with Netlify: + +```bash +netlify init +# build command: yarn build && cd example && yarn && yarn build +# directory to deploy: example/dist +# pick yes for netlify.toml +``` + +## Named Exports + +Per Palmer Group guidelines, [always use named exports.](https://github.com/palmerhq/typescript#exports) Code split inside your React app instead of your React library. + +## Including Styles + +There are many ways to ship styles, including with CSS-in-JS. TSDX has no opinion on this, configure how you like. + +For vanilla CSS, you can include it at the root directory and add it to the `files` section in your `package.json`, so that it can be imported separately by your users and run through their bundler's loader. + +## Publishing to NPM + +We recommend using [np](https://github.com/sindresorhus/np). + +## Usage with Lerna + +When creating a new package with TSDX within a project set up with Lerna, you might encounter a `Cannot resolve dependency` error when trying to run the `example` project. To fix that you will need to make changes to the `package.json` file _inside the `example` directory_. + +The problem is that due to the nature of how dependencies are installed in Lerna projects, the aliases in the example project's `package.json` might not point to the right place, as those dependencies might have been installed in the root of your Lerna project. + +Change the `alias` to point to where those packages are actually installed. This depends on the directory structure of your Lerna project, so the actual path might be different from the diff below. + +```diff + "alias": { +- "react": "../node_modules/react", +- "react-dom": "../node_modules/react-dom" ++ "react": "../../../node_modules/react", ++ "react-dom": "../../../node_modules/react-dom" + }, +``` + +An alternative to fixing this problem would be to remove aliases altogether and define the dependencies referenced as aliases as dev dependencies instead. [However, that might cause other problems.](https://github.com/palmerhq/tsdx/issues/64) diff --git a/templates/react-with-storybook/example/.gitignore b/templates/react-with-storybook/example/.gitignore new file mode 100644 index 000000000..587e4ec7a --- /dev/null +++ b/templates/react-with-storybook/example/.gitignore @@ -0,0 +1,3 @@ +node_modules +.cache +dist \ No newline at end of file diff --git a/templates/react-with-storybook/example/index.html b/templates/react-with-storybook/example/index.html new file mode 100644 index 000000000..547e2e042 --- /dev/null +++ b/templates/react-with-storybook/example/index.html @@ -0,0 +1,14 @@ + + + + + + + Playground + + + +
+ + + diff --git a/templates/react-with-storybook/example/index.tsx b/templates/react-with-storybook/example/index.tsx new file mode 100644 index 000000000..73387c60e --- /dev/null +++ b/templates/react-with-storybook/example/index.tsx @@ -0,0 +1,14 @@ +import 'react-app-polyfill/ie11'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { Thing } from '../.'; + +const App = () => { + return ( +
+ +
+ ); +}; + +ReactDOM.render(, document.getElementById('root')); diff --git a/templates/react-with-storybook/example/package.json b/templates/react-with-storybook/example/package.json new file mode 100644 index 000000000..a50960f5c --- /dev/null +++ b/templates/react-with-storybook/example/package.json @@ -0,0 +1,24 @@ +{ + "name": "example", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "scripts": { + "start": "parcel index.html", + "build": "parcel build index.html" + }, + "dependencies": { + "react-app-polyfill": "^1.0.0" + }, + "alias": { + "react": "../node_modules/react", + "react-dom": "../node_modules/react-dom/profiling", + "scheduler/tracing": "../node_modules/scheduler/tracing-profiling" + }, + "devDependencies": { + "@types/react": "^16.9.11", + "@types/react-dom": "^16.8.4", + "parcel": "^1.12.3", + "typescript": "^3.4.5" + } +} diff --git a/templates/react-with-storybook/example/tsconfig.json b/templates/react-with-storybook/example/tsconfig.json new file mode 100644 index 000000000..1e2e4fd9c --- /dev/null +++ b/templates/react-with-storybook/example/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": false, + "target": "es5", + "module": "commonjs", + "jsx": "react", + "moduleResolution": "node", + "noImplicitAny": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "removeComments": true, + "strictNullChecks": true, + "preserveConstEnums": true, + "sourceMap": true, + "lib": ["es2015", "es2016", "dom"], + "types": ["node"] + } +} diff --git a/templates/react-with-storybook/gitignore b/templates/react-with-storybook/gitignore new file mode 100644 index 000000000..d4de8fc06 --- /dev/null +++ b/templates/react-with-storybook/gitignore @@ -0,0 +1,5 @@ +*.log +.DS_Store +node_modules +.cache +dist diff --git a/templates/react-with-storybook/src/index.tsx b/templates/react-with-storybook/src/index.tsx new file mode 100644 index 000000000..4487cc09d --- /dev/null +++ b/templates/react-with-storybook/src/index.tsx @@ -0,0 +1,15 @@ +import React, { FC, HTMLAttributes, ReactChild } from 'react'; + +export interface Props extends HTMLAttributes { + /** custom content, defaults to 'the snozzberries taste like snozzberries' */ + children?: ReactChild; +} + +// Please do not use types off of a default export module or else Storybook Docs will suffer. +// see: https://github.com/storybookjs/storybook/issues/9556 +/** + * A custom Thing component. Neat! + */ +export const Thing: FC = ({ children }) => { + return
{children || `the snozzberries taste like snozzberries`}
; +}; diff --git a/templates/react-with-storybook/stories/Thing.stories.tsx b/templates/react-with-storybook/stories/Thing.stories.tsx new file mode 100644 index 000000000..3e9f7e64a --- /dev/null +++ b/templates/react-with-storybook/stories/Thing.stories.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Meta, Story } from '@storybook/react'; +import { Thing, Props } from '../src'; + +const meta: Meta = { + title: 'Welcome', + component: Thing, + argTypes: { + children: { + control: { + type: 'text', + }, + }, + }, + parameters: { + controls: { expanded: true }, + }, +}; + +export default meta; + +const Template: Story = args => ; + +// By passing using the Args format for exported stories, you can control the props for a component for reuse in a test +// https://storybook.js.org/docs/react/workflows/unit-testing +export const Default = Template.bind({}); + +Default.args = {}; diff --git a/templates/react-with-storybook/test/blah.test.tsx b/templates/react-with-storybook/test/blah.test.tsx new file mode 100644 index 000000000..dd73ee473 --- /dev/null +++ b/templates/react-with-storybook/test/blah.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import * as ReactDOM from 'react-dom'; +import { Default as Thing } from '../stories/Thing.stories'; + +describe('Thing', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); + }); +}); diff --git a/templates/react-with-storybook/tsconfig.json b/templates/react-with-storybook/tsconfig.json new file mode 100644 index 000000000..2c85b2d99 --- /dev/null +++ b/templates/react-with-storybook/tsconfig.json @@ -0,0 +1,35 @@ +{ + // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs + "include": ["src", "types"], + "compilerOptions": { + "module": "esnext", + "lib": ["dom", "esnext"], + "importHelpers": true, + // output .d.ts declaration files for consumers + "declaration": true, + // output .js.map sourcemap files for consumers + "sourceMap": true, + // match output dir to input dir. e.g. dist/index instead of dist/src/index + "rootDir": "./src", + // stricter type-checking for stronger correctness. Recommended by TS + "strict": true, + // linter checks for common issues + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative + "noUnusedLocals": true, + "noUnusedParameters": true, + // use Node's module resolution algorithm, instead of the legacy TS one + "moduleResolution": "node", + // transpile JSX to React.createElement + "jsx": "react", + // interop between ESM and CJS modules. Recommended by TS + "esModuleInterop": true, + // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS + "skipLibCheck": true, + // error out if import and file system have a casing mismatch. Recommended by TS + "forceConsistentCasingInFileNames": true, + // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` + "noEmit": true, + } +} diff --git a/templates/react/.github/workflows/main.yml b/templates/react/.github/workflows/main.yml new file mode 100644 index 000000000..535e4b7c9 --- /dev/null +++ b/templates/react/.github/workflows/main.yml @@ -0,0 +1,32 @@ +name: CI +on: [push] +jobs: + build: + name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }} + + runs-on: ${{ matrix.os }} + strategy: + matrix: + node: ['10.x', '12.x', '14.x'] + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + + - name: Use Node ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + + - name: Install deps and build (with cache) + uses: bahmutov/npm-install@v1 + + - name: Lint + run: yarn lint + + - name: Test + run: yarn test --ci --coverage --maxWorkers=2 + + - name: Build + run: yarn build diff --git a/templates/react/.github/workflows/size.yml b/templates/react/.github/workflows/size.yml new file mode 100644 index 000000000..6021cda3e --- /dev/null +++ b/templates/react/.github/workflows/size.yml @@ -0,0 +1,12 @@ +name: size +on: [pull_request] +jobs: + size: + runs-on: ubuntu-latest + env: + CI_JOB_NUMBER: 1 + steps: + - uses: actions/checkout@v1 + - uses: andresz1/size-limit-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/templates/react/LICENSE b/templates/react/LICENSE new file mode 100644 index 000000000..ac4f03896 --- /dev/null +++ b/templates/react/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) + +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. \ No newline at end of file diff --git a/templates/react/README.md b/templates/react/README.md index 697c66e50..1db53ccd9 100644 --- a/templates/react/README.md +++ b/templates/react/README.md @@ -2,7 +2,7 @@ Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and how to use it. -> This TSDX setup is meant for developing React components (not apps!) that can be published to NPM. If you’re looking to build an app, you should use `create-react-app`, `razzle`, `nextjs`, `gatsby`, or `react-static`. +> This TSDX setup is meant for developing React component libraries (not apps!) that can be published to NPM. If you’re looking to build a React-based app, you should use `create-react-app`, `razzle`, `nextjs`, `gatsby`, or `react-static`. > If you’re new to TypeScript and React, checkout [this handy cheatsheet](https://github.com/sw-yx/react-typescript-cheatsheet/) @@ -12,7 +12,7 @@ TSDX scaffolds your new library inside `/src`, and also sets up a [Parcel-based] The recommended workflow is to run TSDX in one terminal: -``` +```bash npm start # or yarn start ``` @@ -20,13 +20,13 @@ This builds to `/dist` and runs the project in watch mode so any edits you save Then run the example inside another: -``` +```bash cd example npm i # or yarn to install dependencies npm start # or yarn start ``` -The default example imports and live reloads whatever is in `/dist`, so if you are seeing an out of date component, make sure TSDX is running in watch mode like we recommend above. **No symlinking required**, [we use Parcel's aliasing](https://github.com/palmerhq/tsdx/pull/88/files). +The default example imports and live reloads whatever is in `/dist`, so if you are seeing an out of date component, make sure TSDX is running in watch mode like we recommend above. **No symlinking required**, we use [Parcel's aliasing](https://parceljs.org/module_resolution.html#aliases). To do a one-off build, use `npm run build` or `yarn build`. @@ -34,17 +34,21 @@ To run tests, use `npm test` or `yarn test`. ## Configuration -Code quality is [set up for you](https://github.com/palmerhq/tsdx/pull/45/files) with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly. +Code quality is set up for you with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly. ### Jest -Jest tests are set up to run with `npm test` or `yarn test`. This runs the test watcher (Jest) in an interactive mode. By default, runs tests related to files changed since the last commit. +Jest tests are set up to run with `npm test` or `yarn test`. + +### Bundle analysis + +Calculates the real cost of your library using [size-limit](https://github.com/ai/size-limit) with `npm run size` and visulize it with `npm run analyze`. #### Setup Files This is the folder structure we set up for you: -``` +```txt /example index.html index.tsx # test your component here in a demo app @@ -66,7 +70,7 @@ We do not set up `react-testing-library` for you yet, we welcome contributions a ### Rollup -TSDX uses [Rollup v1.x](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details. +TSDX uses [Rollup](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details. ### TypeScript @@ -74,13 +78,12 @@ TSDX uses [Rollup v1.x](https://rollupjs.org) as a bundler and generates multipl ## Continuous Integration -### Travis - -_to be completed_ +### GitHub Actions -### Circle +Two actions are added by default: -_to be completed_ +- `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix +- `size` which comments cost comparison of your library on every pull request using [`size-limit`](https://github.com/ai/size-limit) ## Optimizations @@ -104,17 +107,7 @@ CJS, ESModules, and UMD module formats are supported. The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found. -## Using the Playground - -``` -cd example -npm i # or yarn to install dependencies -npm start # or yarn start -``` - -The default example imports and live reloads whatever is in `/dist`, so if you are seeing an out of date component, make sure TSDX is running in watch mode like we recommend above. **No symlinking required**! - -## Deploying the Playground +## Deploying the Example Playground The Playground is just a simple [Parcel](https://parceljs.org) app, you can deploy it anywhere you would normally deploy that. Here are some guidelines for **manually** deploying with the Netlify CLI (`npm i -g netlify-cli`): @@ -128,7 +121,7 @@ Alternatively, if you already have a git repo connected, you can set up continuo ```bash netlify init -# build command: cd example && yarn && yarn build +# build command: yarn build && cd example && yarn && yarn build # directory to deploy: example/dist # pick yes for netlify.toml ``` @@ -145,7 +138,7 @@ For vanilla CSS, you can include it at the root directory and add it to the `fil ## Publishing to NPM -We recommend using https://github.com/sindresorhus/np. +We recommend using [np](https://github.com/sindresorhus/np). ## Usage with Lerna diff --git a/templates/react/example/package.json b/templates/react/example/package.json index 2b9a863b4..a50960f5c 100644 --- a/templates/react/example/package.json +++ b/templates/react/example/package.json @@ -16,7 +16,7 @@ "scheduler/tracing": "../node_modules/scheduler/tracing-profiling" }, "devDependencies": { - "@types/react": "^16.8.15", + "@types/react": "^16.9.11", "@types/react-dom": "^16.8.4", "parcel": "^1.12.3", "typescript": "^3.4.5" diff --git a/templates/react/example/tsconfig.json b/templates/react/example/tsconfig.json index 6d518675d..1e2e4fd9c 100644 --- a/templates/react/example/tsconfig.json +++ b/templates/react/example/tsconfig.json @@ -13,7 +13,6 @@ "preserveConstEnums": true, "sourceMap": true, "lib": ["es2015", "es2016", "dom"], - "baseUrl": ".", "types": ["node"] } } diff --git a/templates/react/gitignore b/templates/react/gitignore index 26404ec2d..d4de8fc06 100644 --- a/templates/react/gitignore +++ b/templates/react/gitignore @@ -2,7 +2,4 @@ .DS_Store node_modules .cache -.rts2_cache_cjs -.rts2_cache_esm -.rts2_cache_umd dist diff --git a/templates/react/tsconfig.json b/templates/react/tsconfig.json index d7e223bd3..2c85b2d99 100644 --- a/templates/react/tsconfig.json +++ b/templates/react/tsconfig.json @@ -1,30 +1,35 @@ { + // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs "include": ["src", "types"], "compilerOptions": { - "target": "es5", "module": "esnext", "lib": ["dom", "esnext"], "importHelpers": true, + // output .d.ts declaration files for consumers "declaration": true, + // output .js.map sourcemap files for consumers "sourceMap": true, - "rootDir": "./", + // match output dir to input dir. e.g. dist/index instead of dist/src/index + "rootDir": "./src", + // stricter type-checking for stronger correctness. Recommended by TS "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, + // linter checks for common issues "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, + // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative + "noUnusedLocals": true, + "noUnusedParameters": true, + // use Node's module resolution algorithm, instead of the legacy TS one "moduleResolution": "node", - "baseUrl": "./", - "paths": { - "*": ["src/*", "node_modules/*"] - }, + // transpile JSX to React.createElement "jsx": "react", - "esModuleInterop": true + // interop between ESM and CJS modules. Recommended by TS + "esModuleInterop": true, + // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS + "skipLibCheck": true, + // error out if import and file system have a casing mismatch. Recommended by TS + "forceConsistentCasingInFileNames": true, + // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` + "noEmit": true, } } diff --git a/test/README.md b/test/README.md new file mode 100644 index 000000000..623127f60 --- /dev/null +++ b/test/README.md @@ -0,0 +1,5 @@ +# Tests + +- `unit` contains unit tests of internals +- `e2e` contains end-to-end (E2E) tests of the CLI +- `integration` contains tests ensuring that common or recommended plugins work properly together with TSDX diff --git a/test/e2e/fixtures/README.md b/test/e2e/fixtures/README.md new file mode 100644 index 000000000..9cde3388c --- /dev/null +++ b/test/e2e/fixtures/README.md @@ -0,0 +1,6 @@ +# E2E Test Fixtures Directory + +- `build-default` focuses on our zero config defaults +- `build-invalid` lets us check what happens when we have invalid builds due to type errors +- `build-withTsconfig` lets us check that `tsconfig.json` options are correctly used +- `lint` lets us check that lint errors are correctly detected diff --git a/test/fixtures/build-default/package.json b/test/e2e/fixtures/build-default/package.json similarity index 100% rename from test/fixtures/build-default/package.json rename to test/e2e/fixtures/build-default/package.json diff --git a/test/e2e/fixtures/build-default/package2.json b/test/e2e/fixtures/build-default/package2.json new file mode 100644 index 000000000..6a6ee8983 --- /dev/null +++ b/test/e2e/fixtures/build-default/package2.json @@ -0,0 +1,7 @@ +{ + "scripts": { + "build": "tsdx build" + }, + "name": "build-default-2", + "license": "MIT" +} diff --git a/test/fixtures/build-default/src/foo.ts b/test/e2e/fixtures/build-default/src/foo.ts similarity index 100% rename from test/fixtures/build-default/src/foo.ts rename to test/e2e/fixtures/build-default/src/foo.ts diff --git a/test/e2e/fixtures/build-default/src/index.ts b/test/e2e/fixtures/build-default/src/index.ts new file mode 100644 index 000000000..ca8751686 --- /dev/null +++ b/test/e2e/fixtures/build-default/src/index.ts @@ -0,0 +1,19 @@ +import './syntax/nullish-coalescing'; +import './syntax/optional-chaining'; + +import './syntax/jsx-import/JSX-import-JSX'; + +import './syntax/async'; +export { testGenerator } from './syntax/generator'; + +export { kebabCase } from 'lodash'; +export { merge, mergeAll } from 'lodash/fp'; + +export { foo } from './foo'; + +export const sum = (a: number, b: number) => { + if ('development' === process.env.NODE_ENV) { + console.log('fuck'); + } + return a + b; +}; diff --git a/test/e2e/fixtures/build-default/src/syntax/async.ts b/test/e2e/fixtures/build-default/src/syntax/async.ts new file mode 100644 index 000000000..2cd9e2d48 --- /dev/null +++ b/test/e2e/fixtures/build-default/src/syntax/async.ts @@ -0,0 +1,6 @@ +// regression test for async/await +// code inspired by https://github.com/formium/tsdx/issues/869 +(async () => { + await Promise.resolve(); + console.log('a side effect to make sure this is output'); +})(); diff --git a/test/e2e/fixtures/build-default/src/syntax/generator.ts b/test/e2e/fixtures/build-default/src/syntax/generator.ts new file mode 100644 index 000000000..1e1ccd206 --- /dev/null +++ b/test/e2e/fixtures/build-default/src/syntax/generator.ts @@ -0,0 +1,4 @@ +// regression test for generators +export function* testGenerator() { + return yield 'blah'; +} diff --git a/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-A.jsx b/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-A.jsx new file mode 100644 index 000000000..27f439c05 --- /dev/null +++ b/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-A.jsx @@ -0,0 +1,7 @@ +// DO NOT IMPORT THIS FILE DIRECTLY FROM index.ts +// THIS FILE IS INTENTIONALLY TO TEST JSX CHAINING IMPORT +// SEE https://github.com/jaredpalmer/tsdx/issues/523 + +import JSXB from './JSX-B'; + +export default JSXB; diff --git a/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-B.jsx b/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-B.jsx new file mode 100644 index 000000000..5d51e4235 --- /dev/null +++ b/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-B.jsx @@ -0,0 +1,7 @@ +// DO NOT IMPORT THIS FILE DIRECTLY FROM index.ts +// THIS FILE IS INTENTIONALLY TO TEST JSX CHAINING IMPORT +// SEE https://github.com/jaredpalmer/tsdx/issues/523 + +export default function JSXComponent() { + return 'JSXC'; +} diff --git a/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-import-JSX.jsx b/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-import-JSX.jsx new file mode 100644 index 000000000..18d99216c --- /dev/null +++ b/test/e2e/fixtures/build-default/src/syntax/jsx-import/JSX-import-JSX.jsx @@ -0,0 +1,4 @@ +// Testing for jsx chaining import +// https://github.com/jaredpalmer/tsdx/issues/523 + +export * from './JSX-A'; diff --git a/test/e2e/fixtures/build-default/src/syntax/nullish-coalescing.ts b/test/e2e/fixtures/build-default/src/syntax/nullish-coalescing.ts new file mode 100644 index 000000000..8ceb5505b --- /dev/null +++ b/test/e2e/fixtures/build-default/src/syntax/nullish-coalescing.ts @@ -0,0 +1,5 @@ +// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#nullish-coalescing + +const bar = () => {}; +const foo = false; +export const x = foo ?? bar(); diff --git a/test/e2e/fixtures/build-default/src/syntax/optional-chaining.ts b/test/e2e/fixtures/build-default/src/syntax/optional-chaining.ts new file mode 100644 index 000000000..6978989b4 --- /dev/null +++ b/test/e2e/fixtures/build-default/src/syntax/optional-chaining.ts @@ -0,0 +1,3 @@ +// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining + +export const foo = (foo?: { bar: string }) => foo?.bar || 'bar'; diff --git a/test/e2e/fixtures/build-default/test/some-test.test.ts b/test/e2e/fixtures/build-default/test/some-test.test.ts new file mode 100644 index 000000000..0a394b564 --- /dev/null +++ b/test/e2e/fixtures/build-default/test/some-test.test.ts @@ -0,0 +1,3 @@ +// this is to test that .test/.spec files in the test/ dir are excluded + +// and that rootDir: './src' doesn't error with test/ files diff --git a/test/e2e/fixtures/build-default/test/testUtil.ts b/test/e2e/fixtures/build-default/test/testUtil.ts new file mode 100644 index 000000000..d5c76f081 --- /dev/null +++ b/test/e2e/fixtures/build-default/test/testUtil.ts @@ -0,0 +1,4 @@ +// this is to test that test helper files in the test/ dir are excluded +// i.e. files in test/ that don't have a .spec/.test suffix + +// and that rootDir: './src' doesn't error with test/ files diff --git a/test/fixtures/build-invalid/tsconfig.json b/test/e2e/fixtures/build-default/tsconfig.json similarity index 52% rename from test/fixtures/build-invalid/tsconfig.json rename to test/e2e/fixtures/build-default/tsconfig.json index eae0b2904..d0dea9704 100644 --- a/test/fixtures/build-invalid/tsconfig.json +++ b/test/e2e/fixtures/build-default/tsconfig.json @@ -1,29 +1,21 @@ { "compilerOptions": { - "target": "es5", "module": "ESNext", "lib": ["dom", "esnext"], "declaration": true, "sourceMap": true, - "rootDir": "./", + "rootDir": "./src", "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "node", - "baseUrl": "./", - "paths": { - "*": ["src/*", "node_modules/*"] - }, "jsx": "react", - "esModuleInterop": true + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true }, "include": ["src", "types"] } diff --git a/test/e2e/fixtures/build-default/types/blar.d.ts b/test/e2e/fixtures/build-default/types/blar.d.ts new file mode 100644 index 000000000..a93bfc167 --- /dev/null +++ b/test/e2e/fixtures/build-default/types/blar.d.ts @@ -0,0 +1,3 @@ +// this is to test that rootDir: './src' doesn't error with types/ files + +// and that declaration files aren't re-output in dist/ diff --git a/test/fixtures/build-invalid/package.json b/test/e2e/fixtures/build-invalid/package.json similarity index 100% rename from test/fixtures/build-invalid/package.json rename to test/e2e/fixtures/build-invalid/package.json diff --git a/test/fixtures/build-invalid/src/index.ts b/test/e2e/fixtures/build-invalid/src/index.ts similarity index 100% rename from test/fixtures/build-invalid/src/index.ts rename to test/e2e/fixtures/build-invalid/src/index.ts diff --git a/test/e2e/fixtures/build-invalid/tsconfig.json b/test/e2e/fixtures/build-invalid/tsconfig.json new file mode 100644 index 000000000..d0dea9704 --- /dev/null +++ b/test/e2e/fixtures/build-invalid/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "ESNext", + "lib": ["dom", "esnext"], + "declaration": true, + "sourceMap": true, + "rootDir": "./src", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "moduleResolution": "node", + "jsx": "react", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true + }, + "include": ["src", "types"] +} diff --git a/test/e2e/fixtures/build-withTsconfig/package.json b/test/e2e/fixtures/build-withTsconfig/package.json new file mode 100644 index 000000000..bfd75cf3f --- /dev/null +++ b/test/e2e/fixtures/build-withTsconfig/package.json @@ -0,0 +1,7 @@ +{ + "scripts": { + "build": "tsdx build" + }, + "name": "build-withtsconfig", + "license": "MIT" +} diff --git a/test/e2e/fixtures/build-withTsconfig/src/index.ts b/test/e2e/fixtures/build-withTsconfig/src/index.ts new file mode 100644 index 000000000..af27ae37d --- /dev/null +++ b/test/e2e/fixtures/build-withTsconfig/src/index.ts @@ -0,0 +1,6 @@ +export const sum = (a: number, b: number) => { + if ('development' === process.env.NODE_ENV) { + console.log('fuck'); + } + return a + b; +}; diff --git a/test/e2e/fixtures/build-withTsconfig/src/tsconfig.json b/test/e2e/fixtures/build-withTsconfig/src/tsconfig.json new file mode 100644 index 000000000..1fbdfca66 --- /dev/null +++ b/test/e2e/fixtures/build-withTsconfig/src/tsconfig.json @@ -0,0 +1,7 @@ +{ + // ensure that extends works (trailing comma & comment too) + "extends": "../tsconfig.base.json", + "compilerOptions": { + "declarationDir": "../typingsCustom/" + } +} diff --git a/test/e2e/fixtures/build-withTsconfig/tsconfig.base.json b/test/e2e/fixtures/build-withTsconfig/tsconfig.base.json new file mode 100644 index 000000000..639f22f26 --- /dev/null +++ b/test/e2e/fixtures/build-withTsconfig/tsconfig.base.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "ESNext", + "lib": ["dom", "esnext"], + "declaration": true, + "declarationDir": "typings", + "declarationMap": true, + "sourceMap": true, + "rootDir": "./src", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "moduleResolution": "node", + "jsx": "react", + "esModuleInterop": false, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true + }, + "include": ["src", "types"], // test parsing of trailing comma & comment +} diff --git a/test/e2e/fixtures/build-withTsconfig/tsconfig.json b/test/e2e/fixtures/build-withTsconfig/tsconfig.json new file mode 100644 index 000000000..c87c0de33 --- /dev/null +++ b/test/e2e/fixtures/build-withTsconfig/tsconfig.json @@ -0,0 +1,4 @@ +{ + // ensure that extends works (trailing comma & comment too) + "extends": "./tsconfig.base.json", +} diff --git a/test/tests/lint/file-with-lint-errors.ts b/test/e2e/fixtures/lint/file-with-lint-errors.ts similarity index 100% rename from test/tests/lint/file-with-lint-errors.ts rename to test/e2e/fixtures/lint/file-with-lint-errors.ts diff --git a/test/e2e/fixtures/lint/file-with-lint-warnings.ts b/test/e2e/fixtures/lint/file-with-lint-warnings.ts new file mode 100644 index 000000000..fe0df4995 --- /dev/null +++ b/test/e2e/fixtures/lint/file-with-lint-warnings.ts @@ -0,0 +1,5 @@ +// this file should have 3 "unused var" lint warnings +const unusedVar1 = () => { + const unusedVar2 = 'baz'; + const unusedVar3 = ''; +}; diff --git a/test/tests/lint/file-with-prettier-lint-errors.ts b/test/e2e/fixtures/lint/file-with-prettier-lint-errors.ts similarity index 100% rename from test/tests/lint/file-with-prettier-lint-errors.ts rename to test/e2e/fixtures/lint/file-with-prettier-lint-errors.ts diff --git a/test/tests/lint/file-without-lint-error.ts b/test/e2e/fixtures/lint/file-without-lint-error.ts similarity index 100% rename from test/tests/lint/file-without-lint-error.ts rename to test/e2e/fixtures/lint/file-without-lint-error.ts diff --git a/test/tests/lint/react-file-with-lint-errors.tsx b/test/e2e/fixtures/lint/react-file-with-lint-errors.tsx similarity index 100% rename from test/tests/lint/react-file-with-lint-errors.tsx rename to test/e2e/fixtures/lint/react-file-with-lint-errors.tsx diff --git a/test/tests/lint/react-file-without-lint-error.tsx b/test/e2e/fixtures/lint/react-file-without-lint-error.tsx similarity index 100% rename from test/tests/lint/react-file-without-lint-error.tsx rename to test/e2e/fixtures/lint/react-file-without-lint-error.tsx diff --git a/test/e2e/tsdx-build-default.test.ts b/test/e2e/tsdx-build-default.test.ts new file mode 100644 index 000000000..1f88d1ab1 --- /dev/null +++ b/test/e2e/tsdx-build-default.test.ts @@ -0,0 +1,135 @@ +import * as shell from 'shelljs'; + +import * as util from '../utils/fixture'; +import { execWithCache, grep } from '../utils/shell'; + +shell.config.silent = false; + +const testDir = 'e2e'; +const fixtureName = 'build-default'; +const stageName = `stage-${fixtureName}`; + +describe('tsdx build :: zero-config defaults', () => { + beforeAll(() => { + util.teardownStage(stageName); + util.setupStageWithFixture(testDir, stageName, fixtureName); + }); + + it('should compile files into a dist directory', () => { + const output = execWithCache('node ../dist/index.js build'); + + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default.cjs.production.min.js') + ).toBeTruthy(); + expect(shell.test('-f', 'dist/build-default.esm.js')).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + it("shouldn't compile files in test/ or types/", () => { + const output = execWithCache('node ../dist/index.js build'); + + expect(shell.test('-d', 'dist/test/')).toBeFalsy(); + expect(shell.test('-d', 'dist/types/')).toBeFalsy(); + + expect(output.code).toBe(0); + }); + + it('should create the library correctly', () => { + const output = execWithCache('node ../dist/index.js build'); + + const lib = require(`../../${stageName}/dist`); + expect(lib.foo()).toBe('bar'); + expect(lib.__esModule).toBe(true); + + expect(output.code).toBe(0); + }); + + it('should bundle regeneratorRuntime', () => { + const output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(0); + + const matched = grep(/regeneratorRuntime = r/, ['dist/build-default.*.js']); + expect(matched).toBeTruthy(); + }); + + it('should not bundle regeneratorRuntime when targeting Node', () => { + const output = execWithCache('node ../dist/index.js build --target node'); + expect(output.code).toBe(0); + + const matched = grep(/regeneratorRuntime = r/, ['dist/build-default.*.js']); + expect(matched).toBeFalsy(); + }); + + it('should use lodash for the CJS build', () => { + const output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(0); + + const matched = grep(/lodash/, ['dist/build-default.cjs.*.js']); + expect(matched).toBeTruthy(); + }); + + it('should use lodash-es for the ESM build', () => { + const output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(0); + + const matched = grep(/lodash-es/, ['dist/build-default.esm.js']); + expect(matched).toBeTruthy(); + }); + + it("shouldn't replace lodash/fp", () => { + const output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(0); + + const matched = grep(/lodash\/fp/, ['dist/build-default.*.js']); + expect(matched).toBeTruthy(); + }); + + it('should clean the dist directory before rebuilding', () => { + let output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(0); + + shell.mv('package.json', 'package-og.json'); + shell.mv('package2.json', 'package.json'); + + // cache bust because we want to re-run this command with new package.json + output = execWithCache('node ../dist/index.js build', { noCache: true }); + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + + // build-default files have been cleaned out + expect( + shell.test('-f', 'dist/build-default.cjs.development.js') + ).toBeFalsy(); + expect( + shell.test('-f', 'dist/build-default.cjs.production.min.js') + ).toBeFalsy(); + expect(shell.test('-f', 'dist/build-default.esm.js')).toBeFalsy(); + + // build-default-2 files have been added + expect( + shell.test('-f', 'dist/build-default-2.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default-2.cjs.production.min.js') + ).toBeTruthy(); + expect(shell.test('-f', 'dist/build-default-2.esm.js')).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); + + expect(output.code).toBe(0); + + // reset package.json files + shell.mv('package.json', 'package2.json'); + shell.mv('package-og.json', 'package.json'); + }); + + afterAll(() => { + util.teardownStage(stageName); + }); +}); diff --git a/test/e2e/tsdx-build-invalid.test.ts b/test/e2e/tsdx-build-invalid.test.ts new file mode 100644 index 000000000..3bf8c80b6 --- /dev/null +++ b/test/e2e/tsdx-build-invalid.test.ts @@ -0,0 +1,43 @@ +import * as shell from 'shelljs'; + +import * as util from '../utils/fixture'; +import { execWithCache } from '../utils/shell'; + +shell.config.silent = false; + +const testDir = 'e2e'; +const fixtureName = 'build-invalid'; +const stageName = `stage-${fixtureName}`; + +describe('tsdx build :: invalid build', () => { + beforeAll(() => { + util.teardownStage(stageName); + util.setupStageWithFixture(testDir, stageName, fixtureName); + }); + + it('should fail gracefully with exit code 1 when build failed', () => { + const output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(1); + }); + + it('should only transpile and not type check', () => { + const output = execWithCache('node ../dist/index.js build --transpileOnly'); + + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-invalid.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-invalid.cjs.production.min.js') + ).toBeTruthy(); + expect(shell.test('-f', 'dist/build-invalid.esm.js')).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + afterAll(() => { + util.teardownStage(stageName); + }); +}); diff --git a/test/e2e/tsdx-build-options.test.ts b/test/e2e/tsdx-build-options.test.ts new file mode 100644 index 000000000..ded530033 --- /dev/null +++ b/test/e2e/tsdx-build-options.test.ts @@ -0,0 +1,53 @@ +import * as shell from 'shelljs'; + +import * as util from '../utils/fixture'; +import { execWithCache } from '../utils/shell'; + +shell.config.silent = false; + +const testDir = 'e2e'; +const fixtureName = 'build-default'; +// create a second version of build-default's stage for concurrent testing +const stageName = 'stage-build-options'; + +describe('tsdx build :: options', () => { + beforeAll(() => { + util.teardownStage(stageName); + util.setupStageWithFixture(testDir, stageName, fixtureName); + }); + + it('should compile all formats', () => { + const output = execWithCache( + 'node ../dist/index.js build --format cjs,esm,umd,system' + ); + + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default.cjs.production.min.js') + ).toBeTruthy(); + expect(shell.test('-f', 'dist/build-default.esm.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default.umd.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default.umd.production.min.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default.system.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-default.system.production.min.js') + ).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + afterAll(() => { + util.teardownStage(stageName); + }); +}); diff --git a/test/e2e/tsdx-build-withTsconfig.test.ts b/test/e2e/tsdx-build-withTsconfig.test.ts new file mode 100644 index 000000000..9474104db --- /dev/null +++ b/test/e2e/tsdx-build-withTsconfig.test.ts @@ -0,0 +1,70 @@ +import * as shell from 'shelljs'; + +import * as util from '../utils/fixture'; +import { execWithCache } from '../utils/shell'; + +shell.config.silent = false; + +const testDir = 'e2e'; +const fixtureName = 'build-withTsconfig'; +const stageName = `stage-${fixtureName}`; + +describe('tsdx build :: build with custom tsconfig.json options', () => { + beforeAll(() => { + util.teardownStage(stageName); + util.setupStageWithFixture(testDir, stageName, fixtureName); + }); + + it('should use the declarationDir when set', () => { + const output = execWithCache('node ../dist/index.js build'); + + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-withtsconfig.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-withtsconfig.cjs.production.min.js') + ).toBeTruthy(); + expect(shell.test('-f', 'dist/build-withtsconfig.esm.js')).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeFalsy(); + expect(shell.test('-f', 'typings/index.d.ts')).toBeTruthy(); + expect(shell.test('-f', 'typings/index.d.ts.map')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + it('should set __esModule according to esModuleInterop', () => { + const output = execWithCache('node ../dist/index.js build'); + + const lib = require(`../../${stageName}/dist/build-withtsconfig.cjs.production.min.js`); + // if esModuleInterop: false, no __esModule is added, therefore undefined + expect(lib.__esModule).toBe(undefined); + + expect(output.code).toBe(0); + }); + + it('should read custom --tsconfig path', () => { + const output = execWithCache( + 'node ../dist/index.js build --format cjs --tsconfig ./src/tsconfig.json' + ); + + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-withtsconfig.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-withtsconfig.cjs.production.min.js') + ).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeFalsy(); + expect(shell.test('-f', 'typingsCustom/index.d.ts')).toBeTruthy(); + expect(shell.test('-f', 'typingsCustom/index.d.ts.map')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + afterAll(() => { + util.teardownStage(stageName); + }); +}); diff --git a/test/e2e/tsdx-lint.test.ts b/test/e2e/tsdx-lint.test.ts new file mode 100644 index 000000000..e9ce66f3d --- /dev/null +++ b/test/e2e/tsdx-lint.test.ts @@ -0,0 +1,113 @@ +import * as shell from 'shelljs'; + +import * as util from '../utils/fixture'; + +shell.config.silent = true; + +const testDir = 'e2e'; +const stageName = 'stage-lint'; + +const lintDir = `test/${testDir}/fixtures/lint`; + +describe('tsdx lint', () => { + it('should fail to lint a ts file with errors', () => { + const testFile = `${lintDir}/file-with-lint-errors.ts`; + const output = shell.exec(`node dist/index.js lint ${testFile}`); + expect(output.code).toBe(1); + expect(output.stdout.includes('Parsing error:')).toBe(true); + }); + + it('should succeed linting a ts file without errors', () => { + const testFile = `${lintDir}/file-without-lint-error.ts`; + const output = shell.exec(`node dist/index.js lint ${testFile}`); + expect(output.code).toBe(0); + }); + + it('should fail to lint a ts file with prettier errors', () => { + const testFile = `${lintDir}/file-with-prettier-lint-errors.ts`; + const output = shell.exec(`node dist/index.js lint ${testFile}`); + expect(output.code).toBe(1); + expect(output.stdout.includes('prettier/prettier')).toBe(true); + }); + + it('should fail to lint a tsx file with errors', () => { + const testFile = `${lintDir}/react-file-with-lint-errors.tsx`; + const output = shell.exec(`node dist/index.js lint ${testFile}`); + expect(output.code).toBe(1); + expect(output.stdout.includes('Parsing error:')).toBe(true); + }); + + it('should succeed linting a tsx file without errors', () => { + const testFile = `${lintDir}/react-file-without-lint-error.tsx`; + const output = shell.exec(`node dist/index.js lint ${testFile}`); + expect(output.code).toBe(0); + }); + + it('should succeed linting a ts file with warnings when --max-warnings is not used', () => { + const testFile = `${lintDir}/file-with-lint-warnings.ts`; + const output = shell.exec(`node dist/index.js lint ${testFile}`); + expect(output.code).toBe(0); + expect(output.stdout.includes('@typescript-eslint/no-unused-vars')).toBe( + true + ); + }); + + it('should succeed linting a ts file with fewer warnings than --max-warnings', () => { + const testFile = `${lintDir}/file-with-lint-warnings.ts`; + const output = shell.exec( + `node dist/index.js lint ${testFile} --max-warnings 4` + ); + expect(output.code).toBe(0); + expect(output.stdout.includes('@typescript-eslint/no-unused-vars')).toBe( + true + ); + }); + + it('should succeed linting a ts file with same number of warnings as --max-warnings', () => { + const testFile = `${lintDir}/file-with-lint-warnings.ts`; + const output = shell.exec( + `node dist/index.js lint ${testFile} --max-warnings 3` + ); + expect(output.code).toBe(0); + expect(output.stdout.includes('@typescript-eslint/no-unused-vars')).toBe( + true + ); + }); + + it('should fail to lint a ts file with more warnings than --max-warnings', () => { + const testFile = `${lintDir}/file-with-lint-warnings.ts`; + const output = shell.exec( + `node dist/index.js lint ${testFile} --max-warnings 2` + ); + expect(output.code).toBe(1); + expect(output.stdout.includes('@typescript-eslint/no-unused-vars')).toBe( + true + ); + }); + + it('should not lint', () => { + const output = shell.exec(`node dist/index.js lint`); + expect(output.code).toBe(1); + expect(output.toString()).toContain('Defaulting to "tsdx lint src test"'); + expect(output.toString()).toContain( + 'You can override this in the package.json scripts, like "lint": "tsdx lint src otherDir"' + ); + }); + + describe('when --write-file is used', () => { + beforeEach(() => { + util.teardownStage(stageName); + util.setupStageWithFixture(testDir, stageName, 'build-default'); + }); + + it('should create the file', () => { + const output = shell.exec(`node ../dist/index.js lint --write-file`); + expect(shell.test('-f', '.eslintrc.js')).toBeTruthy(); + expect(output.code).toBe(0); + }); + + afterAll(() => { + util.teardownStage(stageName); + }); + }); +}); diff --git a/test/fixtures/README.md b/test/fixtures/README.md deleted file mode 100644 index c7b211d90..000000000 --- a/test/fixtures/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## Fixtures testing - -here are some fixtures for manual testing things we don't have Jest tests for. - -- `build-default` focuses on our zero config defaults -- `build-invalid` lets us check what happens when we have invalid builds due to type errors -- `build-withConfig` lets us check that `tsdx.config.js` works as expected diff --git a/test/fixtures/build-default/test/blah.test.ts b/test/fixtures/build-default/test/blah.test.ts deleted file mode 100644 index c14b0da80..000000000 --- a/test/fixtures/build-default/test/blah.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { sum } from '../src'; - -describe('fuck', () => { - it('works', () => { - expect(sum(1, 1)).toEqual(2); - }); -}); diff --git a/test/fixtures/build-withConfig/errors/ErrorDev.js b/test/fixtures/build-withConfig/errors/ErrorDev.js deleted file mode 100644 index db795c006..000000000 --- a/test/fixtures/build-withConfig/errors/ErrorDev.js +++ /dev/null @@ -1,9 +0,0 @@ - -function ErrorDev(message) { - const error = new Error(message); - error.name = 'Invariant Violation'; - return error; -} - -export default ErrorDev; - \ No newline at end of file diff --git a/test/fixtures/build-withConfig/errors/ErrorProd.js b/test/fixtures/build-withConfig/errors/ErrorProd.js deleted file mode 100644 index 6adc5f05c..000000000 --- a/test/fixtures/build-withConfig/errors/ErrorProd.js +++ /dev/null @@ -1,15 +0,0 @@ - -function ErrorProd(code) { - // TODO: replace this URL with yours - let url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code; - for (let i = 1; i < arguments.length; i++) { - url += '&args[]=' + encodeURIComponent(arguments[i]); - } - return new Error( - `Minified BuildWithconfig error #${code}; visit ${url} for the full message or ` + - 'use the non-minified dev environment for full errors and additional ' + - 'helpful warnings. ' - ); -} - -export default ErrorProd; diff --git a/test/fixtures/build-withConfig/errors/codes.json b/test/fixtures/build-withConfig/errors/codes.json deleted file mode 100644 index 9bf149d4c..000000000 --- a/test/fixtures/build-withConfig/errors/codes.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "0": "this is an old error that shouldn't be overwritten", - "1": "error occurred! o no" -} diff --git a/test/fixtures/build-withConfig/package.json b/test/fixtures/build-withConfig/package.json deleted file mode 100644 index 3d8a131df..000000000 --- a/test/fixtures/build-withConfig/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "scripts": { - "build": "tsdx build --extractErrors" - }, - "name": "build-withconfig", - "license": "MIT", - "dependencies": { - "tiny-invariant": "^1.0.6", - "tiny-warning": "^1.0.3" - }, - "devDependencies": { - "autoprefixer": "^9.6.1", - "cssnano": "^4.1.10", - "eslint-config-postcss": "^3.0.7", - "rollup-plugin-postcss": "^2.0.3" - } -} diff --git a/test/fixtures/build-withConfig/src/foo.ts b/test/fixtures/build-withConfig/src/foo.ts deleted file mode 100644 index 0abb9556d..000000000 --- a/test/fixtures/build-withConfig/src/foo.ts +++ /dev/null @@ -1 +0,0 @@ -export const foo = () => 'bar'; diff --git a/test/fixtures/build-withConfig/test/blah.test.ts b/test/fixtures/build-withConfig/test/blah.test.ts deleted file mode 100644 index c14b0da80..000000000 --- a/test/fixtures/build-withConfig/test/blah.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { sum } from '../src'; - -describe('fuck', () => { - it('works', () => { - expect(sum(1, 1)).toEqual(2); - }); -}); diff --git a/test/fixtures/build-withConfig/yarn.lock b/test/fixtures/build-withConfig/yarn.lock deleted file mode 100644 index 53698f623..000000000 --- a/test/fixtures/build-withConfig/yarn.lock +++ /dev/null @@ -1,1360 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/q@^1.5.1": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" - integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== - -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -autoprefixer@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47" - integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw== - dependencies: - browserslist "^4.6.3" - caniuse-lite "^1.0.30000980" - chalk "^2.4.2" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.17" - postcss-value-parser "^4.0.0" - -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" - integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== - -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - -browserslist@^4.0.0, browserslist@^4.6.3: - version "4.6.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453" - integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA== - dependencies: - caniuse-lite "^1.0.30000984" - electron-to-chromium "^1.3.191" - node-releases "^1.1.25" - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= - -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000984: - version "1.0.30000989" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9" - integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw== - -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - 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" - -chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - -color-convert@^1.9.0, color-convert@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" - integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.2" - -concat-with-sourcemaps@^1.0.5: - version "1.1.0" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" - integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== - dependencies: - source-map "^0.6.1" - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-modules-loader-core@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz#5908668294a1becd261ae0a4ce21b0b551f21d16" - integrity sha1-WQhmgpShvs0mGuCkziGwtVHyHRY= - dependencies: - icss-replace-symbols "1.1.0" - postcss "6.0.1" - postcss-modules-extract-imports "1.1.0" - postcss-modules-local-by-default "1.2.0" - postcss-modules-scope "1.1.0" - postcss-modules-values "1.3.0" - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede" - integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ== - dependencies: - boolbase "^1.0.0" - css-what "^2.1.2" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-selector-tokenizer@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d" - integrity sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA== - dependencies: - cssesc "^0.1.0" - fastparse "^1.1.1" - regexpu-core "^1.0.0" - -css-tree@1.0.0-alpha.29: - version "1.0.0-alpha.29" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39" - integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg== - dependencies: - mdn-data "~1.1.0" - source-map "^0.5.3" - -css-tree@1.0.0-alpha.33: - version "1.0.0-alpha.33" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.33.tgz#970e20e5a91f7a378ddd0fc58d0b6c8d4f3be93e" - integrity sha512-SPt57bh5nQnpsTBsx/IXbO14sRc9xXu5MtMAVuo0BaQQmyf0NupNPPSoMaqiAF5tDFafYsTkfeH4Q/HCKXkg4w== - dependencies: - mdn-data "2.0.4" - source-map "^0.5.3" - -css-unit-converter@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" - integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= - -css-what@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - -cssesc@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" - integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q= - -cssesc@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" - integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== - -cssnano-preset-default@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" - integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.2" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" - integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" - integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== - dependencies: - postcss "^7.0.0" - -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== - -cssnano@^4.1.10, cssnano@^4.1.8: - version "4.1.10" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" - integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== - dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.7" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -csso@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" - integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== - dependencies: - css-tree "1.0.0-alpha.29" - -define-properties@^1.1.2, define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -dom-serializer@0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.1.tgz#13650c850daffea35d8b626a4cfc4d3a17643fdb" - integrity sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -domelementtype@1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" - integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== - -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -dot-prop@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" - integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== - dependencies: - is-obj "^1.0.0" - -electron-to-chromium@^1.3.191: - version "1.3.241" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.241.tgz#859dc49ab7f90773ed698767372d384190f60cb1" - integrity sha512-Gb9E6nWZlbgjDDNe5cAvMJixtn79krNJ70EDpq/M10lkGo7PGtBUe7Y0CYVHsBScRwi6ybCS+YetXAN9ysAHDg== - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - -entities@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" - integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.12.0, es-abstract@^1.5.1: - version "1.13.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" - integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== - dependencies: - es-to-primitive "^1.2.0" - function-bind "^1.1.1" - has "^1.0.3" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-keys "^1.0.12" - -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -eslint-config-postcss@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/eslint-config-postcss/-/eslint-config-postcss-3.0.7.tgz#390d97d5ac08db9dc57630153cc0c1821559659b" - integrity sha512-5SCkmybZjQhOiFX8FNuqBo9SvCWH6inGXLHQ+BigXUiOGDhBNJuHp6JaF2qKMv+ti6kdk3J1ZFM3NurLww7Nxw== - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -estree-walker@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" - integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== - -fastparse@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" - integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -generic-names@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-1.0.3.tgz#2d786a121aee508876796939e8e3bff836c20917" - integrity sha1-LXhqEhruUIh2eWk56OO/+DbCCRc= - dependencies: - loader-utils "^0.2.16" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= - -has@^1.0.0, has@^1.0.1, has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" - integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" - integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= - -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== - -icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" - integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= - -import-cwd@^2.0.0, import-cwd@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" - integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= - dependencies: - import-from "^2.1.0" - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-from@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" - integrity sha1-M1238qev/VOqpHHUuAId7ja387E= - dependencies: - resolve-from "^3.0.0" - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== - -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" - integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= - dependencies: - has "^1.0.1" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-svg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" - integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== - dependencies: - html-comment-regex "^1.1.0" - -is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== - dependencies: - has-symbols "^1.0.0" - -js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json5@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= - -loader-utils@^0.2.16: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -mdn-data@~1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" - integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA== - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -node-releases@^1.1.25: - version "1.1.28" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.28.tgz#503c3c70d0e4732b84e7aaa2925fbdde10482d4a" - integrity sha512-AQw4emh6iSXnCpDiFe0phYcThiccmkNWMZnFZ+lDJjAP8J0m2fVd59duvUUyuTirQOhIAajTFkzG6FHCLBO59g== - dependencies: - semver "^5.3.0" - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= - -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-keys@^1.0.12: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" - -object.values@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" - integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.12.0" - function-bind "^1.1.1" - has "^1.0.3" - -p-queue@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-2.4.2.tgz#03609826682b743be9a22dba25051bd46724fc34" - integrity sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng== - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -postcss-calc@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" - integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ== - dependencies: - css-unit-converter "^1.1.1" - postcss "^7.0.5" - postcss-selector-parser "^5.0.0-rc.4" - postcss-value-parser "^3.3.1" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-load-config@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" - integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== - dependencies: - cosmiconfig "^5.0.0" - import-cwd "^2.0.0" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -postcss-modules-extract-imports@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb" - integrity sha1-thTJcgvmgW6u41+zpfqh26agXds= - dependencies: - postcss "^6.0.1" - -postcss-modules-local-by-default@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" - integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk= - dependencies: - css-selector-tokenizer "^0.7.0" - postcss "^6.0.1" - -postcss-modules-scope@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" - integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A= - dependencies: - css-selector-tokenizer "^0.7.0" - postcss "^6.0.1" - -postcss-modules-values@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" - integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA= - dependencies: - icss-replace-symbols "^1.1.0" - postcss "^6.0.1" - -postcss-modules@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-1.4.1.tgz#8aa35bd3461db67e27377a7ce770d77b654a84ef" - integrity sha512-btTrbK+Xc3NBuYF8TPBjCMRSp5h6NoQ1iVZ6WiDQENIze6KIYCSf0+UFQuV3yJ7gRHA+4AAtF8i2jRvUpbBMMg== - dependencies: - css-modules-loader-core "^1.1.0" - generic-names "^1.0.3" - lodash.camelcase "^4.3.0" - postcss "^7.0.1" - string-hash "^1.1.1" - -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-selector-parser@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" - integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= - dependencies: - dot-prop "^4.1.1" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^5.0.0-rc.4: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" - integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== - dependencies: - cssesc "^2.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-svgo@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" - integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== - dependencies: - is-svg "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" - integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== - -postcss@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2" - integrity sha1-AA29H47vIXqjaLmiEsX8QLKo8/I= - dependencies: - chalk "^1.1.3" - source-map "^0.5.6" - supports-color "^3.2.3" - -postcss@^6.0.1: - version "6.0.23" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" - integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== - dependencies: - chalk "^2.4.1" - source-map "^0.6.1" - supports-color "^5.4.0" - -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.5: - version "7.0.17" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" - integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -promise.series@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" - integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70= - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - -regenerate@^1.2.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" - integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== - -regexpu-core@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" - integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs= - dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" - -regjsgen@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" - integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= - -regjsparser@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" - integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= - dependencies: - jsesc "~0.5.0" - -reserved-words@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1" - integrity sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE= - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - -resolve@^1.5.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" - integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== - dependencies: - path-parse "^1.0.6" - -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" - integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" - integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= - -rollup-plugin-postcss@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/rollup-plugin-postcss/-/rollup-plugin-postcss-2.0.3.tgz#1fd5b7e1fc7545cb0084d9c99d11b259e41a05e6" - integrity sha512-d12oKl6za/GGXmlytzVPzzTdPCKgti/Kq2kNhtfm5vv9hkNbyrTvizMBm6zZ5rRWX/sIWl3znjIJ8xy6Hofoeg== - dependencies: - chalk "^2.4.2" - concat-with-sourcemaps "^1.0.5" - cssnano "^4.1.8" - import-cwd "^2.1.0" - p-queue "^2.4.2" - pify "^3.0.0" - postcss "^7.0.14" - postcss-load-config "^2.0.0" - postcss-modules "^1.4.1" - promise.series "^0.2.0" - reserved-words "^0.1.2" - resolve "^1.5.0" - rollup-pluginutils "^2.0.1" - style-inject "^0.3.0" - -rollup-pluginutils@^2.0.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97" - integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg== - dependencies: - estree-walker "^0.6.1" - -sax@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semver@^5.3.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - -source-map@^0.5.3, source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -string-hash@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" - integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -style-inject@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/style-inject/-/style-inject-0.3.0.tgz#d21c477affec91811cc82355832a700d22bf8dd3" - integrity sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw== - -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= - dependencies: - has-flag "^1.0.0" - -supports-color@^5.3.0, supports-color@^5.4.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -svgo@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.0.tgz#bae51ba95ded9a33a36b7c46ce9c359ae9154313" - integrity sha512-MLfUA6O+qauLDbym+mMZgtXCGRfIxyQoeH6IKVcFslyODEe/ElJNwr0FohQ3xG4C6HK6bk3KYPPXwHVJk3V5NQ== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.33" - csso "^3.5.1" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= - -tiny-invariant@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73" - integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA== - -tiny-warning@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= - -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= - -util.promisify@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -vendors@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0" - integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw== diff --git a/test/fixtures/util.js b/test/fixtures/util.js deleted file mode 100644 index 5223cc9aa..000000000 --- a/test/fixtures/util.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -const shell = require('shelljs'); -const path = require('path'); -const rootDir = process.cwd(); - -shell.config.silent = true; - -module.exports = { - setupStageWithFixture: (stageName, fixtureName) => { - const stagePath = path.join(rootDir, stageName); - shell.mkdir(stagePath); - shell.exec(`cp -a ${rootDir}/test/fixtures/${fixtureName}/. ${stagePath}/`); - shell.ln( - '-s', - path.join(rootDir, 'node_modules'), - path.join(stagePath, 'node_modules') - ); - shell.cd(stagePath); - }, - - teardownStage: stageName => { - shell.cd(rootDir); - shell.rm('-rf', path.join(rootDir, stageName)); - }, - - rootDir, -}; diff --git a/test/integration/fixtures/README.md b/test/integration/fixtures/README.md new file mode 100644 index 000000000..d0bfd7017 --- /dev/null +++ b/test/integration/fixtures/README.md @@ -0,0 +1,5 @@ +# Integration Test Fixtures Directory + +- `build-options` lets us check that TSDX's flags work as expected +- `build-withConfig` lets us check that `tsdx.config.js` works as expected +- `build-withBabel` lets us check that `.babelrc` works as expected diff --git a/test/integration/fixtures/build-options/package.json b/test/integration/fixtures/build-options/package.json new file mode 100644 index 000000000..1c4970a49 --- /dev/null +++ b/test/integration/fixtures/build-options/package.json @@ -0,0 +1,7 @@ +{ + "scripts": { + "build": "tsdx build --extractErrors" + }, + "name": "build-options", + "license": "MIT" +} diff --git a/test/fixtures/build-withConfig/src/index.ts b/test/integration/fixtures/build-options/src/index.ts similarity index 78% rename from test/fixtures/build-withConfig/src/index.ts rename to test/integration/fixtures/build-options/src/index.ts index 3a23a7132..b0f764913 100644 --- a/test/fixtures/build-withConfig/src/index.ts +++ b/test/integration/fixtures/build-options/src/index.ts @@ -1,8 +1,8 @@ import invariant from 'tiny-invariant'; import warning from 'tiny-warning'; + invariant(true, 'error occurred! o no'); -warning(false, 'warning - water is wet'); -export { foo } from './foo'; +warning(true, 'warning - water is wet'); export const sum = (a: number, b: number) => { if ('development' === process.env.NODE_ENV) { diff --git a/test/fixtures/build-default/tsconfig.json b/test/integration/fixtures/build-options/tsconfig.json similarity index 51% rename from test/fixtures/build-default/tsconfig.json rename to test/integration/fixtures/build-options/tsconfig.json index ecb27d608..bb0179011 100644 --- a/test/fixtures/build-default/tsconfig.json +++ b/test/integration/fixtures/build-options/tsconfig.json @@ -1,29 +1,21 @@ { "compilerOptions": { - "target": "es5", "module": "ESNext", "lib": ["dom", "esnext"], "declaration": true, "sourceMap": true, - "rootDir": "./", + "rootDir": "./src", "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "node", - "baseUrl": "./", - "paths": { - "*": ["src/*", "node_modules/*"] - }, "jsx": "react", - "esModuleInterop": true + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true }, "include": ["src", "types"], -} \ No newline at end of file +} diff --git a/test/integration/fixtures/build-withBabel/.babelrc.js b/test/integration/fixtures/build-withBabel/.babelrc.js new file mode 100644 index 000000000..f5b560d3f --- /dev/null +++ b/test/integration/fixtures/build-withBabel/.babelrc.js @@ -0,0 +1,6 @@ +module.exports = { + presets: [ + // ensure Babel presets are merged and applied + './test-babel-preset' + ] +} diff --git a/test/integration/fixtures/build-withBabel/package.json b/test/integration/fixtures/build-withBabel/package.json new file mode 100644 index 000000000..9e442e40f --- /dev/null +++ b/test/integration/fixtures/build-withBabel/package.json @@ -0,0 +1,7 @@ +{ + "scripts": { + "build": "tsdx build" + }, + "name": "build-withbabel", + "license": "MIT" +} diff --git a/test/integration/fixtures/build-withBabel/src/index.ts b/test/integration/fixtures/build-withBabel/src/index.ts new file mode 100644 index 000000000..3a5c23d07 --- /dev/null +++ b/test/integration/fixtures/build-withBabel/src/index.ts @@ -0,0 +1,8 @@ +export { Title } from './styled'; + +export const sum = (a: number, b: number) => { + if ('development' === process.env.NODE_ENV) { + console.log('fuck'); + } + return a + b; +}; diff --git a/test/integration/fixtures/build-withBabel/src/styled.tsx b/test/integration/fixtures/build-withBabel/src/styled.tsx new file mode 100644 index 000000000..c0dcbc3b0 --- /dev/null +++ b/test/integration/fixtures/build-withBabel/src/styled.tsx @@ -0,0 +1,8 @@ +import styled from 'styled-components/macro'; + +export const Title = styled.h1` + /* this comment should be removed */ + font-size: 1.5em; + text-align: center; + color: palevioletred; +`; diff --git a/test/integration/fixtures/build-withBabel/test-babel-preset.js b/test/integration/fixtures/build-withBabel/test-babel-preset.js new file mode 100644 index 000000000..e319c4f06 --- /dev/null +++ b/test/integration/fixtures/build-withBabel/test-babel-preset.js @@ -0,0 +1,4 @@ +// a simple babel preset to ensure presets are merged and applied +module.exports = () => ({ + plugins: [['replace-identifiers', { sum: 'replacedSum' }]], +}); diff --git a/test/fixtures/build-withConfig/tsconfig.json b/test/integration/fixtures/build-withBabel/tsconfig.json similarity index 51% rename from test/fixtures/build-withConfig/tsconfig.json rename to test/integration/fixtures/build-withBabel/tsconfig.json index ecb27d608..547e772e3 100644 --- a/test/fixtures/build-withConfig/tsconfig.json +++ b/test/integration/fixtures/build-withBabel/tsconfig.json @@ -1,29 +1,21 @@ { "compilerOptions": { - "target": "es5", "module": "ESNext", "lib": ["dom", "esnext"], "declaration": true, "sourceMap": true, - "rootDir": "./", + "rootDir": "./src", "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "node", - "baseUrl": "./", - "paths": { - "*": ["src/*", "node_modules/*"] - }, "jsx": "react", - "esModuleInterop": true + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": false }, "include": ["src", "types"], -} \ No newline at end of file +} diff --git a/test/integration/fixtures/build-withConfig/package.json b/test/integration/fixtures/build-withConfig/package.json new file mode 100644 index 000000000..20b67006d --- /dev/null +++ b/test/integration/fixtures/build-withConfig/package.json @@ -0,0 +1,7 @@ +{ + "scripts": { + "build": "tsdx build" + }, + "name": "build-withconfig", + "license": "MIT" +} diff --git a/test/integration/fixtures/build-withConfig/src/index.css b/test/integration/fixtures/build-withConfig/src/index.css new file mode 100644 index 000000000..51c83342c --- /dev/null +++ b/test/integration/fixtures/build-withConfig/src/index.css @@ -0,0 +1,4 @@ +/* ::placeholder should be autoprefixed, and everything minified */ +.test::placeholder { + color: 'blue'; +} diff --git a/test/fixtures/build-default/src/index.ts b/test/integration/fixtures/build-withConfig/src/index.ts similarity index 83% rename from test/fixtures/build-default/src/index.ts rename to test/integration/fixtures/build-withConfig/src/index.ts index b23b6213f..c0de22055 100644 --- a/test/fixtures/build-default/src/index.ts +++ b/test/integration/fixtures/build-withConfig/src/index.ts @@ -1,4 +1,4 @@ -export { foo } from './foo'; +import './index.css'; export const sum = (a: number, b: number) => { if ('development' === process.env.NODE_ENV) { diff --git a/test/integration/fixtures/build-withConfig/tsconfig.json b/test/integration/fixtures/build-withConfig/tsconfig.json new file mode 100644 index 000000000..bb0179011 --- /dev/null +++ b/test/integration/fixtures/build-withConfig/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "ESNext", + "lib": ["dom", "esnext"], + "declaration": true, + "sourceMap": true, + "rootDir": "./src", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "moduleResolution": "node", + "jsx": "react", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true + }, + "include": ["src", "types"], +} diff --git a/test/fixtures/build-withConfig/tsdx.config.js b/test/integration/fixtures/build-withConfig/tsdx.config.js similarity index 100% rename from test/fixtures/build-withConfig/tsdx.config.js rename to test/integration/fixtures/build-withConfig/tsdx.config.js diff --git a/test/integration/tsdx-build-options.test.ts b/test/integration/tsdx-build-options.test.ts new file mode 100644 index 000000000..d6c286447 --- /dev/null +++ b/test/integration/tsdx-build-options.test.ts @@ -0,0 +1,59 @@ +import * as shell from 'shelljs'; + +import * as util from '../utils/fixture'; +import { execWithCache } from '../utils/shell'; + +shell.config.silent = false; + +const testDir = 'integration'; +const fixtureName = 'build-options'; +const stageName = `stage-integration-${fixtureName}`; + +describe('integration :: tsdx build :: options', () => { + beforeAll(() => { + util.teardownStage(stageName); + util.setupStageWithFixture(testDir, stageName, fixtureName); + }); + + it('should create errors/ dir with --extractErrors', () => { + const output = execWithCache('node ../dist/index.js build --extractErrors'); + + expect(shell.test('-f', 'errors/ErrorDev.js')).toBeTruthy(); + expect(shell.test('-f', 'errors/ErrorProd.js')).toBeTruthy(); + expect(shell.test('-f', 'errors/codes.json')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + it('should have correct errors/codes.json', () => { + const output = execWithCache('node ../dist/index.js build --extractErrors'); + + const errors = require(`../../${stageName}/errors/codes.json`); + expect(errors['0']).toBe('error occurred! o no'); + // TODO: warning is actually not extracted, only invariant + // expect(errors['1']).toBe('warning - water is wet'); + + expect(output.code).toBe(0); + }); + + it('should compile files into a dist directory', () => { + const output = execWithCache('node ../dist/index.js build --extractErrors'); + + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-options.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-options.cjs.production.min.js') + ).toBeTruthy(); + expect(shell.test('-f', 'dist/build-options.esm.js')).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + afterAll(() => { + util.teardownStage(stageName); + }); +}); diff --git a/test/integration/tsdx-build-withBabel.test.ts b/test/integration/tsdx-build-withBabel.test.ts new file mode 100644 index 000000000..e2c84b297 --- /dev/null +++ b/test/integration/tsdx-build-withBabel.test.ts @@ -0,0 +1,69 @@ +import * as shell from 'shelljs'; + +import * as util from '../utils/fixture'; +import { execWithCache, grep } from '../utils/shell'; + +shell.config.silent = false; + +const testDir = 'integration'; +const fixtureName = 'build-withBabel'; +const stageName = `stage-integration-${fixtureName}`; + +describe('integration :: tsdx build :: .babelrc.js', () => { + beforeAll(() => { + util.teardownStage(stageName); + util.setupStageWithFixture(testDir, stageName, fixtureName); + }); + + it('should convert styled-components template tags', () => { + const output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(0); + + // from styled.h1` to styled.h1.withConfig( + const matched = grep(/styled.h1.withConfig\(/, [ + 'dist/build-withbabel.*.js', + ]); + expect(matched).toBeTruthy(); + }); + + // TODO: make styled-components work with its Babel plugin and not just its + // macro by allowing customization of plugin order + it('should remove comments in the CSS', () => { + const output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(0); + + // the comment "should be removed" should no longer be there + const matched = grep(/should be removed/, ['dist/build-withbabel.*.js']); + expect(matched).toBeFalsy(); + }); + + it('should merge and apply presets', () => { + const output = execWithCache('node ../dist/index.js build'); + expect(output.code).toBe(0); + + // ensures replace-identifiers was used + const matched = grep(/replacedSum/, ['dist/build-withbabel.*.js']); + expect(matched).toBeTruthy(); + }); + + it('should compile files into a dist directory', () => { + const output = execWithCache('node ../dist/index.js build'); + + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-withbabel.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-withbabel.cjs.production.min.js') + ).toBeTruthy(); + expect(shell.test('-f', 'dist/build-withbabel.esm.js')).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + afterAll(() => { + util.teardownStage(stageName); + }); +}); diff --git a/test/integration/tsdx-build-withConfig.test.ts b/test/integration/tsdx-build-withConfig.test.ts new file mode 100644 index 000000000..ce69daf6b --- /dev/null +++ b/test/integration/tsdx-build-withConfig.test.ts @@ -0,0 +1,64 @@ +import * as fs from 'fs-extra'; +import * as shell from 'shelljs'; + +import * as util from '../utils/fixture'; +import { execWithCache } from '../utils/shell'; + +shell.config.silent = false; + +const testDir = 'integration'; +const fixtureName = 'build-withConfig'; +const stageName = `stage-integration-${fixtureName}`; + +describe('integration :: tsdx build :: tsdx.config.js', () => { + beforeAll(() => { + util.teardownStage(stageName); + util.setupStageWithFixture(testDir, stageName, fixtureName); + }); + + it('should create a CSS file in the dist/ directory', () => { + const output = execWithCache('node ../dist/index.js build'); + + // TODO: this is kind of subpar naming, rollup-plugin-postcss just names it + // the same as the output file, but with the .css extension + expect(shell.test('-f', 'dist/build-withconfig.cjs.development.css')); + + expect(output.code).toBe(0); + }); + + it('should autoprefix and minify the CSS file', async () => { + const output = execWithCache('node ../dist/index.js build'); + + const cssText = await fs.readFile( + './dist/build-withconfig.cjs.development.css' + ); + + // autoprefixed and minifed output + expect( + cssText.includes('.test::-moz-placeholder{color:"blue"}') + ).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + it('should compile files into a dist directory', () => { + const output = execWithCache('node ../dist/index.js build'); + + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-withconfig.cjs.development.js') + ).toBeTruthy(); + expect( + shell.test('-f', 'dist/build-withconfig.cjs.production.min.js') + ).toBeTruthy(); + expect(shell.test('-f', 'dist/build-withconfig.esm.js')).toBeTruthy(); + + expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + + afterAll(() => { + util.teardownStage(stageName); + }); +}); diff --git a/test/jest.config.json b/test/jest.config.json deleted file mode 100644 index 5ecaf1821..000000000 --- a/test/jest.config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "roots": ["/tests"], - "collectCoverageFrom": ["**/*.js"], - "transform": { - "^.+\\.ts?$": "ts-jest" - }, - "testMatch": [ - "/tests/**/__tests__/**/*.[jt]s?(x)", - "/tests/**/*(*.)@(spec|test).[tj]s?(x)" - ] -} \ No newline at end of file diff --git a/test/tests/lint/tsdx-lint.test.js b/test/tests/lint/tsdx-lint.test.js deleted file mode 100644 index a970c051a..000000000 --- a/test/tests/lint/tsdx-lint.test.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @jest-environment node - */ -'use strict'; - -const shell = require('shelljs'); -const util = require('../../fixtures/util'); - -shell.config.silent = true; - -const stageName = 'stage-lint'; - -describe('tsdx lint', () => { - it('should fail to lint a ts file with errors', () => { - const testFile = 'test/tests/lint/file-with-lint-errors.ts'; - const output = shell.exec(`node dist/index.js lint ${testFile}`); - expect(output.code).toBe(1); - expect(output.stdout.includes('Parsing error:')).toBe(true); - }); - - it('should succeed linting a ts file without errors', () => { - const testFile = 'test/tests/lint/file-without-lint-error.ts'; - const output = shell.exec(`node dist/index.js lint ${testFile}`); - expect(output.code).toBe(0); - }); - - it('should fail to lint a ts file with prettier errors', () => { - const testFile = 'test/tests/lint/file-with-prettier-lint-errors.ts'; - const output = shell.exec(`node dist/index.js lint ${testFile}`); - expect(output.code).toBe(1); - expect(output.stdout.includes('prettier/prettier')).toBe(true); - }); - - it('should fail to lint a tsx file with errors', () => { - const testFile = 'test/tests/lint/react-file-with-lint-errors.tsx'; - const output = shell.exec(`node dist/index.js lint ${testFile}`); - expect(output.code).toBe(1); - expect(output.stdout.includes('Parsing error:')).toBe(true); - }); - - it('should succeed linting a tsx file without errors', () => { - const testFile = 'test/tests/lint/react-file-without-lint-error.tsx'; - const output = shell.exec(`node dist/index.js lint ${testFile}`); - expect(output.code).toBe(0); - }); - - it('should not lint', () => { - const output = shell.exec(`node dist/index.js lint`); - expect(output.code).toBe(1); - expect(output.toString()).toContain('No input files specified.'); - }); - - describe('when --write-file is used', () => { - beforeEach(() => { - util.teardownStage(stageName); - util.setupStageWithFixture(stageName, 'build-default'); - }); - - it('should create the file', () => { - const output = shell.exec(`node ../dist/index.js lint --write-file`); - expect(shell.test('-f', '.eslintrc.js')).toBeTruthy(); - expect(output.code).toBe(0); - }); - - afterAll(() => { - util.teardownStage(stageName); - }); - }); -}); diff --git a/test/tests/tsdx-build.test.js b/test/tests/tsdx-build.test.js deleted file mode 100644 index 2ffdcedf1..000000000 --- a/test/tests/tsdx-build.test.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @jest-environment node - */ -'use strict'; - -const shell = require('shelljs'); -const util = require('../fixtures/util'); - -shell.config.silent = false; - -const stageName = 'stage-build'; - -describe('tsdx build', () => { - beforeAll(() => { - util.teardownStage(stageName); - }); - - it('should compile files into a dist directory', () => { - util.setupStageWithFixture(stageName, 'build-default'); - - const output = shell.exec('node ../dist/index.js build --format esm,cjs'); - - expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); - expect( - shell.test('-f', 'dist/build-default.cjs.development.js') - ).toBeTruthy(); - expect( - shell.test('-f', 'dist/build-default.cjs.production.min.js') - ).toBeTruthy(); - expect(shell.test('-f', 'dist/build-default.esm.js')).toBeTruthy(); - - expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); - - expect(output.code).toBe(0); - }); - - it('should create the library correctly', () => { - util.setupStageWithFixture(stageName, 'build-default'); - - shell.exec('node ../dist/index.js build'); - - const lib = require(`../../${stageName}/dist`); - expect(lib.foo()).toBe('bar'); - }); - - it('should fail gracefully with exit code 1 when build failed', () => { - util.setupStageWithFixture(stageName, 'build-invalid'); - const code = shell.exec('node ../dist/index.js build').code; - expect(code).toBe(1); - }); - - afterEach(() => { - util.teardownStage(stageName); - }); -}); diff --git a/test/tests/utils-safePackageName.test.ts b/test/unit/utils-safePackageName.test.ts similarity index 100% rename from test/tests/utils-safePackageName.test.ts rename to test/unit/utils-safePackageName.test.ts diff --git a/test/utils/fixture.ts b/test/utils/fixture.ts new file mode 100644 index 000000000..148b1e4c0 --- /dev/null +++ b/test/utils/fixture.ts @@ -0,0 +1,29 @@ +import * as path from 'path'; +import * as shell from 'shelljs'; + +export const rootDir = process.cwd(); + +shell.config.silent = true; + +export function setupStageWithFixture( + testDir: string, + stageName: string, + fixtureName: string +): void { + const stagePath = path.join(rootDir, stageName); + shell.mkdir(stagePath); + shell.exec( + `cp -a ${rootDir}/test/${testDir}/fixtures/${fixtureName}/. ${stagePath}/` + ); + shell.ln( + '-s', + path.join(rootDir, 'node_modules'), + path.join(stagePath, 'node_modules') + ); + shell.cd(stagePath); +} + +export function teardownStage(stageName: string): void { + shell.cd(rootDir); + shell.rm('-rf', path.join(rootDir, stageName)); +} diff --git a/test/utils/psKill.js b/test/utils/psKill.js deleted file mode 100644 index 9d823d05b..000000000 --- a/test/utils/psKill.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -const psTree = require('ps-tree'); - -// Loops through processes and kills them -module.exports = (pid, signal = 'SIGKILL', callback) => { - psTree(pid, (err, children) => { - let arr = [pid].concat(children.map(p => p.PID)); - arr = arr.filter((item, poss) => arr.indexOf(item) === poss); - arr.forEach(tpid => { - try { - process.kill(tpid, signal); - } catch (ex) { - const logger = console; - logger.log('Could not kill process', tpid, ex); - } - }); - if (callback) { - callback(); - } - }); -}; diff --git a/test/utils/shell.ts b/test/utils/shell.ts new file mode 100644 index 000000000..ee0b4b7a2 --- /dev/null +++ b/test/utils/shell.ts @@ -0,0 +1,37 @@ +// this file contains helper utils for working with shell.js functions +import * as shell from 'shelljs'; + +shell.config.silent = true; + +// simple shell.exec "cache" that doesn't re-run the same command twice in a row +let prevCommand = ''; +let prevCommandOutput = {} as shell.ShellReturnValue; +export function execWithCache( + command: string, + { noCache = false } = {} +): shell.ShellReturnValue { + // return the old output + if (!noCache && prevCommand === command) return prevCommandOutput; + + const output = shell.exec(command); + + // reset if command is not to be cached + if (noCache) { + prevCommand = ''; + prevCommandOutput = {} as shell.ShellReturnValue; + } else { + prevCommand = command; + prevCommandOutput = output; + } + + return output; +} + +// shell.js grep wrapper returns true if pattern has matches in file +export function grep(pattern: RegExp, fileName: string[]): boolean { + const output = shell.grep(pattern, fileName); + // output.code is always 0 regardless of matched/unmatched patterns + // so need to test output.stdout + // https://github.com/jaredpalmer/tsdx/pull/525#discussion_r395571779 + return Boolean(output.stdout.match(pattern)); +} diff --git a/tsconfig.json b/tsconfig.json index e0d9c086b..b1e42b698 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,9 +11,11 @@ "module": "commonjs", "rootDir": "src", "strict": true, - "noImplicitAny": true, "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, "skipLibCheck": true, - "target": "es2017", + "target": "es2017" } -} \ No newline at end of file +} diff --git a/website/.babelrc b/website/.babelrc new file mode 100644 index 000000000..357e43654 --- /dev/null +++ b/website/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["next/babel"], + "plugins": ["babel-plugin-macros", "./.nextra/babel-plugin-nextjs-mdx-patch"] +} \ No newline at end of file diff --git a/website/.gitignore b/website/.gitignore old mode 100755 new mode 100644 index 1b34df512..c3ee72b81 --- a/website/.gitignore +++ b/website/.gitignore @@ -1,20 +1,4 @@ -# dependencies -/node_modules - -# production -/build - -# generated files -.docusaurus -.cache-loader - -# misc +node_modules +.next .DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* \ No newline at end of file +yarn-error.log \ No newline at end of file diff --git a/website/.nextra/arrow-right.js b/website/.nextra/arrow-right.js new file mode 100644 index 000000000..50c8163f8 --- /dev/null +++ b/website/.nextra/arrow-right.js @@ -0,0 +1,19 @@ +export default ({ width = 24, height = 24, ...props }) => { + return ( + + + + ) +} diff --git a/website/.nextra/babel-plugin-nextjs-mdx-patch.js b/website/.nextra/babel-plugin-nextjs-mdx-patch.js new file mode 100644 index 000000000..c7bfec273 --- /dev/null +++ b/website/.nextra/babel-plugin-nextjs-mdx-patch.js @@ -0,0 +1,34 @@ +/** + * Currently it's not possible to export data fetching functions from MDX pages + * because MDX includes them in `layoutProps`, and Next.js removes them at some + * point, causing a `ReferenceError`. + * + * https://github.com/mdx-js/mdx/issues/742#issuecomment-612652071 + * + * This plugin can be removed once MDX removes `layoutProps`, at least that + * seems to be the current plan. + */ + +// https://nextjs.org/docs/basic-features/data-fetching +const DATA_FETCH_FNS = ['getStaticPaths', 'getStaticProps', 'getServerProps'] + +module.exports = () => { + return { + visitor: { + ObjectProperty(path) { + if ( + DATA_FETCH_FNS.includes(path.node.value.name) && + path.findParent( + (path) => + path.isVariableDeclarator() && + path.node.id.name === 'layoutProps', + ) + ) { + path.remove() + } + }, + }, + } +} + +// https://github.com/vercel/next.js/issues/12053#issuecomment-622939046 diff --git a/website/.nextra/config.js b/website/.nextra/config.js new file mode 100644 index 000000000..0b055a4c5 --- /dev/null +++ b/website/.nextra/config.js @@ -0,0 +1,11 @@ +import userConfig from '../nextra.config'; + +const defaultConfig = { + nextLinks: true, + prevLinks: true, + search: true, +}; + +export default () => { + return { ...defaultConfig, ...userConfig }; +}; diff --git a/website/.nextra/directories.js b/website/.nextra/directories.js new file mode 100644 index 000000000..4d3bf8740 --- /dev/null +++ b/website/.nextra/directories.js @@ -0,0 +1,96 @@ +import preval from 'preval.macro' +import title from 'title' + +const excludes = ['/_app.js', '/_document.js', '/_error.js'] + +// watch all meta files +const meta = {} +function importAll(r) { + return r.keys().forEach(key => { + meta[key.slice(1)] = r(key) + }) +} +importAll(require.context('../pages/', true, /meta\.json$/)) + +// use macro to load the file list +const items = preval` + const { readdirSync, readFileSync } = require('fs') + const { resolve, join } = require('path') + const extension = /\.(mdx?|jsx?)$/ + + function getFiles(dir, route) { + const files = readdirSync(dir, { withFileTypes: true }) + + // go through the directory + const items = files + .map(f => { + const filePath = resolve(dir, f.name) + const fileRoute = join(route, f.name.replace(extension, '').replace(/^index$/, '')) + + if (f.isDirectory()) { + const children = getFiles(filePath, fileRoute) + if (!children.length) return null + return { name: f.name, children, route: fileRoute } + } else if (f.name === 'meta.json') { + return null + } else if (extension.test(f.name)) { + return { name: f.name.replace(extension, ''), route: fileRoute } + } + }) + .map(item => { + if (!item) return + return { ...item, metaPath: join(route, 'meta.json') } + }) + .filter(Boolean) + + return items + } + module.exports = getFiles(join(process.cwd(), 'pages'), '/') +` + +const attachPageConfig = function (items) { + let folderMeta = null + let fnames = null + + return items + .filter(item => !excludes.includes(item.name)) + .map(item => { + const { metaPath, ...rest } = item + folderMeta = meta[metaPath] + if (folderMeta) { + fnames = Object.keys(folderMeta) + } + + const pageConfig = folderMeta?.[item.name] + + if (rest.children) rest.children = attachPageConfig(rest.children) + + if (pageConfig) { + if (typeof pageConfig === 'string') { + return { ...rest, title: pageConfig } + } + return { ...rest, ...pageConfig } + } else { + if (folderMeta) { + return null + } + return { ...rest, title: title(item.name) } + } + }) + .filter(Boolean) + .sort((a, b) => { + if (folderMeta) { + return fnames.indexOf(a.name) - fnames.indexOf(b.name) + } + // by default, we put directories first + if (!!a.children !== !!b.children) { + return !!a.children ? -1 : 1 + } + // sort by file name + return a.name < b.name ? -1 : 1 + }) +} + +export default () => { + return attachPageConfig(items) +} diff --git a/website/.nextra/docsearch.js b/website/.nextra/docsearch.js new file mode 100644 index 000000000..8dd6f4757 --- /dev/null +++ b/website/.nextra/docsearch.js @@ -0,0 +1,44 @@ +import { useRef, useEffect } from 'react' + +export default function () { + const input = useRef(null) + + useEffect(() => { + const inputs = ['input', 'select', 'button', 'textarea'] + + const down = (e) => { + if ( + document.activeElement && + inputs.indexOf(document.activeElement.tagName.toLowerCase() !== -1) + ) { + if (e.key === '/') { + e.preventDefault() + input.current?.focus() + } + } + } + + window.addEventListener('keydown', down) + return () => window.removeEventListener('keydown', down) + }, []) + + useEffect(() => { + if (window.docsearch) { + window.docsearch({ + apiKey: '247dd86c8ddbbbe6d7a2d4adf4f3a68a', + indexName: 'vercel_swr', + inputSelector: 'input#algolia-doc-search' + }) + } + }, []) + + return
+ +
+} diff --git a/website/.nextra/github-icon.js b/website/.nextra/github-icon.js new file mode 100644 index 000000000..46b77c994 --- /dev/null +++ b/website/.nextra/github-icon.js @@ -0,0 +1,5 @@ +export default ({ height = 40 }) => { + return + + +} \ No newline at end of file diff --git a/website/.nextra/layout.js b/website/.nextra/layout.js new file mode 100644 index 000000000..18a857bd9 --- /dev/null +++ b/website/.nextra/layout.js @@ -0,0 +1,332 @@ +import React, { + useState, + useEffect, + useMemo, + useContext, + createContext, +} from 'react'; +import { useRouter } from 'next/router'; +import Head from 'next/head'; +import Link from 'next/link'; +import slugify from '@sindresorhus/slugify'; +import 'focus-visible'; +import cn from 'classnames'; +import { SkipNavContent } from '@reach/skip-nav'; + +import Theme from './theme'; +import SSGContext from './ssg'; +import Search from './search'; +// import DocSearch from './docsearch' +import GitHubIcon from './github-icon'; +import ArrowRight from './arrow-right'; + +import getDirectories from './directories'; +import getConfig from './config'; +import * as ReactDOM from 'react-dom/server'; +import stripHtml from 'string-strip-html'; +const config = getConfig(); +const directories = getDirectories(); +const TreeState = new Map(); +const titleType = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; +const MenuContext = createContext(false); + +const flatten = list => { + return list.reduce((flat, toFlatten) => { + return flat.concat( + toFlatten.children ? flatten(toFlatten.children) : toFlatten + ); + }, []); +}; + +const flatDirectories = flatten(directories); + +function Folder({ item, anchors }) { + const route = useRouter().route + '/'; + const active = route.startsWith(item.route + '/'); + const open = TreeState[item.route] ?? true; + const [_, render] = useState(false); + + useEffect(() => { + if (active) { + TreeState[item.route] = true; + } + }, [active]); + + return ( +
  • + +
    + +
    +
  • + ); +} + +function File({ item, anchors }) { + const { setMenu } = useContext(MenuContext); + const route = useRouter().route + '/'; + const active = route.startsWith(item.route + '/'); + + let title = item.title; + // if (item.title.startsWith('> ')) { + // title = title.substr(2) + if (anchors?.length) { + if (active) { + return ( +
  • + + {title} + + +
  • + ); + } + } + + return ( +
  • + + setMenu(false)} className="focus:shadow-outline"> + {title} + + +
  • + ); +} + +function Menu({ dir, anchors }) { + return ( +
      + {dir.map(item => { + if (item.children) { + return ; + } + return ; + })} +
    + ); +} + +function Sidebar({ show, anchors }) { + return ( + + ); +} + +const NextLink = ({ currentIndex }) => { + let next = flatDirectories[currentIndex + 1]; + + if (!config.nextLinks || !next) { + return null; + } + + return ( + + + {next.title} + + + + ); +}; + +const PrevLink = ({ currentIndex }) => { + let prev = flatDirectories[currentIndex - 1]; + + if (!config.prevLinks || !prev) { + return null; + } + + return ( + + + + {prev.title} + + + ); +}; + +const Layout = ({ filename, full, title: _title, ssg = {}, children }) => { + const [menu, setMenu] = useState(false); + const router = useRouter(); + const { route, pathname } = router; + + const filepath = route.slice(0, route.lastIndexOf('/') + 1); + const titles = React.Children.toArray(children).filter(child => + titleType.includes(child.props.mdxType) + ); + const anchors = titles + .filter(child => child.props.mdxType === 'h2') + .map(child => child.props.children); + + useEffect(() => { + if (menu) { + document.body.classList.add('overflow-hidden'); + } else { + document.body.classList.remove('overflow-hidden'); + } + }, [menu]); + + const currentIndex = useMemo( + () => flatDirectories.findIndex(dir => dir.route === pathname), + [flatDirectories, pathname] + ); + + const title = + flatDirectories[currentIndex]?.title || + titles.find(child => child.props.mdxType === 'h1')?.props.children || + 'Untitled'; + + const props = { + filepath: filepath + filename, + route, + }; + + return ( + <> + + + {title} + {config.titleSuffix || ''} + + {config.head ? config.head(props) : null} + +
    + +
    + + + + + {full ? ( + + {children} + + ) : ( + <> + + +
    + {children} +
    + + +
    + + {config.footer ? config.footer(props) : null} +
    +
    {' '} +
    + + )} +
    +
    +
    + + ); +}; + +export default filename => { + return props => ; +}; diff --git a/website/.nextra/nextra-loader.js b/website/.nextra/nextra-loader.js new file mode 100644 index 000000000..b3a4dab9d --- /dev/null +++ b/website/.nextra/nextra-loader.js @@ -0,0 +1,8 @@ +module.exports = function(source, map) { + this.cacheable() + const fileName = this.resourcePath.slice(this.resourcePath.lastIndexOf('/') + 1) + const prefix = `import withNextraLayout from '.nextra/layout'\n\n` + const suffix = `\n\nexport default withNextraLayout("${fileName}")` + source = prefix + source + suffix + this.callback(null, source, map) +} diff --git a/website/.nextra/nextra.js b/website/.nextra/nextra.js new file mode 100644 index 000000000..7b16277c1 --- /dev/null +++ b/website/.nextra/nextra.js @@ -0,0 +1,33 @@ +const path = require('path') + +module.exports = (pluginOptions = { + extension: /\.mdx?$/ +}) => (nextConfig = { + pageExtensions: ['js', 'jsx', 'md', 'mdx'] +}) => { + const extension = pluginOptions.extension || /\.mdx$/ + + return Object.assign({}, nextConfig, { + webpack(config, options) { + config.module.rules.push({ + test: extension, + use: [ + options.defaultLoaders.babel, + { + loader: '@mdx-js/loader', + options: pluginOptions.options + }, + { + loader: path.resolve('.nextra', 'nextra-loader.js') + } + ] + }) + + if (typeof nextConfig.webpack === 'function') { + return nextConfig.webpack(config, options) + } + + return config + } + }) +} diff --git a/website/.nextra/search.js b/website/.nextra/search.js new file mode 100644 index 000000000..511f50b89 --- /dev/null +++ b/website/.nextra/search.js @@ -0,0 +1,149 @@ +import { useMemo, useCallback, useRef, useState, useEffect } from 'react'; +import matchSorter from 'match-sorter'; +import cn from 'classnames'; +import { useRouter } from 'next/router'; +import Link from 'next/link'; + +const Item = ({ title, active, href, onMouseOver, search }) => { + const highlight = title.toLowerCase().indexOf(search.toLowerCase()); + + return ( + + +
  • + {title.substring(0, highlight)} + + {title.substring(highlight, highlight + search.length)} + + {title.substring(highlight + search.length)} +
  • +
    + + ); +}; + +const Search = ({ directories }) => { + const router = useRouter(); + const [show, setShow] = useState(false); + const [search, setSearch] = useState(''); + const [active, setActive] = useState(0); + const input = useRef(null); + + const results = useMemo(() => { + if (!search) return []; + + // Will need to scrape all the headers from each page and search through them here + // (similar to what we already do to render the hash links in sidebar) + // We could also try to search the entire string text from each page + return matchSorter(directories, search, { keys: ['title'] }); + }, [search]); + + const handleKeyDown = useCallback( + (e) => { + switch (e.key) { + case 'ArrowDown': { + e.preventDefault(); + if (active + 1 < results.length) { + setActive(active + 1); + } + break; + } + case 'ArrowUp': { + e.preventDefault(); + if (active - 1 >= 0) { + setActive(active - 1); + } + break; + } + case 'Enter': { + router.push(results[active].route); + break; + } + } + }, + [active, results, router] + ); + + useEffect(() => { + setActive(0); + }, [search]); + + useEffect(() => { + const inputs = ['input', 'select', 'button', 'textarea']; + + const down = (e) => { + if ( + document.activeElement && + inputs.indexOf(document.activeElement.tagName.toLowerCase() !== -1) + ) { + if (e.key === '/') { + e.preventDefault(); + input.current.focus(); + } else if (e.key === 'Escape') { + setShow(false); + } + } + }; + + window.addEventListener('keydown', down); + return () => window.removeEventListener('keydown', down); + }, []); + + const renderList = show && results.length > 0; + + return ( +
    + {renderList && ( +
    setShow(false)} /> + )} +
    + + + +
    + { + setSearch(e.target.value); + setShow(true); + }} + className="appearance-none pl-8 border rounded-md py-2 pr-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline w-full" + type="search" + placeholder='Search ("/" to focus)' + onKeyDown={handleKeyDown} + onFocus={() => setShow(true)} + ref={input} + aria-label="Search documentation" + /> + {renderList && ( +
      + {results.map((res, i) => { + return ( + setActive(i)} + /> + ); + })} +
    + )} +
    + ); +}; + +export default Search; diff --git a/website/.nextra/ssg.js b/website/.nextra/ssg.js new file mode 100644 index 000000000..2e4fb8f9a --- /dev/null +++ b/website/.nextra/ssg.js @@ -0,0 +1,6 @@ +import { createContext, useContext } from 'react' + +const SSGContext = createContext({}) + +export default SSGContext +export const useSSG = () => useContext(SSGContext) diff --git a/website/.nextra/styles.css b/website/.nextra/styles.css new file mode 100644 index 000000000..50350ac2a --- /dev/null +++ b/website/.nextra/styles.css @@ -0,0 +1,211 @@ +@tailwind base; + +html { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; +} +@supports (font-variation-settings: normal) { + html { + font-family: 'Inter var', -apple-system, BlinkMacSystemFont, 'Segoe UI', + 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + } +} + +html { + @apply subpixel-antialiased; + font-size: 16px; + font-feature-settings: 'rlig' 1, 'calt' 1, 'ss01' 1; +} +body { + @apply bg-white; +} + +h1 { + @apply text-4xl font-bold tracking-tight; +} +h2 { + @apply text-3xl font-semibold tracking-tight mt-10; + @apply border-b; +} +h3 { + @apply text-2xl font-semibold tracking-tight mt-8; +} +h4 { + @apply text-xl font-semibold tracking-tight mt-8; +} +h5 { + @apply text-lg font-semibold tracking-tight mt-8; +} +h6 { + @apply text-base font-semibold tracking-tight mt-8; +} +a { + @apply text-blue-600 underline; +} +p { + @apply mt-6 leading-7; +} +hr { + @apply my-8; +} +code { + @apply p-1 text-sm text-gray-800 bg-gray-500 bg-opacity-25 rounded; +} +pre { + @apply p-4 bg-gray-200 rounded-lg mt-6 mb-4 overflow-x-auto scrolling-touch; +} +pre code { + @apply p-0 text-black bg-transparent rounded-none; +} +a code { + @apply text-current no-underline; +} +figure { + @apply mb-8 relative; +} +video { + @apply absolute top-0 left-0; + cursor: pointer; +} +figure figcaption { + @apply text-sm text-gray-600; +} +@media (min-width: 768px) { + figure { + /* allow figures to overflow, but not exceeding the viewport width */ + @apply -mr-56; + max-width: calc(100vw - 20rem); + } +} + +table { + @apply my-8 w-full text-gray-700 text-sm; +} + +table > thead > tr { + @apply border-b border-t rounded-t-lg; +} + +table > thead > tr > th { + @apply px-3 py-2 text-left text-sm font-bold bg-gray-200 text-gray-700; +} + +table > tbody > tr { + @apply border-b; +} + +table > tbody > tr > td { + @apply p-3; +} + +table > tbody > tr > td:not(:first-child) > code { + @apply text-xs; +} + +table > tbody > tr > td > a > code, +table > tbody > tr > td > code { + @apply text-sm; +} +table > tbody > tr > td > a { + @apply text-blue-600 font-semibold transition-colors duration-150 ease-out; +} +table > tbody > tr > td > a:hover { + @apply text-blue-800 transition-colors duration-150 ease-out; +} +@tailwind components; +@tailwind utilities; + +.main-container { + min-height: 100vh; +} + +.sidebar { + @apply select-none; +} +.sidebar ul ul { + @apply ml-5; +} +.sidebar a:focus-visible, +.sidebar button:focus-visible { + @apply shadow-outline; +} +.sidebar li.active > a { + @apply font-semibold text-black bg-gray-200; +} +.sidebar button, +.sidebar a { + @apply block w-full text-left text-base text-black no-underline text-gray-600 mt-1 p-2 rounded select-none outline-none; + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; +} +.sidebar a:hover, +.sidebar button:hover { + @apply text-gray-900 bg-gray-100; +} + +.sidebar .active-route > button { + @apply text-black font-medium; +} + +.sidebar .active-route > button:hover { + @apply text-current bg-transparent cursor-default; +} + +content ul { + @apply list-disc ml-6 mt-6; +} +content li { + @apply mt-2; +} +.subheading-anchor { + margin-top: -84px; + display: inline-block; + position: absolute; + width: 1px; +} + +.subheading-anchor + a:hover .anchor-icon, +.subheading-anchor + a:focus .anchor-icon { + opacity: 1; +} +.anchor-icon { + opacity: 0; + @apply ml-2 text-gray-500; +} + +h2 a { + @apply no-underline; +} +input[type='search']::-webkit-search-decoration, +input[type='search']::-webkit-search-cancel-button, +input[type='search']::-webkit-search-results-button, +input[type='search']::-webkit-search-results-decoration { + -webkit-appearance: none; +} + +.search-overlay { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--category-header span { + display: inline-block; +} +.algolia-autocomplete .ds-dropdown-menu { + width: 500px; + min-width: 300px; + max-width: calc(100vw - 50px); +} + +[data-reach-skip-link] { + @apply sr-only; +} + +[data-reach-skip-link]:focus { + @apply not-sr-only fixed ml-6 top-0 bg-white text-lg px-6 py-2 mt-2 outline-none shadow-outline z-50; +} diff --git a/website/.nextra/theme.js b/website/.nextra/theme.js new file mode 100644 index 000000000..e27834972 --- /dev/null +++ b/website/.nextra/theme.js @@ -0,0 +1,194 @@ +import { MDXProvider } from '@mdx-js/react'; +import * as ReactDOM from 'react-dom/server'; +import Link from 'next/link'; +import Highlight, { defaultProps } from 'prism-react-renderer'; +import stripHtml from 'string-strip-html'; +import slugify from '@sindresorhus/slugify'; + +const THEME = { + plain: { + color: '#000', + backgroundColor: 'transparent', + }, + styles: [ + { + types: ['keyword'], + style: { + color: '#ff0078', + fontWeight: 'bold', + }, + }, + { + types: ['comment'], + style: { + color: '#999', + fontStyle: 'italic', + }, + }, + { + types: ['string', 'url', 'attr-value'], + style: { + color: '#028265', + }, + }, + { + types: ['builtin', 'char', 'constant', 'language-javascript'], + style: { + color: '#000', + }, + }, + { + types: ['attr-name'], + style: { + color: '#d9931e', + fontStyle: 'normal', + }, + }, + { + types: ['punctuation', 'operator'], + style: { + color: '#333', + }, + }, + { + types: ['number', 'function', 'tag'], + style: { + color: '#0076ff', + }, + }, + { + types: ['boolean', 'regex'], + style: { + color: '#d9931e', + }, + }, + ], +}; + +// Anchor links + +const HeaderLink = ({ tag: Tag, children, ...props }) => { + const slug = slugify(ReactDOM.renderToString(children) || ''); + return ( + + + + {children} + + # + + + + ); +}; + +const H2 = ({ children, ...props }) => { + return ( + + {children} + + ); +}; + +const H3 = ({ children, ...props }) => { + return ( + + {children} + + ); +}; + +const H4 = ({ children, ...props }) => { + return ( + + {children} + + ); +}; + +const H5 = ({ children, ...props }) => { + return ( + + {children} + + ); +}; + +const H6 = ({ children, ...props }) => { + return ( + + {children} + + ); +}; + +const A = ({ children, ...props }) => { + const isExternal = props.href?.startsWith('https://'); + if (isExternal) { + return ( + + {children} + + ); + } + return ( + + {children} + + ); +}; + +const Code = ({ children, className, highlight, ...props }) => { + if (!className) return {children}; + + const highlightedLines = highlight ? highlight.split(',').map(Number) : []; + + // https://mdxjs.com/guides/syntax-highlighting#all-together + const language = className.replace(/language-/, ''); + return ( + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( + + {tokens.map((line, i) => ( +
    + {line.map((token, key) => ( + + ))} +
    + ))} +
    + )} +
    + ); +}; + +const components = { + h2: H2, + h3: H3, + h4: H4, + h5: H5, + h6: H6, + a: A, + code: Code, +}; + +export default ({ children }) => { + return {children}; +}; diff --git a/website/README.md b/website/README.md deleted file mode 100755 index 71505291a..000000000 --- a/website/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Website - -This website is built using Docusaurus 2, a modern static website generator. - -### Installation - -``` -$ yarn -``` - -### Local Development - -``` -$ yarn start -``` - -This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. - -### Build - -``` -$ yarn build -``` - -This command generates static content into the `build` directory and can be served using any static contents hosting service. - -### Deployment - -``` -$ GIT_USER= USE_SSH=1 yarn deploy -``` - -If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/website/blog/2019-08-28-hello-tsdx.md b/website/blog/2019-08-28-hello-tsdx.md deleted file mode 100755 index 901702bbb..000000000 --- a/website/blog/2019-08-28-hello-tsdx.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: hello-tsdx -title: Hello TSDX -author: Jared Palmer -authorTitle: Creator -authorURL: https://palmer.net -authorImageURL: https://avatars2.githubusercontent.com/u/4060187?s=180&v=4 -authorTwitter: jaredpalmer -tags: [tsdx] ---- - -Welcome to the tsdx docs! [We set it up today](https://github.com/palmerhq/tsdx/pull/180). diff --git a/website/components/features.js b/website/components/features.js new file mode 100644 index 000000000..1fd11ffcd --- /dev/null +++ b/website/components/features.js @@ -0,0 +1,819 @@ +import React from 'react'; +import styles from './features.module.css'; + +const Feature = ({ text, icon }) => ( +
    + {icon} +

    {text}

    +
    +); + +export default () => ( +
    +

    + Zero-config CLI for TypeScript package development +

    +
    + CJS, ESM, UMD} + icon={ + + + + + } + /> + Treeshaking} + icon={ + + + + + + + + } + /> + + Live Playground + + } + icon={ + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + + + } + /> + + + + } + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + /> + + + + + + + } + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + /> + + + + + + + } + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + /> +
    +
    +); diff --git a/website/components/features.module.css b/website/components/features.module.css new file mode 100644 index 000000000..cba1aec08 --- /dev/null +++ b/website/components/features.module.css @@ -0,0 +1,27 @@ +.features { + display: flex; + flex-wrap: wrap; + margin: 2.5rem -0.5rem 2rem; +} + +.feature { + flex: 0 0 33%; + align-items: center; + display: inline-flex; + padding: 0 0.5rem 1.5rem; + margin: 0 auto; +} +.feature h4 { + margin: 0 0 0 0.5rem; + font-weight: 700; + font-size: 0.95rem; + white-space: nowrap; +} +@media (max-width: 860px) { + .feature { + padding-left: 0; + } + .feature h4 { + font-size: 0.75rem; + } +} diff --git a/website/components/logo.js b/website/components/logo.js new file mode 100644 index 000000000..98be9e794 --- /dev/null +++ b/website/components/logo.js @@ -0,0 +1,28 @@ +import React from 'react'; + +export const Logo = ({ height }) => ( + + + + + + + +); diff --git a/website/components/video.js b/website/components/video.js new file mode 100644 index 000000000..9b4b65ade --- /dev/null +++ b/website/components/video.js @@ -0,0 +1,52 @@ +import React, { useRef, useCallback, useEffect } from 'react'; +import { useInView } from 'react-intersection-observer'; +import 'intersection-observer'; + +export default ({ src, caption, ratio }) => { + const [inViewRef, inView] = useInView({ + threshold: 1, + }); + const videoRef = useRef(); + + const setRefs = useCallback( + node => { + // Ref's from useRef needs to have the node assigned to `current` + videoRef.current = node; + // Callback refs, like the one from `useInView`, is a function that takes the node as an argument + inViewRef(node); + + if (node) { + node.addEventListener('click', function() { + if (this.paused) { + this.play(); + } else { + this.pause(); + } + }); + } + }, + [inViewRef] + ); + + useEffect(() => { + if (!videoRef || !videoRef.current) { + return; + } + + if (inView) { + videoRef.current.play(); + } else { + videoRef.current.pause(); + } + }, [inView]); + + return ( +
    +
    + + {caption &&
    {caption}
    } +
    + ); +}; diff --git a/website/docs/contributing.md b/website/docs/contributing.md deleted file mode 100644 index a19df8192..000000000 --- a/website/docs/contributing.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: contributing -title: Contributor's Guide ---- - -Thanks for your interest in TSDX! You are very welcome to contribute. - -> ⚠️If you are proposing a new feature, make sure to [open an issue](https://github.com/palmerhq/tsdx/issues/new/choose) first to make sure it is inline with the project goals. - -If you'd like to work on any existing issues, [please be our guest](https://github.com/palmerhq/tsdx/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)! - -## Setup - -0. First, remove any existing `tsdx` global installations that may conflict. - - ``` - yarn global remove tsdx # or npm uninstall -g tsdx - ``` - -1. Fork this repository to your own GitHub account and clone it to your local device: - - ``` - git clone https://github.com/your-name/tsdx.git - cd tsdx - ``` - -1. Install the dependencies and build the Typescript files to Javascript: - - ``` - yarn && yarn build - ``` - - > **Note:** you'll need to run `yarn build` any time you want to see your changes, or run `yarn watch` to leave it in watch mode. - -1. Make it so running `tsdx` anywhere will run your local dev version: - - ``` - yarn link - ``` - -4) To use your local version when running `yarn build`/`yarn start`/`yarn test` in a TSDX project, run this in the project: - - ``` - yarn link tsdx - ``` - - You should see a success message: `success Using linked package for "tsdx".` The project will now use the locally linked version instead of a copy from `node_modules`. - -## Submitting a PR - -Be sure to run `yarn test` before you make your PR to make sure you haven't broken anything. diff --git a/website/docs/docusaurus.md b/website/docs/docusaurus.md deleted file mode 100755 index de0ceb363..000000000 --- a/website/docs/docusaurus.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -id: docusaurus -title: Docusaurus Style Guide ---- - -TSDX docs use Docusaurus. This is their default Style Guide. - -You can write content using [GitHub-flavored Markdown syntax](https://github.github.com/gfm/). - -## Markdown Syntax - -To serve as an example page when styling markdown based Docusaurus sites. - -## Headers - -# H1 - Create the best documentation - -## H2 - Create the best documentation - -### H3 - Create the best documentation - -#### H4 - Create the best documentation - -##### H5 - Create the best documentation - -###### H6 - Create the best documentation - ---- - -## Emphasis - -Emphasis, aka italics, with _asterisks_ or _underscores_. - -Strong emphasis, aka bold, with **asterisks** or **underscores**. - -Combined emphasis with **asterisks and _underscores_**. - -Strikethrough uses two tildes. ~~Scratch this.~~ - ---- - -## Lists - -1. First ordered list item -1. Another item ⋅⋅\* Unordered sub-list. -1. Actual numbers don't matter, just that it's a number ⋅⋅1. Ordered sub-list -1. And another item. - -⋅⋅⋅You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). - -⋅⋅⋅To have a line break without a paragraph, you will need to use two trailing spaces.⋅⋅ ⋅⋅⋅Note that this line is separate, but within the same paragraph.⋅⋅ ⋅⋅⋅(This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.) - -- Unordered list can use asterisks - -* Or minuses - -- Or pluses - ---- - -## Links - -[I'm an inline-style link](https://www.google.com) - -[I'm an inline-style link with title](https://www.google.com "Google's Homepage") - -[I'm a reference-style link][arbitrary case-insensitive reference text] - -[I'm a relative reference to a repository file](../blob/master/LICENSE) - -[You can use numbers for reference-style link definitions][1] - -Or leave it empty and use the [link text itself]. - -URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or and sometimes example.com (but not on Github, for example). - -Some text to show that the reference links can follow later. - -[arbitrary case-insensitive reference text]: https://www.mozilla.org -[1]: http://slashdot.org -[link text itself]: http://www.reddit.com - ---- - -## Images - -Here's our logo (hover to see the title text): - -Inline-style: ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 1') - -Reference-style: ![alt text][logo] - -[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2' - ---- - -## Code - -```javascript -var s = 'JavaScript syntax highlighting'; -alert(s); -``` - -```python -s = "Python syntax highlighting" -print s -``` - -``` -No language indicated, so no syntax highlighting. -But let's throw in a tag. -``` - ---- - -## Tables - -Colons can be used to align columns. - -| Tables | Are | Cool | -| ------------- | :-----------: | -----: | -| col 3 is | right-aligned | \$1600 | -| col 2 is | centered | \$12 | -| zebra stripes | are neat | \$1 | - -There must be at least 3 dashes separating each header cell. The outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown. - -| Markdown | Less | Pretty | -| -------- | --------- | ---------- | -| _Still_ | `renders` | **nicely** | -| 1 | 2 | 3 | - ---- - -## Blockquotes - -> Blockquotes are very handy in email to emulate reply text. This line is part of the same quote. - -Quote break. - -> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can _put_ **Markdown** into a blockquote. - ---- - -## Inline HTML - -
    -
    Definition list
    -
    Is something people use sometimes.
    - -
    Markdown in HTML
    -
    Does *not* work **very** well. Use HTML tags.
    -
    - ---- - -## Line Breaks - -Here's a line for us to start with. - -This line is separated from the one above by two newlines, so it will be a _separate paragraph_. - -This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the _same paragraph_. diff --git a/website/docs/features.md b/website/docs/features.md deleted file mode 100644 index 9ccae8853..000000000 --- a/website/docs/features.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: features -title: Features ---- - -## Features - -TSDX comes with the "battery-pack included" and is part of a complete TypeScript breakfast: - -- Bundles your code with [Rollup](https://github.com/rollup/rollup) and outputs multiple module formats (CJS & ESM by default, and also UMD if you want) plus development and production builds -- Comes with treeshaking, ready-to-rock lodash optimizations, and minification/compression -- Live reload / watch-mode -- Works with React -- Human readable error messages (and in VSCode-friendly format) -- Bundle size snapshots -- Opt-in to extract `invariant` error codes -- Jest test runner setup with sensible defaults via `tsdx test` -- Zero-config, single dependency - -## Inspiration - -TSDX is ripped out of [Formik's](https://github.com/jaredpalmer/formik) build tooling. TSDX is very similar to [@developit/microbundle](https://github.com/developit/microbundle), but that is because Formik's Rollup configuration and Microbundle's internals have converged around similar plugins over the last year or so. - -### Comparison to Microbundle - -- TSDX includes out-of-the-box test running via Jest -- TSDX includes a bootstrap command and default package template -- TSDX is 100% TypeScript focused. While yes, TSDX does use Babel to run a few optimizations (related to treeshaking and lodash), it does not support custom babel configurations. -- TSDX outputs distinct development and production builds (like React does) for CJS and UMD builds. This means you can include rich error messages and other dev-friendly goodies without sacrificing final bundle size. diff --git a/website/docs/starthere.md b/website/docs/starthere.md deleted file mode 100755 index 31c1c442f..000000000 --- a/website/docs/starthere.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -id: starthere -title: Getting Started ---- - -## Quick Start - -``` -npx tsdx create mylib -cd mylib -yarn start -``` - -That's it. You don't need to worry about setting up Typescript or Rollup or Jest or other plumbing. Just start editing `src/index.ts` and go! - -Below is a list of commands you will probably find useful: - -### `npm start` or `yarn start` - -Runs the project in development/watch mode. Your project will be rebuilt upon changes. TSDX has a special logger for your convenience. Error messages are pretty printed and formatted for compatibility VS Code's Problems tab. - - - -Your library will be rebuilt if you make edits. - -### `npm run build` or `yarn build` - -Bundles the package to the `dist` folder. -The package is optimized and bundled with Rollup into multiple formats (CommonJS, UMD, and ES Module). - - - -### `npm test` or `yarn test` - -Runs the test watcher (Jest) in an interactive mode. -By default, runs tests related to files changed since the last commit. - -### `npm run lint` or `yarn lint` - -Runs Eslint with Prettier on .ts and .tsx files. -If you want to customize eslint you can add an `eslint` block to your package.json, or you can run `yarn lint --write-file` and edit the generated `.eslintrc.js` file. diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js deleted file mode 100755 index db2f89d88..000000000 --- a/website/docusaurus.config.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -module.exports = { - title: 'TSDX', - tagline: 'Zero-config TypeScript package development', - url: 'https://your-docusaurus-test-site.com', - baseUrl: '/', - favicon: 'img/favicon.ico', - organizationName: 'palmerhq', // Usually your GitHub org/user name. - projectName: 'tsdx', // Usually your repo name. - themeConfig: { - navbar: { - // title: 'My Site', - logo: { - alt: 'TSDX Logo', - src: 'img/logo.svg', - }, - links: [ - { to: 'docs/starthere', label: 'Docs', position: 'right' }, - { to: 'blog', label: 'Blog', position: 'right' }, - { - href: 'https://github.com/palmerhq/tsdx', - label: 'GitHub', - position: 'right', - }, - ], - }, - footer: { - style: 'dark', - links: [ - { - title: 'Docs', - items: [ - { - label: 'Docs', - to: 'docs/starthere', - }, - ], - }, - // { - // title: 'Community', - // items: [ - // { - // label: 'Discord', - // href: 'https://discordapp.com/invite/docusaurus', - // }, - // ], - // }, - { - title: 'Social', - items: [ - { - label: 'Blog', - to: 'blog', - }, - ], - }, - ], - - copyright: `Copyright © ${new Date().getFullYear()} The Palmer Group. Built with Docusaurus.`, - }, - }, - presets: [ - [ - '@docusaurus/preset-classic', - { - docs: { - sidebarPath: require.resolve('./sidebars.js'), - }, - theme: { - customCss: require.resolve('./src/css/custom.css'), - }, - }, - ], - ], -}; diff --git a/website/jsconfig.json b/website/jsconfig.json new file mode 100644 index 000000000..b639b0f8f --- /dev/null +++ b/website/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} \ No newline at end of file diff --git a/website/next.config.js b/website/next.config.js new file mode 100644 index 000000000..c09e915a8 --- /dev/null +++ b/website/next.config.js @@ -0,0 +1,2 @@ +const withNextra = require('./.nextra/nextra')(); +module.exports = withNextra(); diff --git a/website/nextra.config.js b/website/nextra.config.js new file mode 100644 index 000000000..9febdeee4 --- /dev/null +++ b/website/nextra.config.js @@ -0,0 +1,91 @@ +import React from 'react'; +import { Logo } from 'components/logo'; + +export default { + github: 'https://github.com/formium/tsdx', + titleSuffix: ' – TSDX', + logo: ( + <> + + TSDX + + ), + head: () => ( + <> + {/* Favicons, meta */} + + + + + + + + + + + + + + + + + + + {/* */} + + ), + footer: ({ filepath }) => ( + <> +
    + + A Jared Palmer Project + + + + ), +}; diff --git a/website/package.json b/website/package.json old mode 100755 new mode 100644 index 8ee1d8bf2..17281bc0e --- a/website/package.json +++ b/website/package.json @@ -1,31 +1,37 @@ { - "name": "website", - "version": "0.0.0", - "private": true, + "name": "tsdx-site", + "version": "1.0.0", + "description": "", + "main": "index.js", "scripts": { - "docusaurus": "docusaurus", - "start": "docusaurus start", - "build": "docusaurus build", - "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy" + "dev": "next", + "start": "next", + "build": "next build" }, + "author": "Jared Palmer", + "license": "MIT", "dependencies": { - "@docusaurus/core": "^2.0.0-alpha.24", - "@docusaurus/preset-classic": "^2.0.0-alpha.24", + "@reach/skip-nav": "^0.10.5", + "@sindresorhus/slugify": "^1.0.0", "classnames": "^2.2.6", - "react": "^16.8.4", - "react-dom": "^16.8.4" + "focus-visible": "^5.1.0", + "intersection-observer": "^0.10.0", + "markdown-to-jsx": "^6.11.4", + "match-sorter": "^4.1.0", + "next": "^9.4.4", + "next-google-fonts": "^1.1.0", + "prism-react-renderer": "^1.1.1", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "react-intersection-observer": "^8.26.2", + "string-strip-html": "^4.5.0" }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] + "devDependencies": { + "@mdx-js/loader": "^1.6.5", + "babel-plugin-macros": "^2.8.0", + "postcss-preset-env": "^6.7.0", + "preval.macro": "^5.0.0", + "tailwindcss": "^1.4.6", + "title": "^3.4.2" } -} \ No newline at end of file +} diff --git a/website/pages/_app.js b/website/pages/_app.js new file mode 100644 index 000000000..1c0291bd5 --- /dev/null +++ b/website/pages/_app.js @@ -0,0 +1,15 @@ +import React from 'react'; +import '.nextra/styles.css'; +import GoogleFonts from 'next-google-fonts'; + +export default function Nextra({ Component, pageProps }) { + return ( + <> + + + + ); +} diff --git a/website/pages/_document.js b/website/pages/_document.js new file mode 100644 index 000000000..f46b5a21e --- /dev/null +++ b/website/pages/_document.js @@ -0,0 +1,25 @@ +import React from 'react'; +import Document, { Html, Head, Main, NextScript } from 'next/document'; +import { SkipNavLink } from '@reach/skip-nav'; + +class MyDocument extends Document { + render() { + return ( + + + + +
    + +