@@ -0,0 +1,29 @@ | |||
--- | |||
layout: default | |||
--- | |||
<div class="container m-t-2"> | |||
{% for page in site.pages %} | |||
<p><strong><a href="{{page.url}}">{{page.path}}</a> ({{page.id}})</strong></p> | |||
{% if page.layout == "guide" %} | |||
{% include debug/toc.html content=page.content forceRender=true %} | |||
{% endif %} | |||
{% include debug/todo.html content=page.content forceRender=true %} | |||
<hr> | |||
{% endfor %} | |||
<h2>Redirects</h2> | |||
<pre><code>{% include_relative _redirects %}</code></pre> | |||
<h3>Pages</h2> | |||
{% assign urls_sorted = site.pages | map: "url" | sort %} | |||
<ul> | |||
{% for url in urls_sorted %} | |||
<li><a href="{{url}}">{{url}}</a></li> | |||
{% endfor %} | |||
</ul> | |||
</div> |
@@ -0,0 +1,17 @@ | |||
--- | |||
layout: default | |||
title: 404 | |||
description: Page not found. | |||
permalink: /404.html | |||
--- | |||
<div class="hero"> | |||
<div class="container"> | |||
<h1 class="hero-text display-4">404: Page Not Found</h1> | |||
<script> | |||
var GOOG_FIXURL_LANG = "en"; | |||
var GOOG_FIXURL_SITE = "https://yarnpkg.com"; | |||
</script> | |||
<script src="https://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script> | |||
</div> | |||
</div> |
@@ -0,0 +1,7 @@ | |||
source 'https://rubygems.org' | |||
gem 'jekyll' | |||
gem 'jekyll-redirect-from' | |||
gem 'jekyll-paginate' | |||
gem 'crowdin-cli' | |||
gem 'wdm', '>= 0.1.0' if Gem.win_platform? |
@@ -0,0 +1,63 @@ | |||
.PHONY: start install serve build i18n-upload i18n-download | |||
.DEFAULT_GOAL := start | |||
start: | |||
@make install | |||
@make serve | |||
install: test-builddeps | |||
@git submodule update --init --recursive | |||
@bundle install | |||
@yarn install | |||
serve: test-builddeps | |||
@yarn start& bundle exec jekyll serve --incremental& wait | |||
build: test-builddeps | |||
@yarn build | |||
@bundle exec jekyll build | |||
serve-production: test-builddeps | |||
@make crowdin-download | |||
@NODE_ENV=production yarn build:production | |||
@JEKYLL_ENV=production bundle exec jekyll serve | |||
build-production: test-builddeps | |||
ifeq ($(CONTEXT), "production") | |||
@make crowdin-download | |||
@ruby ./scripts/validate-translations.rb | |||
endif | |||
@NODE_ENV=production yarn build:production | |||
@JEKYLL_ENV=production bundle exec jekyll build | |||
crowdin-upload: test-crowdin | |||
@crowdin-cli upload sources --auto-update -b master | |||
crowdin-download: test-crowdin | |||
@crowdin-cli download -b master | grep -v '^Extracting: ' | grep -v '^ - ' | |||
@ruby ./scripts/remove-unused-languages.rb | |||
@ruby ./scripts/normalize-translations.rb | |||
### | |||
# Misc stuff: | |||
### | |||
BUNDLE_EXISTS := $(shell command -v bundle 2> /dev/null) | |||
CROWDIN_EXISTS := $(shell command -v crowdin-cli 2> /dev/null) | |||
YARN_EXISTS := $(shell command -v yarn 2> /dev/null) | |||
test-builddeps: | |||
ifndef BUNDLE_EXISTS | |||
$(error bundler is not installed. Run `gem install bundler`) | |||
endif | |||
ifndef YARN_EXISTS | |||
$(error yarn is not installed. Follow the instructions on https://yarnpkg.com/docs/install) | |||
endif | |||
test-crowdin: | |||
ifndef CROWDIN_EXISTS | |||
$(error Crowdin is not installed. Run `make install`) | |||
endif | |||
ifndef CROWDIN_API_KEY | |||
$(error CROWDIN_API_KEY is undefined) | |||
endif |
@@ -0,0 +1,41 @@ | |||
Yarn Website | |||
[](https://app.netlify.com/sites/yarnpkg/deploys) | |||
============ | |||
このリポジトリには、<https://classic.yarnpkg.com/> で入手できるYarn Classic(v1)Webサイトのソースコードが含まれています。 Yarn v2サイトについては、<https://github.com/yarnpkg/berry/tree/master/packages/gatsby> を参照してください。 | |||
開始するには: | |||
```sh | |||
$ git clone git@github.com:yarnpkg/website.git yarn-website | |||
$ cd yarn-website | |||
``` | |||
Yarnと [Bundler](http://bundler.io/) がインストールされていることを確認する必要があります。 | |||
```sh | |||
$ gem install bundler | |||
``` | |||
その後: | |||
```sh | |||
$ make | |||
``` | |||
または: | |||
```sh | |||
$ make install | |||
$ make serve | |||
``` | |||
Windowsでは、 `make`は使用できないため、` bundle`と `jekyll`を直接実行する必要があります。 | |||
```sh | |||
bundle install | |||
bundle exec jekyll serve --incremental | |||
``` | |||
--- | |||
**Special thanks to [Netlify](https://www.netlify.com/) for powering the website.** |
@@ -0,0 +1,44 @@ | |||
Yarn Website | |||
[](https://app.netlify.com/sites/yarnpkg/deploys) | |||
============ | |||
This repo contains the source code for the Yarn Classic (v1) website, available at https://classic.yarnpkg.com/. For the Yarn v2 site, see https://github.com/yarnpkg/berry/tree/master/packages/gatsby | |||
[README in Japanese](README.ja.md) | |||
In order to get started: | |||
```sh | |||
$ git clone git@github.com:yarnpkg/website.git yarn-website | |||
$ cd yarn-website | |||
``` | |||
You should ensure you have Yarn and [Bundler](http://bundler.io/) installed: | |||
```sh | |||
$ gem install bundler | |||
``` | |||
And then: | |||
```sh | |||
$ make | |||
``` | |||
Or: | |||
```sh | |||
$ make install | |||
$ make serve | |||
``` | |||
On Windows, `make` is not available, so you need to execute `bundle` and `jekyll` directly: | |||
```sh | |||
bundle install | |||
bundle exec jekyll serve --incremental | |||
``` | |||
--- | |||
**Special thanks to [Netlify](https://www.netlify.com/) for powering the website.** |
@@ -0,0 +1,73 @@ | |||
# Site settings | |||
title: Yarn | |||
description: > # this means to ignore newlines until "baseurl:" | |||
Fast, reliable, and secure dependency management. | |||
baseurl: "" | |||
url: "https://classic.yarnpkg.com" | |||
twitter_username: yarnpkg | |||
github_username: yarnpkg | |||
latest_version: 1.22.19 | |||
latest_rc_version: 1.22.19 | |||
# Whether to show the RC version on the site. Set this to false if the latest | |||
# stable version is newer than the RC (ie. if an RC has not been released since | |||
# the most recent stable version). | |||
show_rc: false | |||
# The versions of node that Yarn supports | |||
# This is visible on the install page | |||
node_support: ^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0 | |||
gacode: "UA-85522875-1" | |||
exclude: | |||
- README.md | |||
- crowdin.yaml | |||
- Gemfile | |||
- Gemfile.lock | |||
- Makefile | |||
- _sass/bootstrap | |||
- .sass-cache | |||
- node_modules | |||
- js/src | |||
include: | |||
- _redirects | |||
- install.sh | |||
plugins: | |||
- jekyll-redirect-from | |||
- jekyll-paginate | |||
# Build settings | |||
markdown: kramdown | |||
kramdown: | |||
input: GFM | |||
syntax_highlighter: rouge | |||
syntax_highlighter_opts: | |||
css_class: 'rougeHighlight' | |||
span: | |||
line_numbers: false | |||
block: | |||
line_numbers: false | |||
start_line: 1 | |||
sass: | |||
style: compressed | |||
redcarpet: | |||
extensions: [with_toc_data] | |||
# Blog Settings | |||
permalink: /blog/:year/:month/:day/:title/ | |||
paginate: 1 | |||
# ignore me | |||
__empty_array__: [] |
@@ -0,0 +1,266 @@ | |||
- id: docs_getting_started | |||
title: docs_getting_started_title | |||
description: docs_getting_started_description | |||
tags: ["intro"] | |||
pages: | |||
- id: docs_getting_started | |||
path: /docs/getting-started | |||
- id: docs_install | |||
path: /docs/install | |||
- id: docs_usage | |||
path: /docs/usage | |||
- id: docs_yarn_workflow | |||
title: docs_yarn_workflow_title | |||
description: docs_yarn_workflow_description | |||
tags: ["intro", "basics"] | |||
pages: | |||
- id: docs_yarn_workflow | |||
path: /docs/yarn-workflow | |||
- id: docs_creating_a_project | |||
path: /docs/creating-a-project | |||
- id: docs_managing_dependencies | |||
path: /docs/managing-dependencies | |||
- id: docs_installing_dependencies | |||
path: /docs/installing-dependencies | |||
- id: docs_version_control | |||
path: /docs/version-control | |||
- id: docs_install_ci | |||
path: /docs/install-ci | |||
- id: docs_cli | |||
title: docs_cli_title | |||
description: docs_cli_description | |||
tags: ["api", "basics", "cli"] | |||
pages: | |||
- id: docs_cli_index | |||
path: /docs/cli/ | |||
- id: docs_cli_add | |||
path: /docs/cli/add | |||
tags: ["cli-add"] | |||
description: docs_cli_add_description | |||
- id: docs_cli_audit | |||
path: /docs/cli/audit | |||
tags: ["cli-audit"] | |||
- id: docs_cli_autoclean | |||
path: /docs/cli/autoclean | |||
tags: ["cli-autoclean"] | |||
- id: docs_cli_bin | |||
path: /docs/cli/bin | |||
tags: ["cli-bin"] | |||
- id: docs_cli_cache | |||
path: /docs/cli/cache | |||
tags: ["cli-cache"] | |||
- id: docs_cli_check | |||
path: /docs/cli/check | |||
tags: ["cli-check"] | |||
- id: docs_cli_config | |||
path: /docs/cli/config | |||
tags: ["cli-config"] | |||
- id: docs_cli_create | |||
path: /docs/cli/create | |||
tags: ["cli-create"] | |||
- id: docs_cli_dedupe | |||
path: /docs/cli/dedupe | |||
tags: ["cli-dedupe"] | |||
- id: docs_cli_generate_lock_entry | |||
path: /docs/cli/generate-lock-entry | |||
tags: ["cli-generate-lock-entry"] | |||
- id: docs_cli_global | |||
path: /docs/cli/global | |||
tags: ["cli-global"] | |||
- id: docs_cli_help | |||
path: /docs/cli/help | |||
tags: ["cli-help"] | |||
- id: docs_cli_import | |||
path: /docs/cli/import | |||
tags: ["cli-import"] | |||
- id: docs_cli_info | |||
path: /docs/cli/info | |||
tags: ["cli-info"] | |||
- id: docs_cli_init | |||
path: /docs/cli/init | |||
tags: ["cli-init"] | |||
- id: docs_cli_install | |||
path: /docs/cli/install | |||
tags: ["cli-install"] | |||
- id: docs_cli_licenses | |||
path: /docs/cli/licenses | |||
tags: ["cli-licenses"] | |||
- id: docs_cli_link | |||
path: /docs/cli/link | |||
tags: ["cli-link"] | |||
- id: docs_cli_list | |||
path: /docs/cli/list | |||
tags: ["cli-list"] | |||
- id: docs_cli_lockfile | |||
path: /docs/cli/lockfile | |||
tags: ["cli-lockfile"] | |||
- id: docs_cli_login | |||
path: /docs/cli/login | |||
tags: ["cli-login"] | |||
- id: docs_cli_logout | |||
path: /docs/cli/logout | |||
tags: ["cli-logout"] | |||
- id: docs_cli_outdated | |||
path: /docs/cli/outdated | |||
tags: ["cli-outdated"] | |||
- id: docs_cli_owner | |||
path: /docs/cli/owner | |||
tags: ["cli-owner"] | |||
- id: docs_cli_pack | |||
path: /docs/cli/pack | |||
tags: ["cli-pack"] | |||
- id: docs_cli_policies | |||
path: /docs/cli/policies | |||
tags: ["cli-policies"] | |||
- id: docs_cli_prune | |||
path: /docs/cli/prune | |||
tags: ["cli-prune"] | |||
- id: docs_cli_publish | |||
path: /docs/cli/publish | |||
tags: ["cli-publish"] | |||
- id: docs_cli_remove | |||
path: /docs/cli/remove | |||
tags: ["cli-remove"] | |||
- id: docs_cli_run | |||
path: /docs/cli/run | |||
tags: ["cli-run"] | |||
- id: docs_cli_self_update | |||
path: /docs/cli/self-update | |||
tags: ["cli-self-update"] | |||
- id: docs_cli_tag | |||
path: /docs/cli/tag | |||
tags: ["cli-tag"] | |||
description: docs_cli_tag_description | |||
- id: docs_cli_team | |||
path: /docs/cli/team | |||
tags: ["cli-team"] | |||
- id: docs_cli_test | |||
path: /docs/cli/test | |||
tags: ["cli-test"] | |||
- id: docs_cli_unlink | |||
path: /docs/cli/unlink | |||
tags: ["cli-unlink"] | |||
- id: docs_cli_upgrade | |||
path: /docs/cli/upgrade | |||
tags: ["cli-upgrade"] | |||
- id: docs_cli_upgrade_interactive | |||
path: /docs/cli/upgrade-interactive | |||
tags: ["cli-upgrade-interactive"] | |||
- id: docs_cli_version | |||
path: /docs/cli/version | |||
tags: ["cli-version"] | |||
- id: docs_cli_versions | |||
path: /docs/cli/versions | |||
tags: ["cli-versions"] | |||
- id: docs_cli_why | |||
path: /docs/cli/why | |||
tags: ["cli-why"] | |||
- id: docs_cli_workspace | |||
path: /docs/cli/workspace | |||
tags: ["cli-workspace"] | |||
- id: docs_cli_workspaces | |||
path: /docs/cli/workspaces | |||
tags: ["cli-workspaces"] | |||
- id: docs_migrating_from_npm | |||
title: docs_migrating_from_npm_title | |||
description: docs_migrating_from_npm_description | |||
tags: ["intro", "basics", "migrating"] | |||
pages: | |||
- id: docs_migrating_from_npm | |||
path: /docs/migrating-from-npm | |||
- id: docs_creating_a_package | |||
title: docs_creating_a_package_title | |||
description: docs_creating_a_package_description | |||
tags: ["intro", "basics", "authoring"] | |||
pages: | |||
- id: docs_creating_a_package | |||
path: /docs/creating-a-package | |||
- id: docs_publishing_a_package | |||
path: /docs/publishing-a-package | |||
- id: docs_dependencies | |||
title: docs_dependencies_title | |||
description: docs_dependencies_description | |||
tags: ["intro", "basics", "dependencies"] | |||
pages: | |||
- id: docs_dependencies | |||
path: /docs/dependencies | |||
- id: docs_dependency_types | |||
path: /docs/dependency-types | |||
- id: docs_dependency_versions | |||
path: /docs/dependency-versions | |||
tags: ["dependencies-versions"] | |||
description: docs_dependency_versions_description | |||
- id: docs_selective_version_resolutions | |||
path: /docs/selective-version-resolutions | |||
tags: ["dependencies-versions"] | |||
description: docs_selective_version_resolutions_description | |||
- id: docs_configuration | |||
title: docs_configuration_title | |||
description: docs_configuration_description | |||
tags: ["configuration"] | |||
pages: | |||
- id: docs_configuration_index | |||
path: /docs/configuration | |||
- id: docs_configuration_package_json | |||
path: /docs/package-json | |||
tags: ["package-json"] | |||
- id: docs_configuration_envvars | |||
path: /docs/envvars | |||
tags: ["envvars"] | |||
- id: docs_configuration_yarnrc | |||
path: /docs/yarnrc | |||
tags: ["yarnrc"] | |||
- id: docs_configuration_yarn_lock | |||
path: /docs/yarn-lock | |||
tags: ["yarn-lock"] | |||
- id: docs_offline_mirror | |||
title: docs_offline_mirror_title | |||
description: docs_offline_mirror_description | |||
pages: | |||
- id: docs_offline_mirror | |||
path: /docs/offline-mirror | |||
- id: docs_prune_offline_mirror | |||
path: /docs/prune-offline-mirror | |||
- id: docs_workspaces | |||
title: docs_workspaces_title | |||
description: docs_workspaces_description | |||
pages: | |||
- id: docs_workspaces | |||
path: /docs/workspaces | |||
- id: docs_plugnplay | |||
title: docs_plugnplay_title | |||
description: docs_plugnplay_description | |||
pages: | |||
- id: docs_plugnplay_overview | |||
path: /docs/pnp | |||
- id: docs_plugnplay_getting_started | |||
path: /docs/pnp/getting-started | |||
- id: docs_plugnplay_troubleshooting | |||
path: /docs/pnp/troubleshooting | |||
- id: yarn_organization | |||
title: yarn_organization_title | |||
description: yarn_organization_description | |||
tags: ["internal"] | |||
pages: | |||
- id: organization | |||
path: /org | |||
- id: code_of_conduct | |||
path: /org/code-of-conduct | |||
- id: contributing | |||
path: /org/contributing | |||
- id: translations | |||
path: /org/translations | |||
- id: release_process | |||
path: /org/release-process | |||
- id: governance | |||
path: /org/governance |
@@ -0,0 +1,400 @@ | |||
### | |||
# Global | |||
### | |||
site_title: Yarn | |||
site_description: Fast, reliable, and secure dependency management. | |||
site_news_important: Important | |||
site_news_docs_cover_v1: This documentation covers Yarn 1 (Classic). | |||
site_news_see_v2_docs: For Yarn 2+ docs and migration guide, see yarnpkg.com. | |||
site_nav_getting_started: Getting Started | |||
site_nav_documentation: Docs | |||
site_nav_packages: Packages | |||
site_nav_blog: Blog | |||
site_nav_stable_version: Classic Stable | |||
site_nav_rc_version: Classic Release Candidate | |||
site_nav_nightly_version: Classic Nightly | |||
site_nav_node_support: Node | |||
site_bsd_license: Distributed under BSD License | |||
site_code_of_conduct: Code of Conduct | |||
site_edit_this_page: Edit this page | |||
blog_title: Yarn Blog | |||
guide_additional_reading: Additional Reading | |||
iframe_github_stars: GitHub Stars | |||
### | |||
# Homepage | |||
### | |||
homepage_tagline: Fast, reliable, and secure dependency management. | |||
homepage_feature_fast_title: Ultra Fast. | |||
homepage_feature_fast_description: | | |||
Yarn caches every package it downloads so it never needs to download it again. It also | |||
parallelizes operations to maximize resource utilization so install times are | |||
faster than ever. | |||
homepage_feature_fast_image_alt: Watercolour of cat riding a rocketship | |||
homepage_feature_secure_title: Mega Secure. | |||
homepage_feature_secure_description: | | |||
Yarn uses checksums to verify the integrity of every installed package | |||
before its code is executed. | |||
homepage_feature_secure_image_alt: Watercolour of cat driving a robot suit | |||
homepage_feature_reliable_title: Super Reliable. | |||
homepage_feature_reliable_description: | | |||
Using a detailed, but concise, lockfile format, and a deterministic algorithm | |||
for installs, Yarn is able to guarantee that an install that worked on one | |||
system will work exactly the same way on any other system. | |||
homepage_feature_reliable_image_alt: Watercolour of cat waving from seat behind computer screens | |||
homepage_c2a_text: What are you waiting for? | |||
homepage_c2a_button: Get Started | |||
homepage_install_button: Install Yarn | |||
homepage_migrate_button: Migrate to Yarn 2+ | |||
homepage_featurette_offline_title: Offline Mode | |||
homepage_featurette_offline_description: | | |||
If you've installed a package before, you can install it again without | |||
any internet connection. | |||
homepage_featurette_deterministic_title: Deterministic | |||
homepage_featurette_deterministic_description: | | |||
The same dependencies will be installed the same exact way across every | |||
machine regardless of install order. | |||
homepage_featurette_network_performance_title: Network Performance | |||
homepage_featurette_network_performance_description: | | |||
Yarn efficiently queues up requests and avoids request waterfalls in order | |||
to maximize network utilization. | |||
homepage_featurette_npm_title: Same Packages | |||
homepage_featurette_npm_description: | | |||
Install any package from npm and keep your package workflow | |||
the same. | |||
homepage_featurette_network_resillience_title: Network Resilience | |||
homepage_featurette_network_resillience_description: | | |||
A single request failing won't cause an install to fail. Requests are retried | |||
upon failure. | |||
homepage_featurette_flat_mode_title: Flat Mode | |||
homepage_featurette_flat_mode_description: | | |||
Resolve mismatching versions of dependencies to a single version to avoid | |||
creating duplicates. | |||
### | |||
# Titles | |||
### | |||
docs_getting_started_title: Getting Started | |||
docs_getting_started_description: | | |||
Never used a package manager before or just new to Yarn? Let's get you up and | |||
running in just a couple of minutes. | |||
docs_index: Documentation | |||
docs_getting_started: Getting Started | |||
docs_install: Installation | |||
docs_usage: Usage | |||
docs_yarn_workflow_title: The Yarn Workflow | |||
docs_yarn_workflow_description: | | |||
How do I use Yarn? There are basic workflows for both creating and consuming | |||
Yarn packages that will help you get productive quickly. | |||
docs_yarn_workflow: The Yarn Workflow | |||
docs_creating_a_project: Creating a new project | |||
docs_managing_dependencies: Managing dependencies | |||
docs_installing_dependencies: Installing dependencies | |||
docs_version_control: Working with version control | |||
docs_cli_title: CLI Commands | |||
docs_cli_description: | | |||
Yarn is executed through a rich set of commands allowing package | |||
installation, administration, publishing, and more. | |||
docs_cli_index: CLI Introduction | |||
docs_cli_add: yarn add | |||
docs_cli_add_description: | | |||
When you want to use another package, you first need to add it to | |||
your dependencies. Running `yarn add` installs it into your project. | |||
docs_cli_audit: yarn audit | |||
docs_cli_autoclean: yarn autoclean | |||
docs_cli_bin: yarn bin | |||
docs_cli_cache: yarn cache | |||
docs_cli_check: yarn check | |||
docs_cli_config: yarn config | |||
docs_cli_create: yarn create | |||
docs_cli_dedupe: yarn dedupe | |||
docs_cli_generate_lock_entry: yarn generate-lock-entry | |||
docs_cli_global: yarn global | |||
docs_cli_help: yarn help | |||
docs_cli_import: yarn import | |||
docs_cli_info: yarn info | |||
docs_cli_init: yarn init | |||
docs_cli_install: yarn install | |||
docs_cli_licenses: yarn licenses | |||
docs_cli_link: yarn link | |||
docs_cli_lockfile: yarn lockfile | |||
docs_cli_login: yarn login | |||
docs_cli_logout: yarn logout | |||
docs_cli_list: yarn list | |||
docs_cli_outdated: yarn outdated | |||
docs_cli_owner: yarn owner | |||
docs_cli_pack: yarn pack | |||
docs_cli_policies: yarn policies | |||
docs_cli_prune: yarn prune | |||
docs_cli_publish: yarn publish | |||
docs_cli_remove: yarn remove | |||
docs_cli_run: yarn run | |||
docs_cli_self_update: yarn self-update | |||
docs_cli_tag: yarn tag | |||
docs_cli_tag_description: | | |||
Tags are a way of publishing versions of your package with a label. | |||
Users of your package can install that instead of a version number. | |||
docs_cli_team: yarn team | |||
docs_cli_test: yarn test | |||
docs_cli_unlink: yarn unlink | |||
docs_cli_upgrade: yarn upgrade | |||
docs_cli_upgrade_description: | | |||
Upgrades packages to their latest version based on the specified range. | |||
docs_cli_upgrade_interactive: yarn upgrade-interactive | |||
docs_cli_upgrade_interactive_description: | | |||
Provides an easy and interactive way to update outdated packages. | |||
docs_cli_version: yarn version | |||
docs_cli_versions: yarn versions | |||
docs_cli_workspace: yarn workspace | |||
docs_cli_why: yarn why | |||
docs_cli_workspaces: yarn workspaces | |||
docs_migrating_from_npm_title: Migrating from npm client | |||
docs_migrating_from_npm_description: | | |||
Yarn interops directly with many features of npm, including its package | |||
metadata format, allowing for a painless migration. | |||
docs_migrating_from_npm: Migrating from npm | |||
docs_creating_a_package_title: Creating a Package | |||
docs_creating_a_package_description: | | |||
Creating and publishing a yarn package can be done with just a few commands | |||
and configuration settings, leaving you to focus on your actual code. | |||
docs_creating_a_package: Creating a Package | |||
docs_publishing_a_package: Publishing a Package | |||
docs_dependencies_title: Dependencies & Versions | |||
docs_dependencies_description: | | |||
Using Yarn you'll be working with dependencies all the time. Let's go through | |||
the different types and versions of dependencies. | |||
docs_dependencies: Dependencies and versions | |||
docs_dependency_types: Types of dependencies | |||
docs_dependency_versions: Versions of dependencies | |||
docs_dependency_versions_description: | | |||
Packages in Yarn follow Semantic Versioning, also known as “semver”. When you | |||
install a new package it will be added with a semver version range. | |||
docs_selective_version_resolutions: Selective dependency resolutions | |||
docs_selective_version_resolutions_description: | | |||
Override sub-dependency version resolutions with Yarn. | |||
docs_configuration_title: Configuration | |||
docs_configuration_description: | | |||
Learn how to use package.json to configure your packages and dependencies. | |||
docs_configuration_index: Configuration | |||
docs_configuration_package_json: package.json | |||
docs_configuration_yarn_lock: yarn.lock | |||
docs_configuration_yarnrc: .yarnrc | |||
docs_configuration_envvars: envvars | |||
docs_offline_mirror: Configuring an Offline Mirror | |||
docs_offline_mirror_title: Offline Mirror | |||
docs_offline_mirror_description: | | |||
Maintain offline copies of your packages for more repeatable and reliable | |||
builds. | |||
docs_prune_offline_mirror: Pruning an Offline Mirror | |||
docs_workspaces: Workspaces | |||
docs_workspaces_title: Workspaces | |||
docs_workspaces_description: | | |||
Link together your projects for easier maintenance. | |||
docs_plugnplay: "Plug'n'Play" | |||
docs_plugnplay_title: "Plug'n'Play" | |||
docs_plugnplay_description: | | |||
Install your projects in a safer and faster way. | |||
docs_plugnplay_overview: Overview | |||
docs_plugnplay_getting_started: Getting Started | |||
docs_plugnplay_troubleshooting: Troubleshooting | |||
yarn_organization_title: Yarn Organization | |||
yarn_organization_description: | | |||
The Yarn organization is a collaboration of many companies and | |||
individuals dedicated to improving your package management experience. | |||
organization: Organization | |||
code_of_conduct: Code of Conduct | |||
contributing: Contributing | |||
translations: Translations | |||
release_process: Release Process | |||
governance: Governance | |||
read_more: Read more | |||
users: Yarn Users | |||
compare: Compare Yarn Performance | |||
blog: Blog | |||
packages: Packages | |||
users_lead: Meet all the users of Yarn. | |||
users_description: > | |||
Logos are added by company or project representatives. They may or may not be | |||
using Yarn on their main properties, but they're definitely using it | |||
somewhere. | |||
users_add_prompt: Want to add your own company or project? | |||
users_add_pr: Open a pull request | |||
users_add_requirements_image: > | |||
Logos must be a 500x200px (2.5x1) SVG (preferred) or PNG image optimized with | |||
svgo, ImageOptim, or another image optimizer. | |||
users_add_requirements_logo: > | |||
Please use a version of your logo that works well on a white background. | |||
install_intro: > | |||
Before you start using Yarn, you'll first need to install it on your system. | |||
There are many different ways to install Yarn, but a single one is recommended and cross-platform: | |||
install_alternatives: Alternatives | |||
install_click_to_expand_collapse: Click to expand / collapse | |||
install_select_platform: Select your platform above | |||
install_os: Operating system | |||
install_version: Version | |||
install_os_alpine: Alpine | |||
install_os_arch: Arch Linux | |||
install_os_centos: CentOS / Fedora / RHEL | |||
install_os_debian: Debian / Ubuntu | |||
install_os_gentoo: Gentoo Linux | |||
install_os_mac: macOS | |||
install_os_opensuse: openSUSE | |||
install_os_solus: Solus | |||
install_os_windows: Windows | |||
install_os_alternatives: Alternatives | |||
install_check: "Check installation" | |||
install_check_details: "Check that Yarn is installed by running:" | |||
docs_nightly: Nightly Builds | |||
install_nightly_intro: > | |||
Nightly builds are the latest and greatest versions of Yarn, built using the | |||
very latest Yarn source code. Nightly builds are useful to try new features or | |||
test bug fixes that have not yet been released as part of a stable release. | |||
However, these builds are <strong>not guaranteed to be stable</strong> and may | |||
have bugs. | |||
install_nightly_learn_more: See how to install nightly builds | |||
nightly_latest_version: Latest Version | |||
nightly_older_versions: Older Versions | |||
nightly_select_build_type: Select a build type above to see older builds | |||
install_table_name: Name | |||
# "Type" refers to type of artifact - tarball, Windows installer, Debian package, etc. | |||
install_table_type: Type | |||
install_table_size: Size | |||
install_table_date: Date | |||
loading: Loading... | |||
install_file: | |||
deb: Debian package | |||
js: Standalone JS | |||
js-legacy: Standalone JS (Node < 4.0) | |||
msi: Windows installer | |||
rpm: RPM | |||
tar: Tarball | |||
docs_install_ci: Continuous Integration | |||
ci_intro: > | |||
Yarn can easily be used in various continuous integration systems. To speed up | |||
builds, the Yarn cache directory can be saved across builds. | |||
ci_select_platform: Select the continuous integration system you're using from the options above | |||
ci_appveyor: AppVeyor | |||
ci_circle: CircleCI | |||
ci_codeship: Codeship | |||
ci_travis: Travis | |||
ci_semaphore: Semaphore | |||
ci_solano: Solano | |||
ci_gitlab: GitLab | |||
ci_codefresh: Codefresh | |||
search_docs: Search documentation | |||
detail: | |||
title: Package detail | |||
script: | |||
search_placeholder: Search packages (i.e. babel, webpack, react…) | |||
search_by_algolia: Search by Algolia | |||
search_by_read_more: read how it works | |||
no_package_found: 'No package {name} was found' | |||
no_results_docsearch: 'Were you looking for something in the {documentation_link}?' | |||
documentation: documentation | |||
downloads_in_last_30_days: '{count} downloads in the last 30 days' | |||
npm_page_for: 'npm page for {name}' | |||
repository_of: '{provider} repository of {name}' | |||
npm: npm | |||
github: GitHub | |||
gitlab: GitLab | |||
bitbucket: Bitbucket | |||
homepage: Homepage | |||
deprecated: deprecated | |||
result_stats: 'found {number_packages} packages in {time_search}ms' | |||
time_ago: '{time_distance} ago' | |||
last_updated: 'last updated {update_date}' | |||
detail: | |||
over_a_year_ago: over a year ago | |||
less_than_a_week_ago: less than a week ago | |||
one_week_ago: one week ago | |||
weeks_ago: '{count} weeks ago' | |||
activity: Activity | |||
commits_last_three_months: Commits last 3 months | |||
last_commit: Last commit | |||
loading: Loading... | |||
use_it: Use it | |||
try_in_runkit: Try in RunKit | |||
back_to_details: Back to Details | |||
browse_files: Browse Files | |||
cdns: CDNs | |||
contributors: Contributors | |||
display_full_readme: Display full readme | |||
display_full_changelog: Display full changelog | |||
files_error: 'Could not load file listing: {error}' | |||
files_header: 'Files in {name}' | |||
collapse: Collapse | |||
readme: readme | |||
no_readme_found: no readme found 😢 | |||
changelog: changelog | |||
popularity: Popularity | |||
github_stargazers: GitHub stargazers | |||
gitlab_stargazers: GitLab stargazers | |||
downloads_last_30_days: Downloads last 30 days | |||
jsdelivr_hits: jsDelivr last 30 days | |||
dependents: Dependents | |||
usage: Usage | |||
dependencies: Dependencies | |||
devdependencies: DevDependencies | |||
packages: Packages | |||
see_package_json: see package.json | |||
tags: Tags | |||
versions: Versions | |||
display_all: Display all | |||
hide: Hide | |||
bundlesize: Size in browser | |||
bundlesize_text: 'size: {size}, gzip: {gzip}' | |||
not_found: | |||
whoa: Whoa, {package_name} does not exist yet | |||
yours: But that means it is now yours! | |||
make: Make your package |
@@ -0,0 +1,140 @@ | |||
- name: English | |||
tag: en | |||
enabled: Yes | |||
accept_languages: ["en", "en-us", "en-au", "en-nz", "en-za", "en-bz", "en-tt"] | |||
- name: Afrikaans | |||
tag: af | |||
enabled: No | |||
accept_languages: ["af"] | |||
- name: العربية | |||
tag: ar | |||
enabled: No | |||
accept_languages: ["ar-sa", "ar-iq", "ar-eg", "ar-ly", "ar-dz", "ar-ma", "ar-tn", "ar-om", "ar-ye", "ar-sy", "ar-jo", "ar-lb", "ar-kw", "ar-ae", "ar-bh", "ar-qa"] | |||
- name: Bosanski | |||
tag: bs-BA | |||
enabled: No | |||
accept_languages: ["bs-BA"] | |||
- name: Català | |||
tag: ca | |||
enabled: No | |||
accept_languages: ["ca"] | |||
- name: Čeština | |||
tag: cs | |||
enabled: No | |||
accept_languages: ["cs"] | |||
- name: Dansk | |||
tag: da | |||
enabled: No | |||
accept_languages: ["da"] | |||
- name: Deutsch | |||
tag: de | |||
enabled: No | |||
accept_languages: ["de", "de-ch", "de-at", "de-lu", "de-li"] | |||
- name: Ελληνικά | |||
tag: el | |||
enabled: No | |||
accept_languages: ["el"] | |||
- name: Español | |||
tag: es-ES | |||
enabled: Yes | |||
accept_languages: ["es", "es-mx", "es-gt", "es-cr", "es-pa", "es-do", "es-ve", "es-co", "es-pe", "es-ar", "es-ec", "es-cl", "es-uy", "es-py", "es-bo", "es-sv", "es-hn", "es-ni", "es-pr"] | |||
- name: فارسی | |||
tag: fa-IR | |||
enabled: No | |||
accept_languages: ["fa", "fa-IR"] | |||
- name: Suomi | |||
tag: fi | |||
enabled: No | |||
accept_languages: ["fi"] | |||
- name: Français | |||
tag: fr | |||
enabled: Yes | |||
accept_languages: ["fr", "fr-be", "fr-ca", "fr-ch", "fr-lu"] | |||
- name: עִברִית | |||
tag: he | |||
enabled: No | |||
accept_languages: ["he"] | |||
- name: Magyar | |||
tag: hu | |||
enabled: No | |||
accept_languages: ["hu", "hu-HU"] | |||
- name: Bahasa Indonesia | |||
tag: id-ID | |||
enabled: Yes | |||
accept_languages: ["id"] | |||
- name: Italiano | |||
tag: it | |||
enabled: No | |||
accept_languages: ["it", "it-ch"] | |||
- name: 日本語 | |||
tag: ja | |||
enabled: Yes | |||
accept_languages: ["ja"] | |||
- name: 한국어 | |||
tag: ko | |||
enabled: No | |||
accept_languages: ["ko"] | |||
- name: मराठी | |||
tag: mr-IN | |||
enabled: No | |||
accept_languages: ["mr"] | |||
- name: Nederlands | |||
tag: nl | |||
enabled: No | |||
accept_languages: ["nl", "nl-be"] | |||
- name: Norsk | |||
tag: no-NO | |||
enabled: No | |||
accept_languages: ["no", "no-NO"] | |||
- name: Polskie | |||
tag: pl | |||
enabled: No | |||
accept_languages: ["pl"] | |||
- name: Português | |||
tag: pt-PT | |||
enabled: No | |||
accept_languages: ["pt"] | |||
- name: Português (Brasil) | |||
tag: pt-BR | |||
enabled: Yes | |||
accept_languages: ["pt-br"] | |||
- name: Română | |||
tag: ro | |||
enabled: No | |||
accept_languages: ["ro"] | |||
- name: Русский | |||
tag: ru | |||
enabled: Yes | |||
accept_languages: ["ru", "ru-mo"] | |||
- name: Slovenský | |||
tag: sk-SK | |||
enabled: No | |||
accept_languages: ["sk"] | |||
- name: Српски језик (Ћирилица) | |||
tag: sr | |||
enabled: No | |||
accept_languages: ["sr"] | |||
- name: Svenska | |||
tag: sv-SE | |||
enabled: No | |||
accept_languages: ["sv", "sv-fi"] | |||
- name: Türkçe | |||
tag: tr | |||
enabled: Yes | |||
accept_languages: ["tr"] | |||
- name: Українська | |||
tag: uk | |||
enabled: Yes | |||
accept_languages: ["uk"] | |||
- name: Tiếng Việt | |||
tag: vi | |||
enabled: No | |||
accept_languages: ["vi"] | |||
- name: 中文 | |||
tag: zh-Hans | |||
enabled: Yes | |||
accept_languages: ["zh-cn", "zh-sg"] | |||
- name: 繁體中文 | |||
tag: zh-Hant | |||
enabled: Yes | |||
accept_languages: ["zh-hk", "zh-tw"] |
@@ -0,0 +1,7 @@ | |||
# Types of files available as part of the nightly builds | |||
- tar | |||
- deb | |||
- msi | |||
- rpm | |||
- js | |||
#- js-legacy |
@@ -0,0 +1,11 @@ | |||
- name: Facebook | |||
url: https://www.facebook.com/ | |||
image: facebook.svg | |||
- name: Exponent | |||
url: https://getexponent.com/ | |||
image: exponent.svg | |||
- name: Tilde | |||
url: http://www.tilde.io | |||
image: tilde.svg |
@@ -0,0 +1,34 @@ | |||
{% assign tags = include.tags %} | |||
{% include vars.html %} | |||
<div class="list-group"> | |||
<strong class="list-group-item bg-faded">{{i18n.guide_additional_reading}}</strong> | |||
{% for tag in tags %} | |||
{% for guide in site.data.guides %} | |||
{% if guide.tags contains tag %} | |||
<a class="list-group-item list-group-item-action" href="{{url_base}}{{guide.pages[0].path}}"> | |||
<h5 class="list-group-item-heading">{{i18n[guide.title]}}</h5> | |||
<p class="list-group-item-text text-muted">{{i18n[guide.description]}}</p> | |||
</a> | |||
{% break %} | |||
{% endif %} | |||
{% endfor %} | |||
{% endfor %} | |||
{% for tag in tags %} | |||
{% for guide in site.data.guides %} | |||
{% for page in guide.pages %} | |||
{% if page.tags contains tag %} | |||
<a class="list-group-item list-group-item-action" href="{{url_base}}{{page.path}}"> | |||
<h5 class="list-group-item-heading">{{i18n[page.id]}}</h5> | |||
{% if page.description %} | |||
<p class="list-group-item-text text-muted">{{i18n[page.description]}}</p> | |||
{% endif %} | |||
</a> | |||
{% break %} | |||
{% endif %} | |||
{% endfor %} | |||
{% endfor %} | |||
{% endfor %} | |||
</div> |
@@ -0,0 +1,34 @@ | |||
{% assign inputL = include.ms %} | |||
{% assign second = 1000 %} | |||
{% assign minute = second | times: 60 %} | |||
{% assign hour = minute | times: 60 %} | |||
{% assign hours = inputL | divided_by: hour | round %} | |||
{% assign hoursL = inputL | modulo: hour %} | |||
{% assign mins = hoursL | divided_by: minute | round %} | |||
{% assign minsL = hoursL | modulo: minute %} | |||
{% assign secs = minsL | divided_by: second | round %} | |||
{% assign secsL = minsL | modulo: second %} | |||
{% assign mills = secsL | round %} | |||
{% if hours != 0 %} | |||
{{hours}} hour{% if hours > 1 %}s{% endif %} | |||
{% endif %} | |||
{% if mins != 0 %} | |||
{{mins}} minute{% if mins > 1 %}s{% endif %} | |||
{% endif %} | |||
{% if secs != 0 %} | |||
{{secs}} second{% if secs > 1 %}s{% endif %} | |||
{% endif %} | |||
{% if mins == 0 %} | |||
{% if mills != 0 %} | |||
{{mills}}ms | |||
{% endif %} | |||
{% endif %} |
@@ -0,0 +1,68 @@ | |||
{% include vars.html %} | |||
{% assign shouldRender = false %} | |||
{% if jekyll.environment == "development" %} | |||
{% assign shouldRender = true %} | |||
{% endif %} | |||
{% if include.forceRender %} | |||
{% assign shouldRender = true %} | |||
{% endif %} | |||
{% if shouldRender and vars_active_language.tag == 'en' %} | |||
{% assign lines = include.content | newline_to_br | strip_newlines | split: '<br />' %} | |||
{% assign insideCodeBlock = false %} | |||
{% for line in lines %} | |||
{% assign threeChar = line | slice: 0, 3 %} | |||
{% assign firstChar = line | slice: 0, 1 %} | |||
{% if threeChar == '```' %} | |||
{% if insideCodeBlock %} | |||
{% assign insideCodeBlock = false %} | |||
{% else %} | |||
{% assign insideCodeBlock = true %} | |||
{% endif %} | |||
{% endif %} | |||
{% unless insideCodeBlock %} | |||
{% if firstChar == '#' %} | |||
{% assign stripped = line | strip_html %} | |||
{% assign slug = stripped | slugify | prepend: 'toc-' %} | |||
{% assign link = '<a class="toc" id="' | append: slug | append: '" href="#' | append: slug | append: '"></a>' %} | |||
{% assign full = stripped | append: ' ' | append: link %} | |||
{% unless line contains link %} | |||
<div class="alert alert-danger"> | |||
<h4>Yarn Website Internal Developer Warning!</h4> | |||
<p>This page contains this heading:</p> | |||
<pre><code>{{line | escape}}</code></pre> | |||
<p> | |||
All headings need a table of contents "toc" link, you have either | |||
left it out or are using the "wrong" one. Please use this one: | |||
</p> | |||
<pre><code>{{link | escape}}</code></pre> | |||
<p> | |||
Even if this is not your fault, please correct the error. Also | |||
if there is an existing toc, please check that any links on the | |||
website to this heading are correct (just search for the previous | |||
toc id). | |||
</p> | |||
<p> | |||
If you believe you are seeing this warning by mistake, please open | |||
an issue. | |||
</p> | |||
</div> | |||
{% endunless %} | |||
{% endif %} | |||
{% endunless %} | |||
{% endfor %} | |||
{% endif %} |
@@ -0,0 +1,46 @@ | |||
{% assign shouldRender = false %} | |||
{% if jekyll.environment == "development" %} | |||
{% assign shouldRender = true %} | |||
{% endif %} | |||
{% if include.forceRender %} | |||
{% assign shouldRender = true %} | |||
{% endif %} | |||
{% if shouldRender %} | |||
{% if include.content contains '[TODO' %} | |||
{% if include.content %} | |||
<div class="alert alert-info"> | |||
<h4>Yarn Website Internal Developer Info!</h4> | |||
<p>This page contains some <code>[TODO]</code>'s, if you have time, please complete them</p> | |||
{% assign lines = include.content | newline_to_br | strip_newlines | split: '<br />' %} | |||
{% assign total = lines | size %} | |||
{% for i in (0..total) %} | |||
{% assign line = lines[i] %} | |||
{% if line contains '[TODO' %} | |||
{% assign message = '' %} | |||
{% assign start = i | minus: 3 %} | |||
{% assign end = i | plus: 3 %} | |||
{% if start < 0 %}{% assign start = 0 %}{% endif %} | |||
{% if end > total %}{% assign end = total %}{% endif %} | |||
<pre><code>{% for n in (start..end) %}{{n}}: {{lines[n] | escape}} | |||
{% endfor %}</code></pre> | |||
{% endif %} | |||
{% endfor %} | |||
<p> | |||
If you believe you are seeing this warning by mistake, please open | |||
an issue. | |||
</p> | |||
</div> | |||
{% endif %} | |||
{% endif %} | |||
{% endif %} |
@@ -0,0 +1,29 @@ | |||
{% assign shouldRender = false %} | |||
{% if jekyll.environment == "development" %} | |||
{% assign shouldRender = true %} | |||
{% endif %} | |||
{% if include.forceRender %} | |||
{% assign shouldRender = true %} | |||
{% endif %} | |||
{% if shouldRender %} | |||
{% if include.url contains "_" %} | |||
<div class="alert alert-danger"> | |||
<h4>Yarn Website Internal Developer Info!</h4> | |||
<p> | |||
This page has an incorrectly formatted url. Underscores (_) are not | |||
allowed inside urls, use dashes instead. | |||
</p> | |||
<pre><code>{{include.url}}</code></pre> | |||
<p> | |||
If you believe you are seeing this warning by mistake, please open | |||
an issue. | |||
</p> | |||
</div> | |||
{% endif %} | |||
{% endif %} |
@@ -0,0 +1,11 @@ | |||
{% comment %} | |||
This is Algolia docsearch, search of the documentation. See https://community.algolia.com/docsearch/ | |||
Scripts are added in the en/docs/index.html page | |||
{% endcomment %} | |||
<div class="row"> | |||
<div class="col-12 col-lg-6 push-lg-3 mb-4 search"> | |||
<label class="search-label sr-only" for="algolia-doc-search">{{i18n.search_docs}}</label> | |||
<input class="search-input form-control" id="algolia-doc-search" type="search" placeholder="{{i18n.search_docs}}" /> | |||
</div> | |||
</div> |
@@ -0,0 +1,34 @@ | |||
{% include vars.html %} | |||
<hr class="footer-divider"> | |||
<div class="container"> | |||
<footer class="footer"> | |||
<div class="footer-left"> | |||
<span class="footer-item">Yarn</span> | |||
<span class="footer-item"><a href="https://github.com/yarnpkg/yarn/blob/master/LICENSE">{{i18n.site_bsd_license}}</a></span> | |||
<span class="footer-item"><a href="{{url_base}}/org/code-of-conduct">{{i18n.site_code_of_conduct}}</a></span> | |||
</div> | |||
<div class="footer-right"> | |||
{% if layout.shitty %} | |||
<span class="footer-item"><a href="https://en.wikipedia.org/wiki/Hector_Janse_van_Rensburg">Artwork by Shitty Watercolour</a></span> | |||
{% endif %} | |||
{% if vars_active_language.tag == "en" %} | |||
<span class="footer-item"><a href="https://github.com/yarnpkg/website/edit/master/{{page.path}}">{{i18n.site_edit_this_page}}</a></span> | |||
{% else %} | |||
<span class="footer-item"><a href="{{url_base}}/org/translations">{{i18n.site_edit_this_page}}</a></span> | |||
{% endif %} | |||
</div> | |||
</footer> | |||
</div> | |||
<script> | |||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | |||
ga('create', '{{ site.gacode }}', 'auto'); | |||
ga('send', 'pageview'); | |||
</script> |
@@ -0,0 +1,24 @@ | |||
{% comment %} | |||
get the current translations from Jekyll, to be used in scripts | |||
{% endcomment }%} | |||
<script> | |||
var i18n_default = {{ site.data.i18n.en.script | jsonify }}; | |||
window.i18n = {{ i18n.script | jsonify }} || {}; | |||
window.i18n.url_base = {{ url_base | jsonify }}; | |||
window.i18n.active_language = {{ vars_active_language.tag | jsonify }}; | |||
// give defaults | |||
function copyDefaults(from, to) { | |||
for (var key in from) { | |||
if (from[key] !== null && typeof from[key] === 'object') { | |||
copyDefaults(from[key], to[key] || (to[key] = {})); | |||
continue; | |||
} | |||
if (to.hasOwnProperty(key) === false || to[key] === null) { | |||
to[key] = from[key]; | |||
} | |||
} | |||
} | |||
copyDefaults(i18n_default, window.i18n); | |||
</script> |
@@ -0,0 +1,76 @@ | |||
{% include vars.html %} | |||
<nav class="navbar navbar-static-top navbar-light"> | |||
<div class="container"> | |||
<a href="{{url_base}}/"> | |||
{% include svg/yarn-kitten-full.svg class="navbar-logo" %} | |||
<span class="sr-only">Yarn</span> | |||
</a> | |||
<div class="clearfix hidden-lg-up"> | |||
<button class="navbar-toggler hidden-lg-up float-right" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="exCollapsingNavbar2" | |||
aria-expanded="false" aria-label="Toggle navigation"></button> | |||
</div> | |||
<div class="collapse navbar-toggleable-md" id="navbar"> | |||
<ul class="nav navbar-nav"> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="{{url_base}}/docs/getting-started">{{i18n.site_nav_getting_started}}</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="{{url_base}}/docs">{{i18n.site_nav_documentation}}</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="{{url_base}}/packages">{{i18n.site_nav_packages}}</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="{{site.baseurl}}/blog">{{i18n.site_nav_blog}}</a> | |||
</li> | |||
</ul> | |||
<ul class="nav navbar-nav navbar-nav-right float-lg-right"> | |||
{% assign path = page.url | split: '/' %} {% unless path[1] == 'blog' %} | |||
<li class="nav-item dropdown"> | |||
<a id="dropdownNavLanguage" class="nav-link dropdown-toggle" role="button" href="#" data-toggle="dropdown" aria-haspopup="true" | |||
aria-expanded="false"> | |||
{% include svg/language.svg class="navbar-icon" %} {{vars_active_language.name}} | |||
</a> | |||
<div class="dropdown-menu" aria-labelledby="dropdownNavLanguage" id="dropdownNavLanguageMenu"> | |||
{% for language in site.data.languages %} {% if language.enabled %} | |||
<a href="{{site.baseurl}}/{{language.tag}}/{{vars_url_relative}}" class="dropdown-item{% if vars_active_language.tag == language.tag %} active{% endif %}" | |||
data-lang="{{language.accept_languages[0]}}"> | |||
{{language.name}} | |||
</a> | |||
{% endif %} {% endfor %} | |||
</div> | |||
</li> | |||
{% endunless %} | |||
<li class="nav-item"> | |||
<a class="nav-link" href="https://discord.gg/yarnpkg"> | |||
{% include svg/discord.svg class="navbar-icon" %} | |||
<span class="sr-only">Discord</span> | |||
</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="https://twitter.com/yarnpkg"> | |||
{% include svg/twitter.svg class="navbar-icon" %} | |||
<span class="sr-only">Twitter</span> | |||
</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="https://www.facebook.com/yarnpkg"> | |||
{% include svg/facebook.svg class="navbar-icon" %} | |||
<span class="sr-only">Facebook</span> | |||
</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="https://github.com/yarnpkg"> | |||
{% include svg/github.svg class="navbar-icon" %} | |||
<span class="sr-only">GitHub</span> | |||
</a> | |||
</li> | |||
</ul> | |||
</div> | |||
</div> | |||
</nav> |
@@ -0,0 +1,14 @@ | |||
{% include vars.html %} | |||
<div class="news-container"> | |||
<a class="news-overlay" href="https://yarnpkg.com/getting-started/migration"></a> | |||
<div class="news-inner"> | |||
<div class="news-line"> | |||
<span class="news-highlight">{{i18n.site_news_important}}:</span> {{i18n.site_news_docs_cover_v1}} | |||
</div> | |||
<div class="news-line"> | |||
{{i18n.site_news_see_v2_docs}} | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,4 @@ | |||
<svg class="discord {{include.class}}" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="discord-svg" fill="none" viewBox="0 0 71 55"> | |||
<title id="discord-svg">Discord</title> | |||
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"/> | |||
</svg> |
@@ -0,0 +1,4 @@ | |||
<svg class="facebook {{include.class}}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 266.9 266.9" role="img" aria-labelledby="facebook-svg"> | |||
<title id="facebook-svg">Facebook</title> | |||
<path d="M252.2 0H14.7C6.6 0 0 6.6 0 14.7v237.4c0 8.1 6.6 14.7 14.7 14.7h127.8V163.5h-34.8v-40.3h34.8V93.6c0-34.5 21.1-53.2 51.8-53.2 14.7 0 27.4 1.1 31.1 1.6v36h-21.3c-16.7 0-20 7.9-20 19.6v25.7H224l-5.2 40.3h-34.7V267h68c8.1 0 14.7-6.6 14.7-14.7V14.7c.1-8.1-6.5-14.7-14.6-14.7z"/> | |||
</svg> |
@@ -0,0 +1,4 @@ | |||
<svg class="github {{include.class}}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.6 31.8" role="img" aria-labelledby="github-svg"> | |||
<title id="github-svg">GitHub</title> | |||
<path d="M16.3 0C7.3 0 0 7.3 0 16.3c0 7.2 4.7 13.3 11.1 15.5.8.1 1.1-.4 1.1-.8v-2.8c-4.5 1-5.5-2.2-5.5-2.2-.7-1.9-1.8-2.4-1.8-2.4-1.5-1 .1-1 .1-1 1.6.1 2.5 1.7 2.5 1.7 1.5 2.5 3.8 1.8 4.7 1.4.1-1.1.6-1.8 1-2.2-3.6-.4-7.4-1.8-7.4-8.1 0-1.8.6-3.2 1.7-4.4-.1-.3-.7-2 .2-4.2 0 0 1.4-.4 4.5 1.7 1.3-.4 2.7-.5 4.1-.5 1.4 0 2.8.2 4.1.5 3.1-2.1 4.5-1.7 4.5-1.7.9 2.2.3 3.9.2 4.3 1 1.1 1.7 2.6 1.7 4.4 0 6.3-3.8 7.6-7.4 8 .6.5 1.1 1.5 1.1 3V31c0 .4.3.9 1.1.8 6.5-2.2 11.1-8.3 11.1-15.5C32.6 7.3 25.3 0 16.3 0z"/> | |||
</svg> |
@@ -0,0 +1,3 @@ | |||
<svg class="language {{include.class}}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> | |||
<path d="M19.753 10.909c-.624-1.707-2.366-2.726-4.661-2.726-.09 0-.176.002-.262.006l-.016-2.063 3.525-.607c.115-.019.133-.119.109-.231-.023-.111-.167-.883-.188-.976-.027-.131-.102-.127-.207-.109-.104.018-3.25.461-3.25.461l-.013-2.078c-.001-.125-.069-.158-.194-.156l-1.025.016c-.105.002-.164.049-.162.148l.033 2.307s-3.061.527-3.144.543c-.084.014-.17.053-.151.143.019.09.19 1.094.208 1.172.018.08.072.129.188.107l2.924-.504.035 2.018c-1.077.281-1.801.824-2.256 1.303-.768.807-1.207 1.887-1.207 2.963 0 1.586.971 2.529 2.328 2.695 3.162.387 5.119-3.06 5.769-4.715 1.097 1.506.256 4.354-2.094 5.98-.043.029-.098.129-.033.207l.619.756c.08.096.206.059.256.023 2.51-1.73 3.661-4.515 2.869-6.683zm-7.386 3.188c-.966-.121-.944-.914-.944-1.453 0-.773.327-1.58.876-2.156a3.21 3.21 0 0 1 1.229-.799l.082 4.277a2.773 2.773 0 0 1-1.243.131zm2.427-.553l.046-4.109c.084-.004.166-.01.252-.01.773 0 1.494.145 1.885.361.391.217-1.023 2.713-2.183 3.758zm-8.95-7.668a.196.196 0 0 0-.196-.145h-1.95a.194.194 0 0 0-.194.144L.008 16.916c-.017.051-.011.076.062.076h1.733c.075 0 .099-.023.114-.072l1.008-3.318h3.496l1.008 3.318c.016.049.039.072.113.072h1.734c.072 0 .078-.025.062-.076-.014-.05-3.083-9.741-3.494-11.04zm-2.618 6.318l1.447-5.25 1.447 5.25H3.226z"/> | |||
</svg> |
@@ -0,0 +1,4 @@ | |||
<svg class="twitter {{include.class}}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 203.1" role="img" aria-labelledby="twitter-svg"> | |||
<title id="twitter-svg">Twitter</title> | |||
<path d="M78.6 203.1c94.3 0 145.9-78.2 145.9-145.9 0-2.2 0-4.4-.1-6.6 10-7.3 18.7-16.3 25.6-26.5-9.4 4.1-19.3 6.9-29.5 8.1 10.7-6.4 18.7-16.5 22.5-28.4-10.1 6-21.1 10.2-32.6 12.4C191-4.5 158.5-5.5 137.8 14c-13.3 12.5-19 31.2-14.8 49C81.9 60.9 43.4 41.4 17.4 9.4 3.8 32.8 10.7 62.8 33.3 77.8c-8.2-.2-16.1-2.4-23.3-6.4v.6c0 24.4 17.2 45.4 41.2 50.3-7.6 2.1-15.5 2.4-23.2.9 6.7 20.9 26 35.2 47.9 35.6-18.2 14.3-40.6 22-63.7 22-4.1 0-8.2-.3-12.2-.7 23.5 15.1 50.7 23 78.6 23"/> | |||
</svg> |
@@ -0,0 +1,5 @@ | |||
<svg class="logo {{include.class}}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 518 518" height="{{size}}" role="img" aria-labelledby="yarn-kitten-svg"> | |||
<title id="yarn-kitten-svg">Yarn Kitten</title> | |||
<path class="logo-primary" d="M259 0c143 0 259 116 259 259S402 518 259 518 0 402 0 259 116 0 259 0z"/> | |||
<path class="logo-secondary" d="M435.2 337.5c-1.8-14.2-13.8-24-29.2-23.8-23 .3-42.3 12.2-55.1 20.1-5 3.1-9.3 5.4-13 7.1.8-11.6.1-26.8-5.9-43.5-7.3-20-17.1-32.3-24.1-39.4 8.1-11.8 19.2-29 24.4-55.6 4.5-22.7 3.1-58-7.2-77.8-2.1-4-5.6-6.9-10-8.1-1.8-.5-5.2-1.5-11.9.4C293.1 96 289.6 93.8 286.9 92c-5.6-3.6-12.2-4.4-18.4-2.1-8.3 3-15.4 11-22.1 25.2-1 2.1-1.9 4.1-2.7 6.1-12.7.9-32.7 5.5-49.6 23.8-2.1 2.3-6.2 4-10.5 5.6h.1c-8.8 3.1-12.8 10.3-17.7 23.3-6.8 18.2.2 36.1 7.1 47.7-9.4 8.4-21.9 21.8-28.5 37.5-8.2 19.4-9.1 38.4-8.8 48.7-7 7.4-17.8 21.3-19 36.9-1.6 21.8 6.3 36.6 9.8 42 1 1.6 2.1 2.9 3.3 4.2-.4 2.7-.5 5.6.1 8.6 1.3 7 5.7 12.7 12.4 16.3 13.2 7 31.6 10 45.8 2.9 5.1 5.4 14.4 10.6 31.3 10.6h1c4.3 0 58.9-2.9 74.8-6.8 7.1-1.7 12-4.7 15.2-7.4 10.2-3.2 38.4-12.8 65-30 18.8-12.2 25.3-14.8 39.3-18.2 13.6-3.3 22.1-15.7 20.4-29.4zm-23.8 14.7c-16 3.8-24.1 7.3-43.9 20.2-30.9 20-64.7 29.3-64.7 29.3s-2.8 4.2-10.9 6.1c-14 3.4-66.7 6.3-71.5 6.4-12.9.1-20.8-3.3-23-8.6-6.7-16 9.6-23 9.6-23s-3.6-2.2-5.7-4.2c-1.9-1.9-3.9-5.7-4.5-4.3-2.5 6.1-3.8 21-10.5 27.7-9.2 9.3-26.6 6.2-36.9.8-11.3-6 .8-20.1.8-20.1s-6.1 3.6-11-3.8c-4.4-6.8-8.5-18.4-7.4-32.7 1.2-16.3 19.4-32.1 19.4-32.1s-3.2-24.1 7.3-48.8c9.5-22.5 35.1-40.6 35.1-40.6s-21.5-23.8-13.5-45.2c5.2-14 7.3-13.9 9-14.5 6-2.3 11.8-4.8 16.1-9.5 21.5-23.2 48.9-18.8 48.9-18.8s13-39.5 25-31.8c3.7 2.4 17 32 17 32s14.2-8.3 15.8-5.2c8.6 16.7 9.6 48.6 5.8 68-6.4 32-22.4 49.2-28.8 60-1.5 2.5 17.2 10.4 29 43.1 10.9 29.9 1.2 55 2.9 57.8.3.5.4.7.4.7s12.5 1 37.6-14.5c13.4-8.3 29.3-17.6 47.4-17.8 17.5-.3 18.4 20.2 5.2 23.4z"/> | |||
</svg> |
@@ -0,0 +1,14 @@ | |||
<svg class="logo {{include.class}}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1154.8 518"> | |||
<defs> | |||
<path id="main" d=" | |||
M718.6 257.8c-8 27.6-20.8 47.6-35.2 63.6V181c0-9.6-8.4-17.6-21.6-17.6-5.6 0-10.4 2.8-10.4 6.8 0 2.8 1.6 5.2 1.6 12.8v64.4c-4.8 28-16.8 54-32.8 54-11.6 0-18.4-11.6-18.4-33.2 0-33.6 4.4-51.2 11.6-80.8 1.6-6 13.2-22-6.4-22-21.2 0-18.4 8-21.2 14.8 0 0-13.4 47.6-13.4 90 0 34.8 14.6 57.6 41.4 57.6 17.2 0 29.6-11.6 39.2-27.6V351c-26.4 23.2-49.6 43.6-49.6 84 0 25.6 16 46 38.4 46 20.4 0 41.6-14.8 41.6-56.8V355c21.6-18.8 44.8-42.4 58.4-88.8.4-1.6.4-3.6.4-4 0-7.6-7.6-16.4-14-16.4-4 0-7.2 3.6-9.6 12zm-76.8 198c-6.4 0-10.4-9.6-10.4-22 0-24 8.8-39.2 21.6-52.4v42.8c0 7.6 1.6 31.6-11.2 31.6z | |||
M833.4 301c-9.6 0-13.6-9.6-13.6-18.4v-66c0-9.6-8.4-17.6-21.6-17.6-5.6 0-10.4 2.8-10.4 6.8 0 2.8 1.6 5.2 1.6 12.8v61.6C785 291.4 777.8 301 767 301c-14 0-22.8-12-22.8-32.8 0-57.6 35.6-83.6 66-83.6 4 0 8 .8 11.6.8 4 0 5.2-2.4 5.2-9.2 0-10.4-7.6-16.8-18.4-16.8-48.8 0-95.2 40.8-95.2 107.6 0 34 16.4 60.4 47.6 60.4 15.2 0 26.4-7.2 34.4-16.4 6 9.6 16.8 16.4 30.8 16.4 34.4 0 50.4-36 57.2-62.4.4-1.6.4-2.4.4-2.8 0-7.6-7.6-16.4-14-16.4-4 0-8 3.6-9.6 12-3.6 17.6-10.8 43.2-26.8 43.2z | |||
M949 327.4c34.4 0 50-36 57.2-62.4 0-.8.4-1.6.4-2.8 0-7.6-7.6-16.4-14-16.4-4 0-8 3.6-9.6 12-3.6 17.6-10.4 43.2-28.8 43.2-10.8 0-16-10.4-16-21.6 0-40 18-87.2 18-92 1.6-9.2-14.4-22.4-19.2-22.4h-20.8c-4 0-8 0-21.2-1.6-4.4-16.4-15.6-21.2-25.2-21.2-10.4 0-20 7.2-20 18.4 0 11.6 7.2 20 17.2 25.6-.4 20.4-2 53.6-6.4 69.6-3.6 13.6 17.2 28 22.4 11.2 7.2-23.2 9.6-58 10-73.6h34.8c-12.8 34.4-20 62.8-20 88.4 0 35.2 22.4 45.6 41.2 45.6z | |||
M984.6 309.8c0 14.8 11.2 17.6 19.2 17.6 11.6 0 11.2-9.6 11.2-17.2v-58.4c2.8-31.6 27.6-66 39.2-66 7.6 0 8.4 10.4 8.4 22.8v81.2c0 20.4 12.4 37.6 33.6 37.6 34.4 0 51.4-36 58.2-62.4.4-1.6.4-2.4.4-2.8 0-7.6-7.6-16.4-14-16.4-4 0-8 3.6-9.6 12-3.6 17.6-11.8 43.2-27.8 43.2-10.4 0-10.4-14.8-10.4-18.4v-82.8c0-18.4-6.4-40.4-33.2-40.4-19.6 0-34 17.2-44.8 39.6v-18c0-9.6-8.4-17.6-21.6-17.6-5.6 0-10.4 2.8-10.4 6.8 0 2.8 1.6 5.2 1.6 12.8v126.8z | |||
M259 0c143 0 259 116 259 259S402 518 259 518 0 402 0 259 116 0 259 0z"/> | |||
</defs> | |||
<use class="logo-primary" xlink:href="#main" x="0" y="0"/> | |||
<path class="logo-secondary" d="M435.2 337.5c-1.8-14.2-13.8-24-29.2-23.8-23 .3-42.3 12.2-55.1 20.1-5 3.1-9.3 5.4-13 7.1.8-11.6.1-26.8-5.9-43.5-7.3-20-17.1-32.3-24.1-39.4 8.1-11.8 19.2-29 24.4-55.6 4.5-22.7 3.1-58-7.2-77.8-2.1-4-5.6-6.9-10-8.1-1.8-.5-5.2-1.5-11.9.4C293.1 96 289.6 93.8 286.9 92c-5.6-3.6-12.2-4.4-18.4-2.1-8.3 3-15.4 11-22.1 25.2-1 2.1-1.9 4.1-2.7 6.1-12.7.9-32.7 5.5-49.6 23.8-2.1 2.3-6.2 4-10.5 5.6h.1c-8.8 3.1-12.8 10.3-17.7 23.3-6.8 18.2.2 36.1 7.1 47.7-9.4 8.4-21.9 21.8-28.5 37.5-8.2 19.4-9.1 38.4-8.8 48.7-7 7.4-17.8 21.3-19 36.9-1.6 21.8 6.3 36.6 9.8 42 1 1.6 2.1 2.9 3.3 4.2-.4 2.7-.5 5.6.1 8.6 1.3 7 5.7 12.7 12.4 16.3 13.2 7 31.6 10 45.8 2.9 5.1 5.4 14.4 10.6 31.3 10.6h1c4.3 0 58.9-2.9 74.8-6.8 7.1-1.7 12-4.7 15.2-7.4 10.2-3.2 38.4-12.8 65-30 18.8-12.2 25.3-14.8 39.3-18.2 13.6-3.3 22.1-15.7 20.4-29.4zm-23.8 14.7c-16 3.8-24.1 7.3-43.9 20.2-30.9 20-64.7 29.3-64.7 29.3s-2.8 4.2-10.9 6.1c-14 3.4-66.7 6.3-71.5 6.4-12.9.1-20.8-3.3-23-8.6-6.7-16 9.6-23 9.6-23s-3.6-2.2-5.7-4.2c-1.9-1.9-3.9-5.7-4.5-4.3-2.5 6.1-3.8 21-10.5 27.7-9.2 9.3-26.6 6.2-36.9.8-11.3-6 .8-20.1.8-20.1s-6.1 3.6-11-3.8c-4.4-6.8-8.5-18.4-7.4-32.7 1.2-16.3 19.4-32.1 19.4-32.1s-3.2-24.1 7.3-48.8c9.5-22.5 35.1-40.6 35.1-40.6s-21.5-23.8-13.5-45.2c5.2-14 7.3-13.9 9-14.5 6-2.3 11.8-4.8 16.1-9.5 21.5-23.2 48.9-18.8 48.9-18.8s13-39.5 25-31.8c3.7 2.4 17 32 17 32s14.2-8.3 15.8-5.2c8.6 16.7 9.6 48.6 5.8 68-6.4 32-22.4 49.2-28.8 60-1.5 2.5 17.2 10.4 29 43.1 10.9 29.9 1.2 55 2.9 57.8.3.5.4.7.4.7s12.5 1 37.6-14.5c13.4-8.3 29.3-17.6 47.4-17.8 17.5-.3 18.4 20.2 5.2 23.4z"/> | |||
</svg> |
@@ -0,0 +1,22 @@ | |||
{% capture __vars %} | |||
{% assign vars_url_parts = page.url | split: "/" | shift %} | |||
{% assign vars_url_language = vars_url_parts[1] %} | |||
{% assign vars_url_relative = vars_url_parts | shift | shift | join: "/" | replace: ".html", "" %} | |||
{% assign vars_temp_matched_languages = site.data.languages | where: "tag", vars_url_language %} | |||
{% assign vars_active_language = vars_temp_matched_languages[0] %} | |||
{% unless vars_active_language %} | |||
{% assign vars_active_language = site.data.languages[0] %} | |||
{% assign vars_production_url_base_language = "" %} | |||
{% else %} | |||
{% assign vars_production_url_base_language = "/" | append: vars_active_language.tag %} | |||
{% endunless %} | |||
{% assign i18n = site.data.i18n[vars_active_language.tag] %} | |||
{% if jekyll.environment == "development" %} | |||
{% assign url_base = site.baseurl | append: "/lang/" | append: vars_active_language.tag %} | |||
{% else %} | |||
{% assign url_base = site.baseurl | append: vars_production_url_base_language %} | |||
{% endif %} | |||
{% endcapture %} |
@@ -0,0 +1,13 @@ | |||
<div class="alert alert-warning news-container-alert mt-4"> | |||
<a class="news-overlay" href="https://yarnpkg.com/getting-started/migration#why-should-you-migrate"></a> | |||
<div class="news-inner"> | |||
<div> | |||
These instructions only cover Yarn versions prior to 2.0. Those versions entered <a href="https://en.wikipedia.org/wiki/Maintenance_mode">maintenance mode</a> in January 2020 and will eventually reach their end-of-life in terms of support. Please see the main website for the most up-to-date documentation: <a href="https://yarnpkg.com/getting-started/migration#why-should-you-migrate">yarnpkg.com/getting-started/migration</a>. | |||
</div> | |||
<div style="margin-top: 15px"> | |||
The latest Yarn version is: <img alt="Latest CLI Release" src="https://img.shields.io/npm/v/@yarnpkg/cli/latest?label=latest"> | |||
</div> | |||
</div> | |||
</div> | |||
{% include version.html %} |
@@ -0,0 +1,25 @@ | |||
<div class="mt-4"> | |||
<div> | |||
{{i18n.site_nav_stable_version}}: | |||
<strong class="navbar-text"> | |||
<a href="https://github.com/yarnpkg/yarn/blob/master/CHANGELOG.md"> | |||
v{{site.latest_version}} | |||
</a> | |||
</strong> | |||
{% if site.show_rc %} | |||
<span aria-hidden="true">•</span> | |||
{{i18n.site_nav_rc_version}} | |||
<strong> | |||
<a href="https://github.com/yarnpkg/yarn/releases/tag/v{{site.latest_rc_version}}"> | |||
v{{site.latest_rc_version}} | |||
</a> | |||
</strong> | |||
{% endif %} | |||
</div> | |||
<div> | |||
{{i18n.site_nav_node_support}}: | |||
<strong> | |||
{{site.node_support}} | |||
</strong> | |||
</div> | |||
</div> |
@@ -0,0 +1,57 @@ | |||
{% include vars.html %} | |||
<!doctype html> | |||
<html lang="{% if vars_active_language.tag %}{{vars_active_language.tag}}{% else %}en{% endif %}"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> | |||
<meta property="og:url" content="{{ page.url | replace: 'index.html', '' | replace: '/lang', '' | prepend: site.baseurl | prepend: site.url }}" /> | |||
<meta property="og:site_name" content="{{ site.title }}"/> | |||
<meta property="og:title" content="{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}" /> | |||
<meta property="og:image" content="{{ site.url }}{{ site.baseurl }}/assets/og_image.png" /> | |||
<meta property="og:description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ site.description | strip_html | strip_newlines }}{% endif %}" /> | |||
<title>{% if page.layout == "post" %}{{page.title}} | {{ i18n.blog_title }}{% elsif page.title %}{{page.title}} | {{ i18n.site_title }}{% elsif page.id %}{{ i18n[page.id] }} | {{ i18n.site_title }}{% else %}{{ i18n.site_title }}{% endif %}</title> | |||
<meta name="description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ i18n.site_description }}{% endif %}"> | |||
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}"> | |||
<link rel="icon" href="{{ "/favicon.ico" | prepend: site.baseurl }}" type="image/x-icon"> | |||
<link rel="search" href="/opensearch.xml" type="application/opensearchdescription+xml" title="Yarn"> | |||
{% for stylesheet in page.stylesheets %} | |||
<link rel="stylesheet" href="{{stylesheet}}" /> | |||
{% endfor %} | |||
<link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}?t={{ site.time | date_to_xmlschema }}"> | |||
<script src="https://cdn.polyfill.io/v2/polyfill.js?unknown=polyfill&excludes=Element"></script> | |||
<meta name="google-site-verification" content="DIcCyEkVaGHm864NWzItnt2n6Gg7hz3l47RBIRyxvcQ" /> | |||
</head> | |||
<body> | |||
{% include debug/url.html url=page.url %} | |||
{% include debug/todo.html content=page.content %} | |||
{% include news.html %} | |||
{% include navigation.html %} | |||
<div id="search"> | |||
<!-- Here to avoid flash of unstyled content on page load --> | |||
<div class="ais-InstantSearch-root full-searchbox"> | |||
<form novalidate="" class="ais-SearchBox"> | |||
<div role="search" class="ais-SearchBox-form"></div> | |||
</form> | |||
</div> | |||
</div> | |||
<main>{{content}}</main> | |||
{% include footer.html %} | |||
{% include i18n-as-script.html %} | |||
<script src="{{site.data.webpack['/js/build/vendor.js']}}"></script> | |||
<script src="{{site.data.webpack['/js/build/common.js']}}"></script> | |||
{% for script in layout.scripts %} | |||
<script src="{{site.data.webpack[script]}}"></script> | |||
{% endfor %} | |||
{% for script in page.scripts %} | |||
<script src="{{site.data.webpack[script]}}"></script> | |||
{% endfor %} | |||
</body> | |||
</html> |
@@ -0,0 +1,73 @@ | |||
--- | |||
layout: page | |||
--- | |||
{% include vars.html %} | |||
{% assign matched_guides = site.data.guides | where: "id", page.guide %} | |||
{% assign current_guide = matched_guides[0] %} | |||
{% assign guides_size = current_guide.pages | size | plus: 2 %} | |||
{% for i in (0..guides_size) %} | |||
{% if current_guide.pages[i].id == page.id %} | |||
{% assign guide_prev_index = i | minus: 1 %} | |||
{% assign guide_next_index = i | plus: 1 %} | |||
{% break %} | |||
{% endif %} | |||
{% endfor %} | |||
{% assign guide_prev = current_guide.pages[guide_prev_index] %} | |||
{% assign guide_next = current_guide.pages[guide_next_index] %} | |||
<div class="row"> | |||
<div class="col-md-8 guide"> | |||
{% include debug/toc.html content=page.content %} | |||
<div class="guide-content"> | |||
{{content}} | |||
</div> | |||
<div class="guide-controls"> | |||
<hr> | |||
<div class="clearfix"> | |||
{% if guide_prev_index > -1 %} | |||
<a class="btn btn-outline-primary float-left" href="{{url_base}}{{guide_prev.path}}"> | |||
← | |||
{{i18n[guide_prev.id]}} | |||
</a> | |||
{% endif %} | |||
{% if guide_next %} | |||
<a class="btn btn-primary float-right" href="{{url_base}}{{guide_next.path}}"> | |||
{{i18n[guide_next.id]}} | |||
→ | |||
</a> | |||
{% endif %} | |||
</div> | |||
</div> | |||
{% if page.additional_reading_tags %} | |||
{% include additional-reading.html tags=page.additional_reading_tags %} | |||
{% endif %} | |||
</div> | |||
<div class="col-md-4"> | |||
<ul class="nav nav-stacked guide-nav"> | |||
{% for step in current_guide.pages %} | |||
<li class="nav-item"> | |||
{% if step.id == page.id %} | |||
<a class="nav-link active"> | |||
{{i18n[step.id]}} | |||
</a> | |||
{% else %} | |||
<a class="nav-link" href="{{url_base}}{{step.path}}"> | |||
{{i18n[step.id]}} | |||
</a> | |||
{% endif %} | |||
</li> | |||
{% endfor %} | |||
</ul> | |||
</div> | |||
</div> |
@@ -0,0 +1,21 @@ | |||
--- | |||
layout: default | |||
--- | |||
{% include vars.html %} | |||
<div class="hero"> | |||
<div class="container"> | |||
<h1 class="hero-text display-4">{{i18n[page.id]}}</h1> | |||
{% if layout.hero_subtext %} | |||
{% include {{ layout.hero_subtext }} %} | |||
{% elsif page.hero_subtext %} | |||
{% include {{ page.hero_subtext }} %} | |||
{% endif %} | |||
</div> | |||
</div> | |||
<div class="container"> | |||
{{content}} | |||
</div> |
@@ -0,0 +1,15 @@ | |||
--- | |||
layout: default | |||
--- | |||
{% include vars.html %} | |||
<div class="hero"> | |||
<div class="container"> | |||
<h1 class="hero-text display-4">{{i18n.detail.title}}</h1> | |||
</div> | |||
</div> | |||
<div class="container"> | |||
{{content}} | |||
</div> |
@@ -0,0 +1,36 @@ | |||
--- | |||
layout: page | |||
scripts: | |||
- "/js/build/documentation.js" | |||
--- | |||
{% include vars.html %} | |||
{% assign guides_size = site.data.guides | size | minus: 1 %} | |||
{% include docsearch.html %} | |||
{% for index in (0..guides_size) %} | |||
{% assign index_mod = index | modulo: 3 %} | |||
{% if index_mod == 0 %} | |||
<div class="row"> | |||
{% endif %} | |||
{% assign guide = site.data.guides[index] %} | |||
<div class="col-lg-4"> | |||
<a class="card guide-card" href="{{url_base}}{{guide.pages[0].path}}"> | |||
<div class="card-block"> | |||
<h4 class="card-title">{{i18n[guide.title]}}</h4> | |||
<p class="card-text text-muted"> | |||
{{i18n[guide.description]}} | |||
<span class="float-right text-primary">{{i18n.read_more}}</span> | |||
</p> | |||
</div> | |||
</a> | |||
</div> | |||
{% if index_mod == 2 %} | |||
</div> | |||
{% endif %} | |||
{% endfor %} |
@@ -0,0 +1,130 @@ | |||
--- | |||
layout: default | |||
shitty: Yes | |||
--- | |||
{% include vars.html %} | |||
<div class="hero"> | |||
<div class="container"> | |||
<h1 class="hero-title"> | |||
{{i18n.homepage_tagline}} | |||
</h1> | |||
<div> | |||
<a class="btn hero-btn hidden-md-down" href="{{url_base}}/docs/getting-started">{{i18n.homepage_c2a_button}}</a> | |||
<a class="btn hero-btn hidden-md-down" href="{{url_base}}/docs/install">{{i18n.homepage_install_button}}</a> | |||
<a class="btn hero-btn hidden-md-down" href="https://yarnpkg.com/getting-started/migration">{{i18n.homepage_migrate_button}}</a> | |||
<span class="hero-ghbtn"> | |||
<iframe src="https://ghbtns.com/github-btn.html?user=yarnpkg&repo=yarn&type=star&count=true&size=large" frameborder="0" scrolling="0" width="160px" height="30px" title="{{i18n.iframe_github_stars}}"></iframe> | |||
</span> | |||
</div> | |||
{% include version.html %} | |||
</div> | |||
</div> | |||
<div class="container features"> | |||
<div class="row feature"> | |||
<div class="col-lg-7"> | |||
<h2 class="feature-heading"> | |||
{{i18n.homepage_feature_fast_title}} | |||
</h2> | |||
<p class="feature-text"> | |||
{{i18n.homepage_feature_fast_description}} | |||
</p> | |||
</div> | |||
<div class="col-lg-5"> | |||
<img class="feature-image img-fluid mx-auto feature-image-speed" | |||
src="/assets/feature-speed.png" | |||
width="500" height="300" | |||
alt="{{i18n.homepage_feature_fast_image_alt}}"/> | |||
</div> | |||
</div> | |||
<hr class="feature-divider"> | |||
<div class="row feature"> | |||
<div class="col-lg-7 push-lg-5"> | |||
<h2 class="feature-heading"> | |||
{{i18n.homepage_feature_secure_title}} | |||
</h2> | |||
<p class="feature-text"> | |||
{{i18n.homepage_feature_secure_description}} | |||
</p> | |||
</div> | |||
<div class="col-lg-5 pull-lg-7"> | |||
<img class="feature-image img-fluid mx-auto feature-image-secure" | |||
src="/assets/feature-secure.png" | |||
width="375" height="300" | |||
alt="{{i18n.homepage_feature_secure_image_alt}}"/> | |||
</div> | |||
</div> | |||
<hr class="feature-divider"> | |||
<div class="row feature"> | |||
<div class="col-lg-7"> | |||
<h2 class="feature-heading"> | |||
{{i18n.homepage_feature_reliable_title}} | |||
</h2> | |||
<p class="feature-text"> | |||
{{i18n.homepage_feature_reliable_description}} | |||
</p> | |||
</div> | |||
<div class="col-lg-5"> | |||
<img class="feature-image img-fluid mx-auto feature-image-reliable" | |||
src="/assets/feature-reliable.png" | |||
width="500" height="300" | |||
alt="{{i18n.homepage_feature_reliable_image_alt}}"/> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="hero"> | |||
<div class="container"> | |||
<p class="hero-prompt float-md-left"> | |||
{{i18n.homepage_c2a_text}} | |||
</p> | |||
<a href="{{url_base}}/docs/getting-started" class="btn hero-btn float-md-right"> | |||
{{i18n.homepage_c2a_button}} | |||
</a> | |||
</div> | |||
</div> | |||
<div class="container featurettes"> | |||
<div class="row"> | |||
<div class="col-lg-4 col-md-6 featurette"> | |||
<h2>{{i18n.homepage_featurette_offline_title}}</h2> | |||
<p>{{i18n.homepage_featurette_offline_description}}</p> | |||
</div> | |||
<div class="col-lg-4 col-md-6 featurette"> | |||
<h2>{{i18n.homepage_featurette_deterministic_title}}</h2> | |||
<p>{{i18n.homepage_featurette_deterministic_description}}</p> | |||
</div> | |||
<div class="col-lg-4 col-md-6 featurette"> | |||
<h2>{{i18n.homepage_featurette_network_performance_title}}</h2> | |||
<p>{{i18n.homepage_featurette_network_performance_description}}</p> | |||
</div> | |||
<div class="col-lg-4 col-md-6 featurette"> | |||
<h2>{{i18n.homepage_featurette_npm_title}}</h2> | |||
<p>{{i18n.homepage_featurette_npm_description}}</p> | |||
</div> | |||
<div class="col-lg-4 col-md-6 featurette"> | |||
<h2>{{i18n.homepage_featurette_network_resillience_title}}</h2> | |||
<p>{{i18n.homepage_featurette_network_resillience_description}}</p> | |||
</div> | |||
<div class="col-lg-4 col-md-6 featurette"> | |||
<h2>{{i18n.homepage_featurette_flat_mode_title}}</h2> | |||
<p>{{i18n.homepage_featurette_flat_mode_description}}</p> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- [TODO: Add users section once the users page has more people] --> |
@@ -0,0 +1,72 @@ | |||
--- | |||
layout: guide | |||
scripts: | |||
- "/js/build/install.js" | |||
--- | |||
{% include vars.html %} | |||
{{i18n.ci_intro}} | |||
{% capture travis %} | |||
{% include_relative _ci/travis.md %} | |||
{% endcapture %} | |||
{% capture semaphore %} | |||
{% include_relative _ci/semaphore.md %} | |||
{% endcapture %} | |||
{% capture appveyor %} | |||
{% include_relative _ci/appveyor.md %} | |||
{% endcapture %} | |||
{% capture circle %} | |||
{% include_relative _ci/circle.md %} | |||
{% endcapture %} | |||
{% capture codeship %} | |||
{% include_relative _ci/codeship.md %} | |||
{% endcapture %} | |||
{% capture solano %} | |||
{% include_relative _ci/solano.md %} | |||
{% endcapture %} | |||
{% capture gitlab %} | |||
{% capture exists %}{% file_exists _ci/gitlab.md %}{% endcapture %} | |||
{% if exists == "true" %} | |||
{% include_relative _ci/gitlab.md %} | |||
{% endif %} | |||
{% endcapture %} | |||
{% capture codefresh %} | |||
{% capture exists %}{% file_exists _ci/codefresh.md %}{% endcapture %} | |||
{% if exists == "true" %} | |||
{% include_relative _ci/codefresh.md %} | |||
{% endif %} | |||
{% endcapture %} | |||
<div class="tabs"> | |||
<nav> | |||
<div class="nav nav-tabs bg-faded text-center" id="nav-tab"> | |||
<a id="appveyor-tab" class="nav-item nav-link" data-toggle="tab" href="#appveyor">{{i18n.ci_appveyor}}</a> | |||
<a id="circle-tab" class="nav-item nav-link" data-toggle="tab" href="#circle">{{i18n.ci_circle}}</a> | |||
<a id="codeship-tab" class="nav-item nav-link" data-toggle="tab" href="#codeship">{{i18n.ci_codeship}}</a> | |||
<a id="travis-tab" class="nav-item nav-link" data-toggle="tab" href="#travis">{{i18n.ci_travis}}</a> | |||
<a id="semaphore-tab" class="nav-item nav-link" data-toggle="tab" href="#semaphore">{{i18n.ci_semaphore}}</a> | |||
<a id="solano-tab" class="nav-item nav-link" data-toggle="tab" href="#solano">{{i18n.ci_solano}}</a> | |||
<a id="gitlab-tab" class="nav-item nav-link" data-toggle="tab" href="#gitlab">{{i18n.ci_gitlab}}</a> | |||
<a id="codefresh-tab" class="nav-item nav-link" data-toggle="tab" href="#codefresh">{{i18n.ci_codefresh}}</a> | |||
</div> | |||
</nav> | |||
<div class="tab-content"> | |||
<div id="select-platform" class="tab-pane bg-faded active"> | |||
<p class="text-center my-4 text-muted"> | |||
{{i18n.ci_select_platform}} | |||
</p> | |||
</div> | |||
<div class="tab-pane" id="appveyor">{{ appveyor | markdownify }}</div> | |||
<div class="tab-pane" id="circle">{{ circle | markdownify }}</div> | |||
<div class="tab-pane" id="codeship">{{ codeship | markdownify }}</div> | |||
<div class="tab-pane" id="travis">{{ travis | markdownify }}</div> | |||
<div class="tab-pane" id="semaphore">{{ semaphore | markdownify }}</div> | |||
<div class="tab-pane" id="solano">{{ solano | markdownify }}</div> | |||
<div class="tab-pane" id="gitlab">{{ gitlab | markdownify }}</div> | |||
<div class="tab-pane" id="codefresh">{{ codefresh | markdownify }}</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,84 @@ | |||
--- | |||
layout: guide | |||
hero_subtext: version-with-upgrade-notice.html | |||
scripts: | |||
- "/js/build/install.js" | |||
operating_systems: | |||
- alpine | |||
- arch | |||
- centos | |||
- debian | |||
- gentoo | |||
- mac | |||
- solus | |||
- windows | |||
- alternatives | |||
--- | |||
{% include vars.html %} | |||
{{i18n.install_intro}} | |||
{% capture npm_info %}{% include_relative _installations/npm.md %}{% endcapture %} | |||
{{npm_info | markdownify}} | |||
<h2>{{i18n.install_alternatives}}</h2> | |||
<details> | |||
<summary>{{i18n.install_click_to_expand_collapse}}</summary> | |||
<br /> | |||
<div class="install-select-os"> | |||
<div class="form-group row"> | |||
<label for="os" class="col-3 col-form-label">{{i18n.install_os}}:</label> | |||
<div class="col-6"> | |||
<select id="os" class="form-control"> | |||
{% for os in layout.operating_systems %} | |||
{% capture os_i18n %}install_os_{{os}}{% endcapture %} | |||
<option value="{{os}}">{{i18n[os_i18n]}}</option> | |||
{% endfor %} | |||
</select> | |||
</div> | |||
</div> | |||
<div class="form-group row"> | |||
<label for="version" class="col-3 col-form-label">{{i18n.install_version}}:</label> | |||
<div class="col-6"> | |||
<select id="version" class="form-control"> | |||
<option value="stable">{{i18n.site_nav_stable_version}} ({{site.latest_version}})</option> | |||
<option value="rc">{{i18n.site_nav_rc_version}} ({{site.latest_rc_version}})</option> | |||
<option id="nightly-version" value="nightly">{{i18n.site_nav_nightly_version}}</option> | |||
</select> | |||
</div> | |||
</div> | |||
</div> | |||
<div id="install-instructions"> | |||
<div class="install-os-instructions">Loading...</div> | |||
{% for os in layout.operating_systems %} | |||
<div id="{{os}}" class="install-os-instructions" style="display: none"> | |||
{% capture os_i18n %}install_os_{{os}}{% endcapture %} | |||
<h3>{{i18n[os_i18n]}}</h3> | |||
{% capture exists %}{% file_exists _installations/{{os}}.md %}{% endcapture %} | |||
{% if exists == "true" %} | |||
{% capture info %}{% include_relative _installations/{{os}}.md %}{% endcapture %} | |||
{{info | markdownify}} | |||
{% endif %} | |||
</div> | |||
{% endfor %} | |||
</div> | |||
</details> | |||
<h2>{{i18n.install_check}}</h2> | |||
<p> | |||
{{i18n.install_check_details}} | |||
</p> | |||
{% highlight sh %} | |||
{% endhighlight %} | |||
<div class="language-sh highlighter-rouge"> | |||
<pre class="rougeHighlight"><code>yarn --version</code></pre> | |||
</div> |
@@ -0,0 +1,75 @@ | |||
--- | |||
layout: guide | |||
scripts: | |||
- "/js/build/nightly.js" | |||
--- | |||
{% include vars.html %} | |||
{% capture nightly %}{% include_relative _installations/nightly.md %}{% endcapture %} | |||
{{nightly | markdownify}} | |||
<h2>{{i18n.nightly_latest_version}}</h2> | |||
<table> | |||
<thead> | |||
<tr> | |||
<th width="45%">{{i18n.install_table_name}}</th> | |||
<th width="25%">{{i18n.install_table_type}}</th> | |||
<th width="15%">{{i18n.install_table_size}}</th> | |||
<th width="15%">{{i18n.install_table_date}}</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
{% for type in site.data.nightly_types %} | |||
<tr id="row_{{type}}"> | |||
<td> | |||
<a class="link" href="https://nightly.yarnpkg.com/latest.{{type}}"> | |||
<span class="filename">{{i18n.loading}}</span> | |||
</a> | |||
</td> | |||
<td>{{i18n.install_file[type]}}</td> | |||
<td class="size"></td> | |||
<td class="date"></td> | |||
</tr> | |||
{% endfor %} | |||
</tbody> | |||
</table> | |||
<h2>{{i18n.nightly_older_versions}}</h2> | |||
<div class="tabs"> | |||
<div id="older-versions" class="nav nav-tabs bg-faded text-center"> | |||
<ul class="nav navbar-nav nav-inline"> | |||
{% for type in site.data.nightly_types %} | |||
<a id="{{type}}-tab" class="nav-item nav-link" data-type="{{type}}" data-toggle="tab" href="#{{type}}"> | |||
{{i18n.install_file[type]}} | |||
</a> | |||
{% endfor %} | |||
</ul> | |||
</div> | |||
<div class="tab-content"> | |||
<div id="select-platform" class="tab-pane bg-faded active"> | |||
<p class="text-center my-4 text-muted"> | |||
{{i18n.nightly_select_build_type}} | |||
</p> | |||
</div> | |||
{% for type in site.data.nightly_types %} | |||
<div class="tab-pane" id="{{type}}"> | |||
<table> | |||
<thead> | |||
<tr> | |||
<th width="60%">{{i18n.install_table_name}}</th> | |||
<th width="20%">{{i18n.install_table_size}}</th> | |||
<th width="20%">{{i18n.install_table_date}}</th> | |||
</tr> | |||
</thead> | |||
<tbody id="{{type}}-body"> | |||
<tr> | |||
<td colspan="3">{{i18n.loading}}</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
{% endfor %} | |||
</div> | |||
</div> |
@@ -0,0 +1,42 @@ | |||
--- | |||
layout: page | |||
--- | |||
{% include vars.html %} | |||
<!-- [TODO: Add a million more users] --> | |||
<div class="row"> | |||
<div class="col-xl-10"> | |||
<p class="lead mb-5"> | |||
<strong>{{i18n.users_lead}}</strong> {{i18n.users_description}} | |||
</p> | |||
<div class="row"> | |||
{% for user in site.data.users %} | |||
<div class="col-lg-4 col-md-6"> | |||
<div class="user-target" id="{{user.name | slugify}}"></div> | |||
<a class="user" href="{{user.url}}"> | |||
<img src="{{site.baseurl}}/assets/users/{{user.image}}" alt="{{user.name}}"> | |||
</a> | |||
</div> | |||
{% endfor %} | |||
</div> | |||
<div class="card"> | |||
<div class="card-block"> | |||
{{i18n.users_add_prompt}} | |||
<a href="https://github.com/yarnpkg/website">{{i18n.users_add_pr}}</a> | |||
<br> | |||
<small> | |||
{{i18n.users_add_requirements_image}} | |||
<br class="hidden-md-down"> | |||
{{i18n.users_add_requirements_logo}} | |||
</small> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,27 @@ | |||
--- | |||
layout: default | |||
--- | |||
<div class="hero"> | |||
<div class="container"> | |||
<h1 class="hero-text display-4 text-md-center">{{page.title}}</h1> | |||
</div> | |||
</div> | |||
<div class="container"> | |||
<div class="row"> | |||
<div class="col-md-8 offset-md-2 blog-content"> | |||
<p class="text-muted text-md-center"> | |||
Posted {{page.date | date: "%b %-d, %Y"}} by | |||
{% if page.author_url %} | |||
<a href="{{page.author_url}}">{{page.author}}</a> | |||
{% else %} | |||
{{page.author}} | |||
{% endif %} | |||
{% if page.meta %} · {{page.meta}}{% endif %} | |||
</p> | |||
{{content}} | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,25 @@ | |||
# source: https://github.com/michaelx/jekyll_file_exists/blob/dd363223754836a640de81ddab68fd95cebe7791/file_exists.rb | |||
module Jekyll | |||
class FileExistsTag < Liquid::Tag | |||
def initialize(tag_name, path, tokens) | |||
super | |||
@path = path | |||
end | |||
def render(context) | |||
# Pipe parameter through Liquid to make additional replacements possible | |||
url = Liquid::Template.parse(@path).render context | |||
# Adds the site source, so that it also works with a custom one | |||
site_source = context.registers[:site].config['source'] | |||
dir_name = File.dirname(context.environments.first["page"]["path"]) | |||
file_path = File.join(site_source, dir_name, url.strip) | |||
# Check if file exists (returns true or false) | |||
"#{File.exist?(file_path)}" | |||
end | |||
end | |||
end | |||
Liquid::Template.register_tag('file_exists', Jekyll::FileExistsTag) |
@@ -0,0 +1,16 @@ | |||
--- | |||
layout : post | |||
title : "Yarn: A new package manager for JavaScript" | |||
author : Sebastian McKenzie, Christoph Pojer, James Kyle | |||
author_url : "https://twitter.com/yarnpkg" | |||
date : 2016-10-11 8:00:00 | |||
categories : announcements | |||
share_text : "Yarn: A new package manager for JavaScript" | |||
--- | |||
We're pleased to announce the open source release of Yarn, a collaboration between [Facebook][], [Exponent][], [Google][], and [Tilde][]. With Yarn, engineers still have access to the npm registry, but can install packages more quickly and manage dependencies consistently across machines or in secure offline environments. Yarn enables engineers to move faster and with confidence when using shared code so they can focus on what matters — building new products and features. Read the full announcement on [code.facebook.com](https://code.facebook.com/posts/1840075619545360). | |||
[facebook]: https://www.facebook.com/facebook/about/ | |||
[exponent]: https://expo.io/about | |||
[google]: https://about.google/ | |||
[tilde]: https://www.tilde.io/about-us/ |
@@ -0,0 +1,280 @@ | |||
--- | |||
layout : post | |||
title : "Running Yarn offline" | |||
author : Konstantin Raev | |||
author_url : "https://github.com/bestander" | |||
date : 2016-11-24 8:00:00 | |||
categories : announcements | |||
share_text : "installing node_modules with Yarn offline" | |||
--- | |||
Repeatable and reliable builds for large JavaScript projects are vital. | |||
If your builds depend on dependencies being downloaded from network, this build system is neither repeatable nor reliable. | |||
One of the main advantages of Yarn is that it can install node_modules from files located in file system. | |||
We call it “Offline Mirror” because it mirrors the files downloaded from registry during the first build and stores them locally for future builds. | |||
“Offline mirror” is different from cache that both npm CLI and Yarn have. | |||
Caches store already unzipped tarballs downloaded from registry, they also can be implementation specific and may be invalid between multiple versions of CLI tools. | |||
The tarballs in “Offline mirror” can be consumed by any version Yarn that will build cache based on them. | |||
It is also easier to store files when they are compressed. | |||
## Let's set up “Offline mirror” for a simple JS project | |||
```json | |||
{ | |||
"name": "yarn-offline", | |||
"version": "1.0.0", | |||
"main": "index.js", | |||
"license": "MIT", | |||
"dependencies": { | |||
"is-array": "^1.0.1", | |||
"left-pad": "^1.1.3", | |||
"mime-types": "^2.1.13" | |||
} | |||
} | |||
``` | |||
When you run `yarn install`, the generated `yarn.lock` file has sections for every dependency: | |||
```yaml | |||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. | |||
# yarn lockfile v1 | |||
is-array@^1.0.1: | |||
version "1.0.1" | |||
resolved "https://registry.yarnpkg.com/is-array/-/is-array-1.0.1.tgz#e9850cc2cc860c3bc0977e84ccf0dd464584279a" | |||
left-pad@^1.1.3: | |||
version "1.1.3" | |||
resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.1.3.tgz#612f61c033f3a9e08e939f1caebeea41b6f3199a" | |||
mime-db@~1.25.0: | |||
version "1.25.0" | |||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.25.0.tgz#c18dbd7c73a5dbf6f44a024dc0d165a1e7b1c392" | |||
mime-types@^2.1.13: | |||
version "2.1.13" | |||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.13.tgz#e07aaa9c6c6b9a7ca3012c69003ad25a39e92a88" | |||
dependencies: | |||
mime-db "~1.25.0" | |||
``` | |||
Each of these dependencies have a `resolved` field with a remote URL. If you delete your `node_modules` folder and run `yarn install` again, Yarn will download the same resolved dependencies specified in this lockfile. | |||
It will even guarantee that no one modified the files since your first installs by verifying checksum for each of them. | |||
However, if for some reason these urls are unreachable during your build, it will fail. To solve this, we'll need an “Offline mirror”. | |||
### Set up .yarnrc | |||
First we need to setup a directory to be our “Offline mirror” storage, we can do that with `yarn config` command: | |||
```bash | |||
$ yarn config set yarn-offline-mirror ./npm-packages-offline-cache | |||
yarn config v0.23.2 | |||
success Set "yarn-offline-mirror" to "./npm-packages-offline-cache". | |||
✨ Done in 0.06s. | |||
``` | |||
> `./npm-packages-offline-cache` is an example location relative to home folder where all the source`.tar.gz` files will be downloaded to from the registry. | |||
Offline mirror does not come with removing tarballs. In order to keep the cache folder up to date, you need to add the following to the config file: | |||
This feature is only available in version 0.23.0 and above. | |||
```bash | |||
$ yarn config set yarn-offline-mirror-pruning true | |||
yarn config v0.23.2 | |||
success Set "yarn-offline-mirror-pruning" to "true". | |||
✨ Done in 0.06s. | |||
``` | |||
This will create a .yarnrc file in your HOME directory. | |||
Let's move this file to the project root so that offline mirror would be used only for this project. | |||
```bash | |||
$ mv ~/.yarnrc ./ | |||
``` | |||
(In the unlikely event that `yarn config` didn't update the file in your home folder, e.g. if you are running `yarn config` as root inside a Docker container, the file being updated might be a different one. Use `yarn config list --verbose` to locate the proper file.) | |||
### Initialize the new lockfile | |||
Remove the node_modules that got generated previously and run yarn install again: | |||
```bash | |||
$ rm -rf node_modules/ yarn.lock | |||
$ yarn install | |||
yarn install v0.17.8 | |||
[1/4] 🔍 Resolving packages... | |||
[2/4] 🚚 Fetching packages... | |||
[3/4] 🔗 Linking dependencies... | |||
[4/4] 📃 Building fresh packages... | |||
success Saved lockfile. | |||
✨ Done in 0.57s. | |||
``` | |||
The dependency resolutions in your `yarn.lock` should look the same as the original (you shouldn't have any diff): | |||
```yaml | |||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. | |||
# yarn lockfile v1 | |||
is-array@^1.0.1: | |||
version "1.0.1" | |||
resolved "https://registry.yarnpkg.com/is-array/-/is-array-1.0.1.tgz#e9850cc2cc860c3bc0977e84ccf0dd464584279a" | |||
left-pad@^1.1.3: | |||
version "1.1.3" | |||
resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.1.3.tgz#612f61c033f3a9e08e939f1caebeea41b6f3199a" | |||
mime-db@~1.25.0: | |||
version "1.25.0" | |||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.25.0.tgz#c18dbd7c73a5dbf6f44a024dc0d165a1e7b1c392" | |||
mime-types@^2.1.13: | |||
version "2.1.13" | |||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.13.tgz#e07aaa9c6c6b9a7ca3012c69003ad25a39e92a88" | |||
dependencies: | |||
mime-db "~1.25.0" | |||
``` | |||
The offline cache file will be stored in the npm-packages-offline-cache folder that was configured earlier. Each resolved dependency also contains a checksum after the file name to ensure that no one mangles with the downloaded files. | |||
If we look inside the “Offline mirror” folder we'll see the .tgz files that yarn will use for the following builds without reaching out to network: | |||
```bash | |||
$ ls npm-packages-offline-cache/ | |||
is-array-1.0.1.tgz left-pad-1.1.3.tgz mime-db-1.25.0.tgz mime-types-2.1.13.tgz | |||
``` | |||
> How can you test to make sure it is offline? | |||
- Clear your global cache with "yarn cache clean" | |||
- Turn off wifi | |||
- Run "yarn install --offline". The offline flag will make sure yarn does not reach out to the network | |||
> In a nutshell, to enable “Offline mirror” for your project you need: | |||
- add “yarn-offline-mirror” configuration to .yarnrc file | |||
- generate a new yarn.lock with “yarn install” command | |||
## A few tips and tricks | |||
### Updating your package | |||
If you want to make sure you have a clean cached modules, here are few of the steps you can take: | |||
- Remove the package first. Make sure you have "yarn-offline-mirror-pruning" set to true in your .yarnrc file | |||
- Clear the yarn cache with "yarn cache clean" before adding the updated version of the package | |||
- Add your package | |||
The "yarn-offline-mirror-pruning" will help clean up any unlinked dependencies. When you add the updated package, it will check the yarn cache first and pull any missing dependencies from there. This will prevent yarn adding new tarball back with the updated package. You want to make sure the yarn cache is all clean before you do any adding for cache module. | |||
### You can check in “Offline mirror” into git or mercurial repository | |||
The “Offline Mirror” can be shared between build servers or development machines in any way that is convenient: a Box / Dropbox folder, stored in source control or on a network drive. At Facebook the offline mirror lives inside of our big Mercurial “monorepo”. | |||
Whether to commit binary files into a repository or not depends on the number and size of your project's dependencies. | |||
For example, out of 849 React Native dependencies totaling 23MB, only 10% are larger than 30KB. | |||
{:style="max-width: 700px"} | |||
{:style="max-width: 700px"} | |||
Many Facebook teams, including the React Native team, decided to check in their “Offline mirror”. | |||
They all share the same “Offline mirror” which means that most dependencies for new projects are often already checked into that folder, so the cost of storing the packages in source control gets lower the more projects use it. | |||
### Let's compare checking in node_modules to checking in “Offline mirror” | |||
The React Native team used to check in the `node_modules` folder but they hit several limits: | |||
- `node_modules` contains more than 37,000 files (and more than 100,000 files back when we were using npm2). This had a bad performance impact on our Mercurial repository. | |||
- Reviewing Pull Requests that changed a dependency was quite hard as all the files in `node_modules` that were added and removed created a ton of noise, making code reviews unpleasant | |||
In comparison, updating a third-party dependency with the Offline Mirror adds just a few files that are very easy to review: | |||
```bash | |||
$ yarn add shelljs@0.7.0 --dev | |||
yarn add v0.23.2 | |||
[1/4] 🔍 Resolving packages... | |||
[2/4] 🚚 Fetching packages... | |||
[3/4] 🔗 Linking dependencies... | |||
[4/4] 📃 Building fresh packages... | |||
success Saved lockfile. | |||
success Saved 4 new dependencies. | |||
├─ interpret@1.0.1 | |||
├─ rechoir@0.6.2 | |||
└─ shelljs@0.7.0 | |||
│ └─ glob@7.1.1 | |||
✨ Done in 8.15s. | |||
$ git diff | |||
diff --git a/package.json b/package.json | |||
index 4619f16..7acb42f 100644 | |||
--- a/package.json | |||
+++ b/package.json | |||
@@ -220,7 +220,7 @@ | |||
"mock-fs": "^3.11.0", | |||
"portfinder": "0.4.0", | |||
"react": "~15.3.1", | |||
- "shelljs": "0.6.0", | |||
+ "shelljs": "0.7.0", | |||
"sinon": "^2.0.0-pre.2" | |||
} | |||
} | |||
diff --git a/yarn.lock b/yarn.lock | |||
index 11ce116..f5d81ba 100644 | |||
--- a/yarn.lock | |||
+++ b/yarn.lock | |||
... | |||
-shelljs@0.6.0: | |||
- version "0.6.0" | |||
- resolved https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.0.tgz#ce1ed837b4b0e55b5ec3dab84251ab9dbdc0c7ec | |||
+shelljs@0.7.0: | |||
+ version "0.7.0" | |||
+ resolved https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.0.tgz#3f6f2e4965cec565f65ff3861d644f879281a576 | |||
+ dependencies: | |||
+ glob "^7.0.0" | |||
+ interpret "^1.0.0" | |||
+ rechoir "^0.6.2" | |||
shellwords@^0.1.0: | |||
version "0.1.0" | |||
$ git status | |||
On branch testing-yarn | |||
Changes not staged for commit: | |||
(use "git add <file>..." to update what will be committed) | |||
(use "git checkout -- <file>..." to discard changes in working directory) | |||
modified: package.json | |||
modified: yarn.lock | |||
Untracked files: | |||
(use "git add <file>..." to include in what will be committed) | |||
yarn-offline-mirror/interpret-1.0.1.tgz | |||
yarn-offline-mirror/rechoir-0.6.2.tgz | |||
yarn-offline-mirror/shelljs-0.7.0.tgz | |||
no changes added to commit (use "git add" and/or "git commit -a") | |||
``` | |||
### Did you know that Yarn is also distributed as a single bundle JS file in [releases](https://github.com/yarnpkg/yarn/releases) that can be used on CI systems without internet access? | |||
{:style="max-width: 700px"} | |||
**yarn-<version>.js** (for Node 5+) and **yarn-legacy-<version>.js** (for Node 4) can be used stand-alone in CI systems without a need to install it. | |||
Just check it into your project's repository and use it in the build script: | |||
```bash | |||
node ./yarn-0.23.2.js install | |||
``` | |||
This is quite convenient for teams that use multiple operating systems and want to have atomic updates for Yarn. | |||
### This is going to get better | |||
The “Offline mirror” was implemented early in Yarn's development cycle and we are working on improving it in a backwards compatible way: | |||
- The `resolved` field is used both for offline mirror paths and registry URIs. This means that the `yarn.lock` file that React Native team uses internally can't be shared with the open source community because the React Native team does not sync the offline mirror with the open source version of React Native. [Issue](https://github.com/yarnpkg/yarn/issues/394). | |||
- There is an [improved workflow being considered](https://github.com/yarnpkg/yarn/issues/393) for future versions of Yarn. It is not drastically different but some settings and lock files may change. |
@@ -0,0 +1,274 @@ | |||
--- | |||
layout : post | |||
title : "Lockfiles should be committed on all projects" | |||
author : James Kyle | |||
author_url : "https://twitter.com/thejameskyle" | |||
date : 2016-11-24 8:00:00 | |||
categories : announcements | |||
share_text : "Yarn: Lockfiles should be committed on all projects @yarnpkg" | |||
--- | |||
Yarn is a new package manager that we built to be consistent and reliable. When | |||
installing hundreds or even thousands of third-party packages from the internet | |||
you want to be sure that you're executing the same code across every system. | |||
Yarn maintains consistency across machines in two key ways: | |||
- Yarn uses a deterministic algorithm that builds up the entire dependency tree | |||
before placing files where they need to be. | |||
- Important info from the install process is stored in the `yarn.lock` lockfile | |||
so that it can be shared between every system installing the dependencies. | |||
This lockfile contains information about the exact versions of every single | |||
dependency that was installed as well as checksums of the code to make sure the | |||
code is identical. | |||
Yarn needs this info about every dependency because packages are constantly | |||
changing. New versions of packages are published all the time and since | |||
`package.json` specifies version ranges you need to lock them down to a single | |||
version. | |||
These version ranges exist because Yarn follows | |||
[Semantic Versioning](http://semver.org/), or "SemVer". SemVer is a versioning | |||
system designed around "breaking" or "non-breaking" changes. | |||
When you have a version such as `v1.2.3`, it's broken into three parts: | |||
- **Major (1.x.x)** – _Changes that may cause user code to break_ | |||
- **Minor (x.2.x)** – _Changes that add new features (but should not break user | |||
code)_ | |||
- **Patch (x.x.3)** – _Changes that are fixing bugs in previous versions (but | |||
do not add new features and should not break user code)_ | |||
When a package publishes a new version, the author bumps major, minor, or patch | |||
based on the changes that they have made as a way to communicate them. | |||
Users of packages should typically welcome minor and patch versions but should | |||
be wary of major versions as they could break your code. | |||
Version ranges are a way of specifying which types of changes you want to | |||
accept and which versions you want to prevent. These version ranges then | |||
resolve down to a single version which is either the version you have installed | |||
or the latest published version that matches your version range. | |||
If you don't store which version you ended up installing, someone could be | |||
installing the same set of dependencies and end up with different versions | |||
depending on when they installed. This can lead to "Works On My Machine" | |||
problems and should be avoided. | |||
Also, since package authors are people and they can make mistake, it's possible | |||
for them to publish an accidental breaking change in a minor or patch version. | |||
If you install this breaking change when you don't intend to it could have bad | |||
consequences like breaking your app in production. | |||
Lockfiles _lock_ the versions for every single dependency you have installed. | |||
This prevents "Works On My Machine" problems, and ensures that you don't | |||
accidentally get a bad dependency. | |||
It also works as a security precaution: If the package author is either | |||
malicious or is attacked by someone malicious and a bad version is published, | |||
you do not want that code to end up running without you knowing about it. | |||
## Libraries vs Applications | |||
There are two primary types of projects that use Yarn: | |||
- **Libraries** – _Projects that get published as packages to the registry and | |||
installed by users. (e.g. React or Babel)_ | |||
- **Applications** – _Projects that only consume other packages, typically | |||
building some kind of product. (e.g. Your company's app)_ | |||
For applications, most developers agree that lockfiles are A Good Idea™. | |||
But there has been some question about using them when building libraries. | |||
When you publish a package that contains a `yarn.lock`, any user of that | |||
library will not be affected by it. When you install dependencies in your | |||
application or library, only your own `yarn.lock` file is respected. Lockfiles | |||
within your dependencies will be ignored. | |||
It is important that Yarn behaves this way for two reasons: | |||
- You would never be able to update the versions of sub-dependencies because | |||
they would be locked by other `yarn.lock` files. | |||
- Yarn would never be able to fold (de-duplicate) dependencies so that | |||
compatible version ranges only install a single version. | |||
Some have wondered why libraries should use lockfiles at all if they do not and | |||
should not affect users. Even further, some have said that using lockfiles when | |||
developing libraries creates a _false sense of security_ since your users could | |||
be installing different versions than you. | |||
This seems to logically makes sense, but let's dive deeper into the problem. | |||
## Development Dependencies | |||
So far we've been talking about dependencies as if there were only one type of | |||
dependency when in fact there are several different types. These are broken | |||
down into two categories: | |||
- **Runtime** – _Dependencies that are used by the project's code and needed | |||
when the code is run._ | |||
- **Development** – _Dependencies that are only needed to work directly on the | |||
project_ | |||
When a library is installed by a user, only the runtime dependencies are | |||
installed. The development dependencies are only ever installed when working | |||
directly on the project that specifies them. | |||
Each type of dependency creates a whole tree of dependencies which are needed | |||
for them to be used. | |||
It turns out that most projects (libraries or applications) have far more | |||
dependencies for development than for runtime. The tree of dependencies created | |||
by a projects development dependencies is almost always the largest part of the | |||
total. | |||
You can blame this on how frustratingly complicated JavaScript development is, | |||
but it seems to hold true across every ecosystem. You almost always need more | |||
code to develop projects than you need to run them. | |||
When working on a library, it is far more likely that a development dependency | |||
breaks just because there are more of them that _could_ break. | |||
## The Breaking Change Race | |||
When a package accidentally publishes a breaking change it starts the clock on | |||
who will be the first person to catch it. Whoever installs that breaking change | |||
first will (most likely) be the first to discover it. | |||
Let's _imagine_ we have a package called `left-pad` which takes a string and | |||
adds a specified amount of padding in front of it. This small and seemingly | |||
harmless package is used by a really big project, which we'll just call... | |||
Babel. | |||
One day, the maintainer of `left-pad` decides they are going to do something | |||
unspeakably evil and make it pad the right side instead. They publish it as a | |||
patch version which quickly spreads to everyone using it. | |||
Remember, Babel is a really huge project with tens of thousands of users and | |||
dozens of contributors. Let's try and figure out who will catch the padding | |||
fiasco first. | |||
- Looking at build history, Babel was installed in CI (with the `left-pad` | |||
dependency) exactly **103 times** in the last 30 days. | |||
- Looking at statistics from the registry, Babel was installed (with | |||
`left-pad`) by users over **5.5 Million times** in the same 30 days. | |||
To put that in a different measurement: | |||
- Contributors install Babel on average **every 7 hours** | |||
- Users install Babel about on average **every 0.5 seconds** | |||
When the `left-pad` incident happened, users discovered it almost immediately. | |||
Notifying the Babel contributors via GitHub issues, tweets, Facebook messages, | |||
emails, phone calls, smoke signals, and by carrier pigeon. | |||
Babel contributors couldn't possibly prevent users from being affected by every | |||
error. In order to do so, every 0.5 seconds they would need to be able to run | |||
CI, notice the error, find a fix, publish a new version, and get every user to | |||
upgrade. There's just no way to make this happen. | |||
This might seem like a bit too extreme of an example to apply broadly, but the | |||
reasoning stays the same just with different numbers. Libraries should always | |||
be installed more frequently by users than by contributors. If that isn't true, | |||
it's because no one uses that library anyways– and we should not be optimizing | |||
our workflow around code that no one uses. | |||
## User testing | |||
Many library developers work very hard to maintain a test suite that has 100% | |||
code coverage. This coverage ensures that every single line of code is run at | |||
least once during the tests. | |||
But it still does not catch everything. If a library has even a small number of | |||
users, it will be far better tested by the users than it will be by any test | |||
suite the contributors can come up with. | |||
Users just write more code, and the code they write is not always what a | |||
library author will expect. The more popular the library the more edge cases | |||
users will find. Users will do things that you didn't even think was possible– | |||
it will _horrify_ you. It will make you question the goodness of humanity. | |||
Even if a contributor happened to beat users to finding a breaking change, | |||
there's a pretty decent chance they won't catch it anyways. Untested edge cases | |||
are the most likely things to break. | |||
## The contributor's burden | |||
Now let's talk a bit about the social side of not using lockfiles in libraries. | |||
As mentioned previously, the majority of breaking changes that occur in library | |||
dependencies will be development dependencies. | |||
Some percentage of these breaking changes will be caught and (hopefully) fixed | |||
by regular contributors. However, the remaining breaking changes will be caught | |||
by new or infrequent contributors. | |||
Contributing to a project for the first time is a very intimidating experience | |||
for anyone who is not an open source veteran. If the first thing a potential | |||
new contributor runs into is a broken build or test suite, they could be so | |||
intimidated that they decide to forget about contributing back. | |||
Even as someone who contributes to lots of open source projects, it's a | |||
terrible thing to have to debug a build system for a few hours when you just | |||
want to fix a bug or add a small feature to a library. | |||
## The user's burden | |||
Of course not every breaking change is a development dependency, and | |||
theoretically contributors _could_ catch breaking changes before users notice | |||
them. In that (narrow) scenario, aren't we shifting the burden from | |||
contributors to users? | |||
First, it's not going to be every user that is affected by this. It's well | |||
agreed upon that applications should be using lockfiles, and if they are then | |||
they won't be affected by sudden breaking changes. | |||
We're only talking about users that are either installing a package for the | |||
first time, or are upgrading the versions of their dependencies. | |||
For users upgrading their dependencies, they should be trained to look out for | |||
breaking changes and if they encounter one, to roll back the version to a | |||
working state and open an issue in the library. | |||
The only really negative experience that we are adding is for new users, which | |||
is admittedly terrible. You want new users to have the best possible | |||
experience. | |||
But remember that they already face this burden in the majority of cases. It's | |||
only when contributors could have caught breaking changes before users | |||
experienced them that we are now placing an additional burden on new users. | |||
It's also important to note that new users make up a minority of the total | |||
installations. The majority of the time it will be existing users that are the | |||
first to catch breaking changes because they are the ones installing a library | |||
the most. | |||
## In Closing | |||
There is a simple universal rule that everyone should follow with Yarn: If you | |||
are installing a new dependency or upgrading an existing one you should check | |||
to make sure the package works as intended and is not breaking your code. | |||
You should follow that rule regardless of what your libraries are doing. | |||
Without lockfiles it gets even more complicated: In applications or libraries, | |||
if there is no lockfile, you will have to check the dependencies every time you | |||
install or re-install them and make sure that everything still works. Otherwise | |||
the build might be broken or the tests might fail. You could break something | |||
without even realizing it. You could run into situations where code works on | |||
your machine but no one else's. | |||
The idea that not using lockfiles in libraries somehow saves users from | |||
encountering breaking changes is at best extremely rare and at worst is never | |||
true. | |||
By not using lockfiles in libraries, the only tangible thing you accomplish is | |||
making the project harder to contribute to. | |||
We should work as a community to keep all of our libraries up to date. We | |||
should go out and build tooling for automatically upgrading dependencies that | |||
makes it painless to do. There has been attempts at such tooling before, but we | |||
can do better. | |||
Please commit your `yarn.lock` files. |
@@ -0,0 +1,45 @@ | |||
--- | |||
layout : post | |||
title : "Cloudflare security incident and impact on Yarn users" | |||
author : Sebastian McKenzie | |||
author_url : "https://twitter.com/sebmck" | |||
date : 2017-02-24 14:00:00 | |||
categories : announcements | |||
share_text : "Yarn statement on Cloudflare security incident" | |||
--- | |||
Yarn uses its own proxy to the npm registry in order to allow us to experiment | |||
with the way the Yarn client works and allow optimizations in the future around | |||
how packages are resolved. This registry is used by all Yarn users by default. | |||
In order to do this we use the popular service, Cloudflare, which is used by | |||
thousands of companies and who had offered to work with us to make Yarn installs | |||
faster globally. | |||
Recently it was [reported](https://blog.cloudflare.com/incident-report-on-memory-leak-caused-by-cloudflare-parser-bug/) | |||
that Cloudflare had a serious bug that was leading to requests from other websites | |||
being leaked into HTTP responses. | |||
When it comes to registry authentication, the Yarn client differs from the npm | |||
client in that when we perform authentication we do not store the resulting token | |||
and invalidate it after it's used. | |||
However, Yarn still allows you to login with your npm account to perform actions | |||
such as publishing and downloading private packages. Out of the 70 million requests | |||
performed daily we only get 10-30 requests that involve registry authentication. | |||
This means that for these requests there was the possibility of user passwords | |||
being leaked. | |||
**Since the Cloudflare announcement we've been in contact and have been assured | |||
that Yarn has not been affected and no Yarn users data has been leaked. Even with | |||
this assurance we'd recommend that if you're one of those 30 people a day using Yarn | |||
for registry authentication that you reset your password as a precautionary measure.** | |||
As a result of this we're evaluating our security policy and have created a new email | |||
address [security@yarnpkg.com](mailto:security@yarnpkg.com) that can be used to report | |||
security vulnerabilities without going through the public issue tracker. We're also in | |||
the process of setting up a HackerOne account and will make an announcement when this | |||
is available. | |||
We'd like to apologize for this disruption and want to reaffirm our commitment to security | |||
and transparency in cases like these. |
@@ -0,0 +1,45 @@ | |||
--- | |||
layout : post | |||
title : "Yarn Create & Yarn 1.0" | |||
author : Maël Nison | |||
author_url : "https://twitter.com/arcanis" | |||
date : 2017-05-12 8:00:00 | |||
categories : announcements | |||
share_text : "Yarn Create & Yarn 1.0" | |||
--- | |||
Last year was a great time for Javascript newcomers! A lot of starter-kit projects were published, refined, and some of them eventually went on to offer command line tools dedicated to make project creation easier. One such example is [create-react-app](https://github.com/facebookincubator/create-react-app), but most frameworks have their own tools, with various flavors and syntaxes. | |||
Despite these tools, one problem remains: Users still need to know how to use their package managers before being able to start a new project. They need to know what's the difference between global packages and local packages, and how to make sure that the binaries are available from the shell, which can sometimes cause subtle issues. Further, because these globally installed tools need to be manually updated, most projects maintain a small cli wrapper that downloads the latest version of the tool itself. Fortunately, we're in a position where we can help with this to make building new applications more cohesive: | |||
### `yarn create <pkg-name>` | |||
With `yarn create`, you can start building apps with many of the existing projects: | |||
- `yarn create react-app my-app` | |||
- `yarn create react-native-app my-app` | |||
- `yarn create next-app my-app` | |||
When ran, the create command will automatically install or update the requested package, prefixing its name with `create-`. Running `yarn create react-app` will start by doing the same thing as `yarn global add create-react-app`. Then, once the package installed, Yarn will run the executable located in the `bin` field of the newly installed package's `package.json`, forwarding to it any remaining command line argument. | |||
It is important to us to keep the feature small and extensible. Yarn should be a lightweight tool and `yarn create` is no exception: An immediate implication is that the create command is a completely agnostic tool: we make no assumption regarding what you want to create, and delegate all the behavior to the `create-*` packages! It is our hope that the community will come up with creative way to use this tool. Creating apps is but only one thing! Feel free to make packages that create tests, readmes, changelogs or anything else you want! | |||
_Note: The `create-` prefix is inserted right before the package name. So, for example, if you run yarn create `@ng/app`, it will install the `@ng/create-app` package, then run it._ | |||
### Other Improvements | |||
`yarn create` is but one of the many things we have been working on over the last couple of weeks. Thanks to numerous pull requests from many open source contributors, the recent releases also ship with the following features & improvements: | |||
- The offline mirror does not require changes to the yarn lockfile any longer ([#2970](https://github.com/yarnpkg/yarn/pull/2970)) | |||
- Command-line arguments and environment variables can now be set in the yarnrc file ([#3033](https://github.com/yarnpkg/yarn/pull/3033), [#3218](https://github.com/yarnpkg/yarn/pull/3218)) | |||
- Prepare & prepublish-only lifecycle hooks are now implemented ([#3004](https://github.com/yarnpkg/yarn/pull/3004)) | |||
- The offline mirror can be pruned if used by a single one of your projects ([#2836](https://github.com/yarnpkg/yarn/pull/2836)) | |||
- Various improvements for yarn pack ([#3175](https://github.com/yarnpkg/yarn/pull/3175), [#3092](https://github.com/yarnpkg/yarn/pull/3092)) | |||
The list of all improvements and bugfixes over the last couple of months can be found in our [releases section on GitHub](https://github.com/yarnpkg/yarn/releases). We would specifically like to thank a team from the Delft University of Technology: [Tim van der Lippe](https://github.com/timvdlippe), [Chris Langhout](https://github.com/clanghout), [Gijs Weterings](https://github.com/gijsweterings) and [Chak Shun Yu](https://github.com/keraito). The four of them did a fantastic [analysis of the Yarn project](https://delftswa.gitbooks.io/desosa-2017/content/yarn/chapter.html) and sent pull requests to improve it in many areas. They also pointed out gaps in our test coverage, which our new core contributor [Simon Vocella](https://github.com/voxsim) has been working on improving. | |||
### Planning for Yarn 1.0 | |||
Yarn has made substantial improvements since its initial release 7 months ago and the project recently surpassed 1,000 Pull Requests. Currently, we are planning for the 1.0 release of Yarn which is scheduled for this summer and will come with stability improvements, new features and performance wins. To hear more about Yarn's present and future, please watch Konstantin's talk about [Building High-Quality JavaScript Tools](https://developers.facebook.com/videos/f8-2017/building-high-quality-javascript-tools/). | |||
We thank each and every one of you for your help to make this project great. If you'd like to contribute to Yarn, please don't hesitate to reach out to us on [GitHub](https://github.com/yarnpkg/yarn) or on [Discord](https://discordapp.com/invite/yarnpkg). |
@@ -0,0 +1,93 @@ | |||
--- | |||
layout : post | |||
title : "Yarn determinism" | |||
author : Sebastian McKenzie | |||
author_url : "https://twitter.com/sebmck" | |||
date : 2017-05-31 09:00:00 | |||
categories : announcements | |||
share_text : "What does Yarn determinism actually mean?" | |||
--- | |||
One of the claims that Yarn makes is that it makes your package management “deterministic”. But what exactly does this mean? This blog post highlights how both Yarn and npm 5 are deterministic, but differ in the exact guarantees they provide and the tradeoffs they have chosen. | |||
## What is determinism? | |||
Determinism in the context of JavaScript package management is defined as always getting the exact same `node_modules` folder given a `package.json` and companion lock file. | |||
## What factors does Yarn’s determinism guarantee? | |||
### Lockfile | |||
Yarn is fully deterministic as long as all your teammates are using the same Yarn version. In both Yarn and npm 5, the determinism is ensured by lockfiles that contain information about the whole tree. However the lockfile formats are different between these two projects. We haven't publicly talked about why we chose this format, so we want to walk you through it: | |||
If you’ve ever seen a `yarn.lock` then you should be pretty familiar with the following structure: | |||
``` | |||
has-flag@^1.0.0: | |||
version "1.0.0" | |||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" | |||
supports-color@^3.2.3: | |||
version "3.2.3" | |||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" | |||
dependencies: | |||
has-flag "^1.0.0" | |||
``` | |||
This is the `yarn.lock` file generated by running the command `yarn add supports-color`. This file contains the version of supports-color that our project is using as well as the exact version of all its sub-dependencies. | |||
With this lock file we can ensure that the version of `has-flag` that `supports-color` relies on is always the same version. | |||
But there’s one key piece of information that the yarn lockfile doesn’t contain and that’s the hoisting and location of each dependency in the tree. For example, given a `yarn.lock` it’s impossible to determine what are the top level dependencies unless you have it’s accompanying `package.json`. Even knowing the top level packages, we still cannot infer what hoisting position packages should be in. | |||
In practice this means that the position of packages in `node_modules` is computed internally in Yarn, which causes Yarn to be non-deterministic between people using different versions. | |||
The reason that we do this is that this lockfile format is great for diffing. That is, changes to the lockfile can easily be human reviewed. The reason we use a custom format instead of JSON and have everything at the top level is so that it’s easy to read and review. Merge conflicts are usually automatically handled by version control and it reduces thrash. | |||
### Hoisting guarantees | |||
Even though Yarn hoisting may differ between versions we still make very strong guarantees around hoisting when the same version of Yarn is used. The most significant of these guarantees is that omitting environmental dependencies like `optionalDependencies` and `devDependencies` still influences the position of normal `dependencies`. | |||
Woah there, that’s a lot of dependency mumbo jumbo. What does that actually mean? | |||
There are several types of dependencies that you can declare in your `package.json`. Two of these are plain `dependencies` which are installed all the time, and there are `devDependencies` which are only installed when you run `npm install` or `yarn` within the directory where the `package.json` file is present. | |||
Due to these features it’s possible to have different layouts of `node_modules` with omitted dependencies. But Yarn still factors all dependencies into account when determining the position that they should be at in `node_modules`, so even if they aren’t installed, they still influence the hoisting position of others. This is important as having variance in hoisting position of packages in production and development can cause really weird obscure bugs. | |||
**NOTE**: This guarantee isn’t unique to Yarn and npm 5 also does this. | |||
## How does this compare to npm 5? | |||
npm 5 introduces a rework of the shrinkwrap feature called package-lock. This file includes all the information required to create `node_modules` as well as all hoisting information. The npm version of the previous `yarn.lock` would be: | |||
``` | |||
{ | |||
"name": "react-example", | |||
"version": "1.0.0", | |||
"lockfileVersion": 1, | |||
"dependencies": { | |||
"has-flag": { | |||
"version": "1.0.0", | |||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", | |||
"integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" | |||
}, | |||
"supports-color": { | |||
"version": "3.2.3", | |||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", | |||
"integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" | |||
} | |||
} | |||
} | |||
``` | |||
Note that here all the packages listed in the first dependencies object are hoisted. This means that npm can use only the package-lock as the source of truth in order to build the final dependency graph whereas Yarn needs the accompanying`package.json` to seed it. | |||
This means that npm has better assurances around hoisting position across npm versions at the cost of having a more dense lockfile. There’s currently no plan on how to support `package-lock.json` in Yarn as the story around lockfile interoperability is unclear. You could however imagine a future where Yarn supports both and updates them in tandem. We’re very interested in community feedback and encourage proposals for how this would work to be submitted as an [RFC](https://github.com/yarnpkg/rfcs). | |||
## Closing remarks | |||
Each lock file format has different tradeoffs and there doesn’t appear to be a perfect format without disadvantages. It’s important to evaluate what sort of guarantees you’re looking for when deciding what package manager to use. | |||
npm 5 has stronger guarantees across versions and has a stronger deterministic lockfile, but Yarn only has those guarantees when you’re on the same version in favor of a lighter lockfile that is better for review. It’s possible that there’s a lockfile solution that has the best of both worlds, but for now this is current state of the ecosystem and possible convergence could happen in the future. | |||
Hopefully this post has highlighted the determinism guarantees that differ between Yarn and npm and helped you decide what works better for your company or project. |
@@ -0,0 +1,45 @@ | |||
--- | |||
layout : post | |||
title : "Private Registry Support" | |||
author : Lukas Spieß | |||
author_url : "https://twitter.com/lumaxis" | |||
date : 2017-06-16 12:00:00 | |||
categories : announcements | |||
share_text : "Yarn now supports even more private registries" | |||
--- | |||
Today, Yarn already supports a wide variety of different package feeds when fetching and downloading your dependencies. Up until now, there was however a small subset of public and private package feed providers that Yarn could not yet handle very well. One example of these package feed providers that were not yet supported was [Visual Studio Team Services](https://www.visualstudio.com/team-services/) (VSTS). | |||
Let's explain why: | |||
### Registry and package location URL differences | |||
Some registries, such as VSTS, use two slightly different URL structures for the location of the package feed and the location of the actual package archive binary itself. | |||
For example, a private feed's URL would look like this: | |||
``` | |||
// Package feed URL | |||
https://$ACCOUNT_NAME.pkgs.visualstudio.com/_packaging/$FEED_NAME/npm/registry | |||
``` | |||
but the URLs to fetch the actual tar archives would then be returned by the feed in this format: | |||
``` | |||
// Package archive URL | |||
https://$ACCOUNT_NAME.pkgs.visualstudio.com/_packaging/da6e033f-20ad-4ee1-a784-8995dd6836b72/npm/registry/@scope/package-name/-/package-name-0.0.1.tgz | |||
``` | |||
As you can see, the archive's URL does not contain the actual feed name anymore but rather a random GUID, followed by the package name and version. This differing layout of the path part of the URL lead to Yarn not recognizing that the two URLs actually both do belong to a request to the same registry and would therefore refuse to download the package. | |||
### Introducing custom host suffixes | |||
Starting with version `0.26.0`, Yarn now understands a new configuration option called `custom-host-suffix`. This allows you to keep the same strict URL validations for most of your package URLs but also selectively loosen that check for a specific registry provider so that Yarn will now match the URLs where the host part ends with the value from this new option. | |||
Simply add `custom-host-suffix` to either your global user-level `.npmrc` or your project's individual `.npmrc` and Yarn will be able to download your packages as desired. | |||
In the above example of Visual Studio Team Services, the `.npmrc` should contain an entry like this: | |||
``` | |||
custom-host-suffix='pkgs.visualstudio.com' | |||
``` | |||
We hope you will find this new option useful and are happy that Yarn can now be used in even more projects! |
@@ -0,0 +1,148 @@ | |||
--- | |||
layout : post | |||
title : "Adding Command Line Aliases for Yarn" | |||
author : G. Kay Lee | |||
author_url : "https://github.com/gsklee" | |||
date : 2017-06-19 00:00:00 | |||
categories : announcements | |||
share_text : "Adding Command Line Aliases for Yarn" | |||
--- | |||
One of the core design philosophies of Yarn is to strive for simpleness; a lean CLI without redundant features. That’s why Yarn has resisted adding random built-in shorthands like `npm r` or an aliases system like the one you can find in Git. We believe that the benefits they could possibly bring to the Yarn experience are not justified by the cost required to build and maintain such a full-fledged subsystem. | |||
We’ve also noticed, however, that it is among one of the most common feature requests we received from the community. People do use aliases for several reasons, for example, to replicate their experiences from the `npm` command. The good news is, all modern shell environments actually support command aliases in one form or another, and we encourage you to improve your CLI experience using these ways that are baked into your favorite shell already. | |||
Let’s say you check for package distribution tags information pretty often, are a report message addict as well as an emoji hater, and you’d like to have a handy shorthand for this common task. Below we’ve compiled a list of ways to add the command alias of `yarn info --verbose --no-emoji <package> dist-tags` in a number of popular shells for your convenience: | |||
## Bash & Zsh | |||
[Bash](<https://en.wikipedia.org/wiki/Bash_(Unix_shell)>) is the default shell on most Unix-like systems; together with [Zsh](https://en.wikipedia.org/wiki/Z_shell), they both descended from the earlier [Bourne shell](https://en.wikipedia.org/wiki/Bourne_shell) and hence syntaxes are largely compatible. To add a simple alias in either Bash or Zsh, simply have the following line added into your `.bashrc` or `.zshrc`, respectively: | |||
```sh | |||
alias ynf="yarn info --verbose --no-emoji" | |||
``` | |||
Restart your shell and now you’ll be able to do: | |||
```sh | |||
ynf react dist-tags | |||
``` | |||
``` | |||
yarn info v0.24.6 | |||
verbose 0.261 Checking for configuration file "/Users/gsklee/.npmrc". | |||
verbose 0.262 Checking for configuration file "/Users/gsklee/.npmrc". | |||
verbose 0.262 Checking for configuration file "/Users/gsklee/.nvm/versions/node/v8.1.2/.npmrc". | |||
verbose 0.263 Checking for configuration file "/Users/gsklee/.npmrc". | |||
verbose 0.263 Checking for configuration file "/Users/.npmrc". | |||
verbose 0.265 Checking for configuration file "/Users/gsklee/.yarnrc". | |||
verbose 0.265 Found configuration file "/Users/gsklee/.yarnrc". | |||
verbose 0.267 Checking for configuration file "/Users/gsklee/.yarnrc". | |||
verbose 0.267 Found configuration file "/Users/gsklee/.yarnrc". | |||
verbose 0.268 Checking for configuration file "/Users/gsklee/.nvm/versions/node/v8.1.2/.yarnrc". | |||
verbose 0.268 Checking for configuration file "/Users/gsklee/.yarnrc". | |||
verbose 0.268 Found configuration file "/Users/gsklee/.yarnrc". | |||
verbose 0.27 Checking for configuration file "/Users/.yarnrc". | |||
verbose 0.274 current time: 2017-06-16T09:43:50.256Z | |||
verbose 0.339 Performing "GET" request to "https://registry.yarnpkg.com/react". | |||
verbose 0.488 Request "https://registry.yarnpkg.com/react" finished with status code 200. | |||
{ latest: '15.6.1', | |||
'0.10.0-rc1': '0.10.0-rc1', | |||
'0.11.0-rc1': '0.11.0-rc1', | |||
next: '16.0.0-alpha.13', | |||
dev: '15.5.0-rc.2', | |||
'0.14-stable': '0.14.9', | |||
'15-next': '15.6.0-rc.1' } | |||
Done in 0.28s. | |||
``` | |||
Now, if you’d like to further alias the `dist-tags` part as well, you’ll need to use a function instead because Bash/Zsh aliases do not accept additional parameters: | |||
```sh | |||
function ynftag { yarn info --verbose --no-emoji "$@" dist-tags; } | |||
``` | |||
You’ll then be able to get the same output by simply typing: | |||
```sh | |||
ynftag react | |||
``` | |||
## Fish | |||
[Fish](https://en.wikipedia.org/wiki/Friendly_interactive_shell) is a newer “[exotic shell](https://en.wikipedia.org/wiki/Unix_shell#Exotic_shells)” that deviates from traditional shell designs. It offers “abbreviations” that expand into full commands live as you type, much like the so-called snippets in modern code editors. To add an abbreviation: | |||
```sh | |||
abbr --add ynf yarn info --verbose --no-emoji | |||
``` | |||
When it comes to passing in additional arguments, however, you have to use functions just like in Bash and Zsh: | |||
```sh | |||
function ynftag --wraps yarn --description "yarn info --verbose --no-emoji <package> dist-tags" | |||
yarn info --verbose --no-emoji $argv dist-tags | |||
end | |||
``` | |||
To persist your alias command definition, save it to your autoload directory: | |||
```sh | |||
funcsave ynftag | |||
``` | |||
## Windows PowerShell | |||
[PowerShell](https://en.wikipedia.org/wiki/PowerShell) is the default shell in current version of Windows. Unlike Unix shells which are built upon text processing and piping, inputs and outputs in PowerShell are .NET objects; as such, aliases in PowerShell do not work as string substitutions, but rather pointers to existing functions. This means that you’ll need to use functions to define your aliases whether additional parameters are involved or not. | |||
Here is a [guidelines-abiding](https://msdn.microsoft.com/en-us/library/ms714428) example of the `ynftag` alias: | |||
```sh | |||
function Get-NpmPackageDistributionTags { yarn info --verbose --no-emoji @Args dist-tags } | |||
New-Alias ynftag Get-NpmPackageDistributionTags | |||
``` | |||
Here's a yarn alias that re-adds the `ls` command to list packages: | |||
```sh | |||
# yarn broke 'ls' | |||
# Scope private do we don't call yarn recursively! | |||
function Private:yarn() { | |||
$modifiedArgs = @() | |||
foreach ( $arg in $args ) { | |||
if ( $arg -cmatch '^ls$' ) { | |||
$arg = 'list' | |||
} | |||
$modifiedArgs += $arg | |||
} | |||
& yarn $modifiedArgs | |||
} | |||
``` | |||
Save the code above to one of the many [PowerShell profiles](https://blogs.technet.microsoft.com/heyscriptingguy/2013/01/04/understanding-and-using-powershell-profiles/) that suits you best to persist the definition. | |||
## Command Prompt (`cmd`) | |||
If you’re still using the clunky Command Prompt, we believe that it’d be better for you, in the long run, to learn to use PowerShell instead. It’s more capable, modern, and everything is just way more consistent. Nonetheless, here is how you define an alias within the current Command Prompt instance: | |||
```sh | |||
doskey ynftag=yarn info --verbose --no-emoji $* dist-tags | |||
``` | |||
Since Command Prompt doesn’t come with a `.bashrc` equivalent, in order to persist your aliases permanently, you’ll need to create a custom `cmdrc.cmd` file (could be any name, but we recommend you to stick with the long-standing naming convention) inside your home directory, with the following content: | |||
```sh | |||
@echo off | |||
doskey ynftag=yarn info --verbose --no-emoji $* dist-tags | |||
``` | |||
Then modify your Command Prompt shortcut target to: | |||
```sh | |||
# Replace `cmdrc.cmd` with the full path that leads to the file. | |||
cmd.exe /k cmdrc.cmd | |||
``` | |||
## In Conclusion | |||
Yarn is a powerful JavaScript tool, but it’s also a tool that resides in your shell environment. By leveraging the innate capabilities of your shell, Yarn can do far more for you right now and right away. |
@@ -0,0 +1,601 @@ | |||
--- | |||
layout : post | |||
title : "Let's Dev: A Package Manager" | |||
author : Maël Nison | |||
author_url : "https://twitter.com/arcanis" | |||
date : 2017-07-11 8:00:00 | |||
categories : announcements | |||
share_text : "Let's Dev: A Package Manager" | |||
--- | |||
Hello everyone! Today, we're gonna write a new package manager, even better than Yarn! Ok, maybe not, but at least we're gonna have some fun, learn how package managers work, and think about what could come next on Yarn. | |||
> **The devil is in the details** | |||
> | |||
> This article omits small details and environment quirks, and focuses on the high-level architecture of a package manager, in an effort to stay succinct. For example, we're gonna assume that all paths are regular POSIX paths. | |||
> | |||
> That being said, there's much to say about these compatibility layers, and maybe talking about them could be an interesting follow up! Feel free to tweet at [@yarnpkg](https://twitter.com/yarnpkg) if you're interested to know more about them! 😃 | |||
To fully understand how things work, we're gonna go step by step, incrementally, adding or extending a single function at a time. We'll treat each of those steps as a separate chapter, and you will find an index of all chapters below this paragraph. Don't worry - they're all relatively short! Note that ES2017 features will be used all through the article - if you're unfamiliar with them, we recommend you to take a look at the great books [Explore ES6](http://exploringjs.com/es6/) and/or [Understanding ECMAScript 6](https://leanpub.com/understandinges6/read), and [Explore ES2017](http://exploringjs.com/es2016-es2017/). Good lecture! | |||
--- | |||
- **[#](#chapter-1---bravely-download) Chapter 1 - Bravely Download** | |||
_Or: where we download package tarballs_ | |||
- **[#](#chapter-2---one-reference-to-rule-them-all) Chapter 2 - One Reference to Rule Them All** | |||
_Or: where we resolve package ranges_ | |||
- **[#](#chapter-3---dependencies-of-our-dependencies-are-our-dependencies) Chapter 3 - Dependencies of Our Dependencies Are Our Dependencies** | |||
_Or: where we extract dependencies from packages_ | |||
- **[#](#chapter-4---super-dependency-world) Chapter 4 - Super Dependency World** | |||
_Or: where we do the same thing, but recursively_ | |||
- **[#](#chapter-5---links-awakening) Chapter 5 - Links Awakening** | |||
_Or: where we install our dependencies on the filesystem_ | |||
- **[#](#chapter-6---lord-of-the-optimization) Chapter 6 - Lord of the Optimization** | |||
_Or: where we try not to install the whole world on our system_ | |||
- **[#](#conclusion---there-really-was-a-cakehttpsgithubcomyarnpkglets-dev-demo) Conclusion - There Really Was a [Cake](https://github.com/yarnpkg/lets-dev-demo)** | |||
_Or: where we reflect on what we've learned_ | |||
--- | |||
## Chapter 1 - Bravely Download | |||
So, where should we start? First we have to think about what a package manager is. Let's forget the caches, the mirrors, the lockfiles, and all of the fancy command-line stuff, and let's focus on the very core: a package manager is a download manager. You ask it to download a package, and it happily executes. That's how we'll begin our adventure: with a very basic function that simply downloads something from the internet. | |||
```js | |||
import fetch from 'node-fetch'; | |||
async function fetchPackage(reference) { | |||
let response = await fetch(reference); | |||
if (!response.ok) throw new Error(`Couldn't fetch package "${reference}"`); | |||
return await response.buffer(); | |||
} | |||
``` | |||
Nice job! We just have to give this function a URL, and we'll eventually get the referenced package back! Of course, it only works if you know the exact URL for your package, but it's a good start. Rome wasn't built in one day, and our package manager won't be built with a single function either. | |||
Ok, what's next? Let's take a break and look at a classic package.json file to see what we could implement. | |||
```js | |||
{ | |||
"dependencies": { | |||
"react": "^15.5.4", | |||
"babel-core": "6.25.0" | |||
} | |||
} | |||
``` | |||
Oh, right, version ranges! It would be nice if we were able to just pass a version number to our fetcher, and let it convert it to an URL, right? Then let's do this! To make it easier, we'll only add support for pinned references (ie. `1.0.0` will be supported, but not `^1.0.0`). Finding the right regexp could be tedious, but thankfully we can rely on the excellent [semver](https://github.com/npm/node-semver) module, which will handle the bulk of the work for us! That being said, we'll still need to make a small change to the signature of our `fetchPackage` function. Instead of using a string to describe a package, we'll now use a `{name, reference}` object, where the name is the package name and the reference is the identifier that allows us to unequivocally locate this package. Thanks to this change, we can now write: | |||
```js | |||
import semver from 'semver'; | |||
async function fetchPackage({ name, reference }) { | |||
if (semver.valid(reference)) | |||
return await fetchPackage({ | |||
name, | |||
reference: `https://registry.yarnpkg.com/${name}/-/${name}-${reference}.tgz`, | |||
}); | |||
// ... same code as before | |||
} | |||
``` | |||
What do you think? If we detect that the reference is a semver version, then we convert it to an actual URL located on the Yarn registry. That's a nice download manager we have here, right? Ok, let's add a quick support for filesystem paths before we call it a day: | |||
```js | |||
import fs from 'fs-extra'; | |||
async function fetchPackage({ name, reference }) { | |||
// In a pure JS fashion, if it looks like a path, it must be a path. | |||
if ([`/`, `./`, `../`].some(prefix => reference.startsWith(prefix))) | |||
return await fs.readFile(reference); | |||
// ... same code as before | |||
} | |||
``` | |||
What do you think? Pretty simple, right? | |||
--- | |||
## Chapter 2 - One Reference to Rule Them All | |||
Our `fetchPackage` function is great, but it has one shortcoming, and a big one: As we said, our function can currently only serve pinned references. Ranges such as `^1.0.0` cannot be served, because they can potentially refer to multiple different versions, each of them having their own tarballs. So, in order to serve them, we'll need to find a way to extract a unique pinned reference from those ranges. Fortunately, it's not that hard! See for yourself: | |||
```js | |||
import semver from 'semver'; | |||
async function getPinnedReference({ name, reference }) { | |||
// 1.0.0 is a valid range per semver syntax, but since it's also a pinned | |||
// reference, we don't actually need to process it. Less work, yeay!~ | |||
if (semver.validRange(reference) && !semver.valid(reference)) { | |||
let response = await fetch(`https://registry.yarnpkg.com/${name}`); | |||
let info = await response.json(); | |||
let versions = Object.keys(info.versions); | |||
let maxSatisfying = semver.maxSatisfying(versions, reference); | |||
if (maxSatisfying === null) | |||
throw new Error( | |||
`Couldn't find a version matching "${reference}" for package "${name}"` | |||
); | |||
reference = maxSatisfying; | |||
} | |||
return { name, reference }; | |||
} | |||
// getPinnedReference({name: "react", reference: "~15.3.0"}) | |||
// → {name: "react", reference: "15.3.2"} | |||
// getPinnedReference({name: "react", reference: "15.3.0"}) | |||
// → {name: "react", reference: "15.3.0"} | |||
// getPinnedReference({name: "react", reference: "/tmp/react-15.3.2.tar.gz"}) | |||
// → {name: "react", reference: "/tmp/react-15.3.2.tar.gz"} | |||
``` | |||
And ... that's it! If we see a semver range, we just have to query the NPM registry to retrieve the list of all available versions. Once we've obtained it, it's just a matter of selecting the best one (which is made easy thanks to the `maxSatisfying` function provided by the semver module), and we're all set. | |||
Note that we don't need to do anything particular with semver versions, direct URLs, nor filesystem paths, since they'll always refer to a single package at any given time. So when we encounter them, we can just return them back without doing anything fancy. | |||
Thanks to this function, we can now rest assured that the references we'll send to our `fetchPackage` function will always be pinned references! Another day, another great victory for us. | |||
--- | |||
## Chapter 3 - Dependencies of Our Dependencies Are Our Dependencies | |||
In Chapter 1 we saw how to make a magic function that would download any package from anywhere, and return it. In Chapter 2, we saw how to convert volatile dependencies into pinned dependencies. That's a great start! But now we'll need to resolve a bigger issue: dependencies. See, the Node ecosystem being what it is, most packages rely on other packages in order to work properly. Fortunately, they all agreed on using a single standard to list those dependencies (remember the `package.json` file we've seen above), and so we should be able to make good use of this. Let's write our function. Given a package, we want it to return the dependencies this package relies on. | |||
> **Can't escape the tooling** | |||
> | |||
> Even if this article tries to stay focused on the core principle of package managers, we will need some utility function from time to time. When you encounter a symbol imported from `./utilities`, just don't bother understanding how it works under the hood. It's usually some boring and verbose code. That being said, all sources are available in the repository linked at the end of this post, including the utilities, so if you're really interested, give it a look later! | |||
```js | |||
// This function reads a file stored within an archive | |||
import { readPackageJsonFromArchive } from './utilities'; | |||
async function getPackageDependencies({ name, reference }) { | |||
let packageBuffer = await fetchPackage({ name, reference }); | |||
let packageJson = JSON.parse(await readPackageJsonFromArchive(packageBuffer)); | |||
// Some packages have no dependency field | |||
let dependencies = packageJson.dependencies || {}; | |||
// It's much easier for us to just keep using the same {name, reference} | |||
// data structure across all of our code, so we convert it there. | |||
return Object.keys(dependencies).map(name => { | |||
return { name, reference: dependencies[name] }; | |||
}); | |||
} | |||
// getPackageDependencies({name: "react", reference: "15.6.1"}) | |||
// → [{name: "create-react-class", reference: "^15.6.0"}, | |||
// {name: "prop-types", reference: "^15.5.10"}] | |||
``` | |||
What do you think? We've even been able to use our very own `fetchPackage` implementation to get the archive from where we extract the package information! From now on, whatever package people send us, we'll be able to know what other packages it depends on. That's a good start, but we'll now have to expand this ability a bit further: instead of resolving the first level of dependencies only, we'll want to resolve _everything_. And that's what the next chapter is about! | |||
--- | |||
## Chapter 4 - Super Dependency World | |||
Time we go full recursion. See, the idea is that before being able to install your packages into your `node_modules` folder, we'll first have to “install” them in memory. Why, you say? Well, proceeding this way will allow us to manipulate the tree before actually persisting it on the filesystem. Whether it's deduplication or hoisting, everything will have to be applied on this tree rather than on the actual disk (which would be really slow otherwise). But we'll cover that in another chapter! Right now, let's focus on extracting a complete dependency tree from a single root dependency. Since we've already written all the needed pieces (first the function to convert a volatile reference to a pinned reference, then the function to obtain a package dependencies), it will be quick. Let's get down to it: | |||
```js | |||
async function getPackageDependencyTree({ name, reference, dependencies }) { | |||
return { | |||
name, | |||
reference, | |||
dependencies: await Promise.all( | |||
dependencies.map(async volatileDependency => { | |||
let pinnedDependency = await getPinnedReference(volatileDependency); | |||
let subDependencies = await getPackageDependencies(pinnedDependency); | |||
return await getPackageDependencyTree( | |||
Object.assign({}, pinnedDependency, { dependencies: subDependencies }) | |||
); | |||
}) | |||
), | |||
}; | |||
} | |||
``` | |||
This one might look hard to digest, but bear with me! We start from a single package with its list of dependencies. Then, for each one of those dependencies, we first resolve the dependency's reference to become a pinned reference, then fetch its own dependencies, and then repeat the cycle on those sub-dependencies. In the end, we'll have a tree structure, where each package will be a node that contains its own dependencies! | |||
In order to use this function, we just have to read the initial dependencies from the `package.json` file located in the local working directory - everything inside is there for us to use! | |||
```js | |||
import { resolve } from 'path'; | |||
import util from 'util'; | |||
// We'll use the first command line argument (argv[2]) as working directory, | |||
// but if there's none we'll just use the directory from which we've executed | |||
// the script | |||
let cwd = process.argv[2] || process.cwd(); | |||
let packageJson = require(resolve(cwd, `package.json`)); | |||
// Remember that because we use a different format for our dependencies than | |||
// a simple dictionary, we also need to convert it when reading this file | |||
packageJson.dependencies = Object.keys(packageJson.dependencies || {}).map( | |||
name => { | |||
return { name, reference: packageJson.dependencies[name] }; | |||
} | |||
); | |||
getPackageDependencyTree(packageJson).then(tree => { | |||
console.log(util.inspect(tree, { depth: Infinity })); | |||
}); | |||
``` | |||
Now, let's test this code. Try running it inside a directory that contains the following `package.json`: | |||
```json | |||
{ | |||
"name": "my-awesome-package", | |||
"dependencies": { | |||
"tar-stream": "*" | |||
} | |||
} | |||
``` | |||
If everything goes According To Plan, here's what you should obtain (or similar, depending on whether a package has been upgraded since the time this article has been written): | |||
> **Undefined Reference** | |||
> | |||
> You might notice a weird reference on the following snippet: `undefined`. It's actually expected! This reference is used on the root package in order to inform the linker (more on that later) that this package is a bit special. In a real-life situation, we would probably want to use a special type of reference (for example `root:///path/to/package`), but in our case it's not necessary. | |||
```js | |||
{ name: "my-awesome-package", | |||
reference: undefined, | |||
dependencies: | |||
[ { name: 'tar-stream', | |||
reference: '1.5.4', | |||
dependencies: | |||
[ { name: 'bl', | |||
reference: '1.2.1', | |||
dependencies: | |||
[ { name: 'readable-stream', | |||
reference: '2.2.11', | |||
dependencies: | |||
[ { name: 'core-util-is', reference: '1.0.2', dependencies: [] }, | |||
{ name: 'inherits', reference: '2.0.3', dependencies: [] }, | |||
{ name: 'isarray', reference: '1.0.0', dependencies: [] }, | |||
{ name: 'process-nextick-args', | |||
reference: '1.0.7', | |||
dependencies: [] }, | |||
{ name: 'safe-buffer', reference: '5.0.1', dependencies: [] }, | |||
{ name: 'string_decoder', | |||
reference: '1.0.2', | |||
dependencies: [ { name: 'safe-buffer', reference: '5.0.1', dependencies: [] } ] }, | |||
{ name: 'util-deprecate', reference: '1.0.2', dependencies: [] } ] } ] }, | |||
{ name: 'end-of-stream', | |||
reference: '1.4.0', | |||
dependencies: | |||
[ { name: 'once', | |||
reference: '1.4.0', | |||
dependencies: [ { name: 'wrappy', reference: '1.0.2', dependencies: [] } ] } ] }, | |||
{ name: 'readable-stream', | |||
reference: '2.2.11', | |||
dependencies: | |||
[ { name: 'core-util-is', reference: '1.0.2', dependencies: [] }, | |||
{ name: 'inherits', reference: '2.0.3', dependencies: [] }, | |||
{ name: 'isarray', reference: '1.0.0', dependencies: [] }, | |||
{ name: 'process-nextick-args', | |||
reference: '1.0.7', | |||
dependencies: [] }, | |||
{ name: 'safe-buffer', reference: '5.0.1', dependencies: [] }, | |||
{ name: 'string_decoder', | |||
reference: '1.0.2', | |||
dependencies: [ { name: 'safe-buffer', reference: '5.0.1', dependencies: [] } ] }, | |||
{ name: 'util-deprecate', reference: '1.0.2', dependencies: [] } ] }, | |||
{ name: 'xtend', reference: '4.0.1', dependencies: [] } ] } ] } | |||
``` | |||
Perfect. Now, let's try to run it with larger packages. Let's try with babel-core! Use the following `package.json` file : | |||
```js | |||
{ | |||
"dependencies": { | |||
"babel-core": "*" | |||
} | |||
} | |||
``` | |||
Don't worry, I'll wait. | |||
... | |||
Still waiting. | |||
... | |||
Still... wait, is this script still running? That's not good, right? | |||
At this point we can safely assume that there's something wrong in our code - Babel is not that large, and the execution should have stopped a long time ago. In order to better understand what happened, open the [babel-core](https://yarnpkg.com/en/package/babel-core) page on Yarnpkg, and check its dependencies. You should see babel-register. Good. Now, open the [babel-register](https://yarnpkg.com/en/package/babel-runtime) page on Yarnpkg, and check its own dependencies. You should see... Yup. Babel-core. Now can you guess what happened? Because of the circular dependency, we've been iterating over babel-core, then babel-register, then babel-core, then... etc. Eventually, our code will end up using too much RAM and will get killed by the OS. That's really not good. | |||
Fortunately, the fix is fairly easy! Remember that in Node, `node_modules` directories can be nested. If a package can't be located inside the current directory `node_modules`, Node will try looking for it inside the parent directory `node_modules`, then its grandparent `node_modules`, etc, until it finds a satisfying match. Let's take advantage of that: | |||
```js | |||
// Look, we've added an extra optional parameter! ---------------------------------v | |||
async function getPackageDependencyTree( | |||
{ name, reference, dependencies }, | |||
available = new Map() | |||
) { | |||
return { | |||
name, | |||
reference, | |||
dependencies: await Promise.all( | |||
dependencies | |||
.filter(volatileDependency => { | |||
let availableReference = available.get(volatileDependency.name); | |||
// If the volatile reference exactly matches the available reference (for | |||
// example in the case of two URLs, or two file paths), it means that it | |||
// is already satisfied by the package provided by its parent. In such a | |||
// case, we can safely ignore this dependency! | |||
if (volatileDependency.reference === availableReference) return false; | |||
// If the volatile dependency is a semver range, and if the package | |||
// provided by its parent satisfies it, we can also safely ignore the | |||
// dependency. | |||
if ( | |||
semver.validRange(volatileDependency.reference) && | |||
semver.satisfies(availableReference, volatileDependency.reference) | |||
) | |||
return false; | |||
return true; | |||
}) | |||
.map(async volatileDependency => { | |||
let pinnedDependency = await getPinnedReference(volatileDependency); | |||
let subDependencies = await getPackageDependencies(pinnedDependency); | |||
let subAvailable = new Map(available); | |||
subAvailable.set(pinnedDependency.name, pinnedDependency.reference); | |||
return await getPackageDependencyTree( | |||
Object.assign({}, pinnedDependency, { | |||
dependencies: subDependencies, | |||
}), | |||
subAvailable | |||
); | |||
}) | |||
), | |||
}; | |||
} | |||
``` | |||
This change adds a filtering pass to our dependencies processing: if any of them happens to be already satisfied by a package made available somewhere in the upstream dependency chain, then we can just skip it, since there isn't any point in resolving it. Otherwise, we continue as usual, except that we insert them into the registry that contains our dependency chain packages. This way, our own dependencies will be able to skip installing us later on. | |||
If we go back to our babel-core example, it will go like this: | |||
``` | |||
- seeing babel-core@* | |||
- is it available in a parent module? NO | |||
- resolve it to babel-core@6.25.0 | |||
- resolve its dependencies | |||
- seeing babel-register@^6.24.1 | |||
- is it available in a parent module? NO | |||
- resolve it to babel-register@6.24.1 | |||
- resolve its dependencies | |||
- seeing babel-core@^6.24.1 | |||
- is it available in a parent module? YES, BECAUSE 6.25.0 MATCHES ^6.24.1 | |||
- skip resolution | |||
``` | |||
Awesome. We now have a working algorithm to compute our full dependency tree. We're almost done, just two more mandatory steps before we reach the fun and optional parts! | |||
--- | |||
## Chapter 5 - Links Awakening | |||
In Chapter 4, we saw how to obtain a complete tree of all of our dependencies. Now, we just have to download their tarballs somewhere, and extract them on the disk. The first part being made trivial by this awesome `fetchPackage` function we've conveniently written not so long ago, our linker will only be a matter of a few lines: | |||
```js | |||
// This function extracts an archive somewhere on the disk | |||
import { extractNpmArchiveTo } from './utilities'; | |||
async function linkPackages({ name, reference, dependencies }, cwd) { | |||
let dependencyTree = await getPackageDependencyTree({ | |||
name, | |||
reference, | |||
dependencies, | |||
}); | |||
// As we previously seen, the root package will be the only one containing | |||
// no reference. We can simply skip its linking, since by definition it already | |||
// contains the entirety of its own code :) | |||
if (reference) { | |||
let packageBuffer = await fetchPackage({ name, reference }); | |||
await extractNpmArchiveTo(packageBuffer, cwd); | |||
} | |||
await Promise.all( | |||
dependencies.map(async dependency => { | |||
await linkPackages(dependency, `${cwd}/node_modules/${dependency.name}`); | |||
}) | |||
); | |||
} | |||
``` | |||
And that's about it. This code will traverse your tree, unpack each package inside its designated directory (check the repository at the end of the article for the `extractArchiveTo` implementation if you care about it), then iterate over its children and do the same for each of them. Seems good enough, but I feel like we might be forgetting something... oh right! The binaries! See, NPM's `package.json` files offers a way for packages to expose utilities to the public (more details [here](https://docs.npmjs.com/files/package.json#bin)). We'll need to add a few extra lines to support this use case: | |||
```js | |||
import fs from 'fs-extra'; | |||
import path from 'path'; | |||
async function linkPackages({ name, reference, dependencies }, cwd) { | |||
// ... same code as before, except for the end: | |||
await Promise.all( | |||
dependencies.map(async ({ name, reference, dependencies }) => { | |||
let target = `${cwd}/node_modules/${name}`; | |||
let binTarget = `${cwd}/node_modules/.bin`; | |||
await linkPackages({ name, reference, dependencies }, target); | |||
let dependencyPackageJson = require(`${target}/package.json`); | |||
let bin = dependencyPackageJson.bin || {}; | |||
if (typeof bin === `string`) bin = { [name]: bin }; | |||
for (let binName of Object.keys(bin)) { | |||
let source = resolve(target, bin[binName]); | |||
let dest = `${binTarget}/${binName}`; | |||
await fs.mkdirp(`${cwd}/node_modules/.bin`); | |||
await fs.symlink(relative(binTarget, source), dest); | |||
} | |||
}) | |||
); | |||
} | |||
``` | |||
Good. But still, I can shake this feeling that... scripts! We're missing install scripts! Packages can specify commands that should run after a package has been installed (for example, they might want to compile or transpile some code depending on your environment). We don't execute them yet, but that should be fairly easy: | |||
```js | |||
import cp from 'child_process'; | |||
import util from 'util'; | |||
const exec = util.promisify(cp.exec); | |||
async function linkPackages({ name, reference, dependencies }, cwd) { | |||
// ... same code as before except the end: | |||
await Promise.all( | |||
dependencies.map(async ({ name, reference, dependencies }) => { | |||
// ... same code as before | |||
if (dependencyPackageJson.scripts) { | |||
for (let scriptName of [`preinstall`, `install`, `postinstall`]) { | |||
let script = dependencyPackageJson.scripts[scriptName]; | |||
if (!script) continue; | |||
await exec(script, { | |||
cwd: target, | |||
env: Object.assign({}, process.env, { | |||
PATH: `${target}/node_modules/.bin:${process.env.PATH}`, | |||
}), | |||
}); | |||
} | |||
} | |||
}) | |||
); | |||
} | |||
``` | |||
> **All your environments are belong to it** | |||
> | |||
> Note that we've only set the `PATH` environment variable inside this snippet, but packages usually have access to a whole lot of extra environment variables (more details [here](https://docs.npmjs.com/misc/scripts#environment)). They are rarely used, but if you plan to write a package manager then you'll have to make sure that you actually define them one way or the other. | |||
Now, calling our linker function will install everything we need on the filesystem! Better yet, all build scripts will be run correctly, meaning you will end up with a working `node_modules` directory! Good job! Our next chapter will be about performances, things will now start to get really interesting. | |||
--- | |||
## Chapter 6 - Lord of the Optimization | |||
Our package manager is working! However, you may notice something ... Because we're not taking advantage of Node's resolution algorithm, and because we don't try to remove duplicates from our package tree, we might end up with a really huge `node_modules` folder! You might think that it's not that much of a problem, but it has proven to [cause issues in the past](https://scottaddie.com/2015/08/16/npm-vs-windows-max_path-limitation/). For example, on most Windows installations, paths have a hard limit of 260 characters. For packages that are deeply nested, this limit is often exceeded and it breaks things. Fortunately, Node's resolution algorithm help us by allowing us to move the dependencies lower in the tree, as long as there is no conflicts. | |||
So let's go! Our job in this chapter will be to decrease the number of packages that get installed on the filesystem, by any means necessary. However, we will also do the best we can to keep our algorithm both simple and encapsulated, so that it can be easily understood by maintainers and contributors alike, and can be switched or disabled in a single line if we need to. | |||
Here's a possible implementation. It's not perfect, but it's a good start! Don't be scared by its length, most of this is just comments: | |||
```js | |||
function optimizePackageTree({ name, reference, dependencies }) { | |||
// This is a Divide & Conquer algorithm - we split the large problem into | |||
// subproblems that we solve on their own, then we combine their results | |||
// to find the final solution. | |||
// | |||
// In this particular case, we will say that our optimized tree is the result | |||
// of optimizing a single depth of already-optimized dependencies (ie we first | |||
// optimize each one of our dependencies independently, then we aggregate their | |||
// results and optimize them all a last time). | |||
dependencies = dependencies.map(dependency => { | |||
return optimizePackageTree(dependency); | |||
}); | |||
// Now that our dependencies have been optimized, we can start working on | |||
// doing the second pass to combine their results together. We'll iterate on | |||
// each one of those "hard" dependencies (called as such because they are | |||
// strictly required by the package itself rather than one of its dependencies), | |||
// and check if they contain any sub-dependency that we could "adopt" as our own. | |||
for (let hardDependency of dependencies.slice()) { | |||
for (let subDependency of hardDependency.dependencies.slice()) { | |||
// First we look for a dependency we own that is called | |||
// just like the sub-dependency we're iterating on. | |||
let availableDependency = dependencies.find(dependency => { | |||
return dependency.name === subDependency.name; | |||
}); | |||
// If there's none, great! It means that there won't be any collision | |||
// if we decide to adopt this one, so we can just go ahead. | |||
if (!availableDependency.length) dependencies.push(subDependency); | |||
// If we've adopted the sub-dependency, or if the already existing | |||
// dependency has the exact same reference than the sub-dependency, | |||
// then it becomes useless and we can simply delete it. | |||
if ( | |||
!availableDependency || | |||
availableDependency.reference === subDependency.reference | |||
) { | |||
hardDependency.dependencies.splice( | |||
hardDependency.dependencies.findIndex(dependency => { | |||
return dependency.name === subDependency.name; | |||
}) | |||
); | |||
} | |||
} | |||
} | |||
return { name, reference, dependencies }; | |||
} | |||
``` | |||
And that's it. We'll just have to call this function after resolving and before linking, and we'll get a much simpler tree that will still produce a valid output according to Node's resolution algorithm! | |||
> **The devil really was in the details** | |||
> | |||
> As we saw in the introduction of this article, a large amount of what makes package managers complex software lies in the details. Our optimizer code suffers from this: despite it working in many cases, it actually has an unfortunate bug related to how binaries are linked. With the code shown above, package binaries will not be installed where they should, because when optimizing we lost the information that would allow the linker to correctly link each binary to the right location. Because of this, they will not be found when running the `build` scripts. Oops! | |||
> | |||
> Solving this would require adding some fields into our resolution tree nodes that we would then use to track the nodes original locations in the tree. The linker would then be able to link the binaries directly inside its children in a post-processing pass. Unfortunately, it would also make the code much less clear, so we opted not to implement this here. Such is the tough life of package manager writers... | |||
--- | |||
## Conclusion - There Really Was a [Cake](https://github.com/yarnpkg/lets-dev-demo) | |||
Finally! After all this time, we have our tiny package manager! You can even see its full code on [this repository](https://github.com/yarnpkg/lets-dev-demo) - you can try it, it really works! It is admittedly pretty basic, kind of slow, and without much features, but we love it nevertheless and that's all that matters. And because it's young, there is still room for a lot of evolutions and improvements: | |||
- We could implement a powerful CLI that would be similar to Yarn! With progress bars, and emojis, and all those fancy things! In fact, the demo already has progress bars, so that's a good start! | |||
- We could split our functions into modules! Our package manager would then be a simple CLI, and our fetchers / resolvers / linkers would be loaded from a configuration file. Want to link everything using symlinks or hardlinks instead of copying files? Just use another linker than the default one! Want to add support for extra fetchers? Add them to your config files and be done with it! In fact, we even [started experimenting with something similar in Yarn](https://github.com/yarnpkg/yarn/pull/3501). | |||
- We could also improve our optimizer so that it would actually work in every case! ;) And assuming a plugin architecture like the one we talked about in the previous bullet point, we could even implement different optimization strategies — from the `[--flat](https://yarnpkg.com/lang/en/docs/cli/install/#toc-yarn-install-flat)` option to ensure that we wouldn't use multiple versions of any single package, up to the more esoteric ones that would use more complex algorithms, such as [SAT solvers](https://github.com/yarnpkg/yarn/issues/422) — and all the while without any risk of hurting the package manager core experience! | |||
- We could persist our resolution tree to a file on the disk, which we would call `yarn.lock`, and each time we would need to process a package from within our `getPinnedReference` and `getPackageDependencies` functions, we would instead extract that information from the file instead of over the network! (In case you're wondering, that's exactly how both Yarn's `yarn.lock` and NPM@5's `package-lock.json` files work) | |||
- We could save the tarballs in some sort of a cache, so that we wouldn't have to download them from the network multiple times. By doing this we could even install our packages offline, if our cache is sufficiently well furnished! | |||
This is only a short list, far from being exhaustive! Package managers can implement a wide range of features, and all of them can each be improved in a lot of different ways. As you can see, the future looks bright: who can tell what new features and improvements will come during the incoming years? No one can tell for sure, but what I _can_ tell you is to watch this blog for the next Yarn announcement! | |||
--- | |||
> I hope you've enjoyed this article as much as I've taken pleasure in writing it! If you want to discuss it, whether it's to correct some mistake or to just talk about package managers, ping me on Twitter via [@arcanis](https://twitter.com/arcanis), or on Yarn's [Discord](https://discord.gg/yarnpkg) server where the core team regularly lurks :) |
@@ -0,0 +1,229 @@ | |||
--- | |||
layout : post | |||
title : "Workspaces in Yarn" | |||
author : Konstantin Raev | |||
author_url : "https://twitter.com/bestander_nz" | |||
date : 2017-08-02 8:00:00 | |||
categories : announcements | |||
share_text : "Yarn Workspaces: the evolution of multipackage projects" | |||
--- | |||
Projects tend to grow over time, and, occasionally, some pieces of a project can be useful elsewhere in other projects. For example, [Jest](http://facebook.github.io/jest/), being a generic testing tool, gave birth to many packages, one of them is [jest-snapshot](https://yarnpkg.com/en/package/jest-snapshot) that is now used in other projects like [snapguidist](https://yarnpkg.com/en/package/snapguidist) and [chai-jest-snapshot](https://yarnpkg.com/en/package/chai-jest-snapshot). | |||
## Monorepos | |||
Those who have [tried splitting a project into multiple packages](https://youtu.be/PvabBs_utr8?t=16m24s) know how hard it is to make changes across multiple packages at one time. To make the process easier, some big projects adopted a [monorepo](http://www.drmaciver.com/2016/10/why-you-should-use-a-single-repository-for-all-your-companys-projects/) approach, or multi-package repositories, which reduces the burden of writing code across packages. | |||
Several projects used every day by JavaScript developers are managed as monorepos: [Babel](https://github.com/babel/babel/tree/master/packages), [React](https://github.com/facebook/react/tree/master/packages), [Jest](https://github.com/facebook/jest/tree/master/packages), [Vue](https://github.com/vuejs/vue/tree/dev/packages), [Angular](https://github.com/angular/angular/tree/master/packages). | |||
However, separating pieces of projects into their own folders is sometimes not enough. Testing, managing dependencies, and publishing multiple packages quickly gets complicated and many such projects [adopt](https://medium.com/@bebraw/the-case-for-monorepos-907c1361708a) tools such as [Lerna](https://lerna.js.org/) to make working with monorepos easier. | |||
## Lerna | |||
Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm. Internally it uses [Yarn](https://code.facebook.com/posts/1840075619545360) or the npm CLI to bootstrap (i.e. install all third party dependencies for each package) a project. In a nutshell, Lerna calls `yarn/npm install` for each package inside the project and then creates symlinks between the packages that refer each other. | |||
Being a wrapper of a package manager, Lerna can't manipulate the contents of node_modules efficiently: | |||
- Lerna calls `yarn install` multiple times for each package which creates overhead because each `package.json` is considered independent and they can't share dependencies with each other. This causes a lot of duplication for each node_modules folder which quite often use the same third-party packages. | |||
- Lerna manually creates links between packages that refer each other after installation has finished. This introduces inconsistency inside node_modules that a package manager may not be aware of, so running `yarn install` from within a package may break the meta structure that Lerna manages. | |||
Issues such as these convinced us, as package manager developers, that we should support multi-package repositories directly in Yarn. **Starting with [Yarn 0.28,](https://yarnpkg.com/en/docs/install) we're excited to share that we support such repositories under the Workspaces feature**. | |||
## Introducing Yarn Workspaces | |||
Yarn Workspaces is a feature that allows users to install dependencies from multiple package.json files in subfolders of a single root package.json file, all in one go. | |||
Making Workspaces native to Yarn enables faster, lighter installation by preventing package duplication across Workspaces. Yarn can also create symlinks between Workspaces that depend on each other, and will ensure the consistency and correctness of all directories. | |||
## Setting up Workspaces | |||
> Starting Yarn 1.0 Workspaces are enabled by default and you may not need to set the below config. Refer to the updated steps at the following location https://yarnpkg.com/lang/en/docs/workspaces/ | |||
To get started, users must enable Workspaces in Yarn by running the following command: | |||
``` | |||
yarn config set workspaces-experimental true | |||
``` | |||
It will add `workspaces-experimental true` to the `.yarnrc` file in your OS home folder. Yarn Workspaces is still considered experimental while we gather feedback from the community. | |||
Let's take [Jest](http://facebook.github.io/jest/) as an example and set Yarn Workspaces up for that structure. As a matter of fact, it has already been [done in a PR](https://github.com/facebook/jest/pull/3906), and Jest has been using Yarn to bootstrap its packages for a while. | |||
Jest's project structure is typical for an Open Source JavaScript monorepo. | |||
``` | |||
| jest/ | |||
| ---- package.json | |||
| ---- packages/ | |||
| -------- jest-matcher-utils/ | |||
| ------------ package.json | |||
| -------- jest-diff/ | |||
| ------------ package.json | |||
... | |||
``` | |||
The top-level `package.json` defines the root of the project, and folders with other package.json files are the Workspaces. | |||
Workspaces usually are published to a registry like npm. | |||
While the root is not supposed to be consumed as a package, it usually contains the glue code or business specific code that is not useful for sharing with other projects, that is why we mark it as “private”. | |||
The following example is a simplified root `package.json` that enables Workspaces for the project and defines third-party packages needed for the project build and test environment. | |||
``` | |||
{ | |||
"private": true, | |||
"name": "jest", | |||
"devDependencies": { | |||
"chalk": "^2.0.1" | |||
}, | |||
"workspaces": [ | |||
"packages/*" | |||
] | |||
} | |||
``` | |||
To keep things simple I'll describe two small Workspaces packages: | |||
1. jest-matcher-utils Workspace: | |||
``` | |||
{ | |||
"name": "jest-matcher-utils", | |||
"description": "...", | |||
"version": "20.0.3", | |||
"license": "...", | |||
"main": "...", | |||
"browser": "...", | |||
"dependencies": { | |||
"chalk": "^1.1.3", | |||
"pretty-format": "^20.0.3" | |||
} | |||
} | |||
``` | |||
2. jest-diff Workspace that depends on jest-matcher-utils: | |||
``` | |||
{ | |||
"name": "jest-diff", | |||
"version": "20.0.3", | |||
"license": "...", | |||
"main": "...", | |||
"browser": "...", | |||
"dependencies": { | |||
"chalk": "^1.1.3", | |||
"diff": "^3.2.0", | |||
"jest-matcher-utils": "^20.0.3", | |||
"pretty-format": "^20.0.3" | |||
} | |||
} | |||
``` | |||
A wrapper like Lerna would first run `yarn install` for each `package.json` separately and then run `yarn link` for packages that depend on each other. | |||
If we used that approach, we would get a folder structure like the following: | |||
``` | |||
| jest/ | |||
| ---- node_modules/ | |||
| -------- chalk/ | |||
| ---- package.json | |||
| ---- packages/ | |||
| -------- jest-matcher-utils/ | |||
| ------------ node_modules/ | |||
| ---------------- chalk/ | |||
| ---------------- pretty-format/ | |||
| ------------ package.json | |||
| -------- jest-diff/ | |||
| ------------ node_modules/ | |||
| ---------------- chalk/ | |||
| ---------------- diff/ | |||
| ---------------- jest-matcher-utils/ (symlink) -> ../jest-matcher-utils | |||
| ---------------- pretty-format/ | |||
| ------------ package.json | |||
... | |||
``` | |||
As you see, there is a redundancy of third-party dependencies. | |||
With Workspaces enabled, Yarn can produce a much more optimized dependency structure and when you run the usual `yarn install` anywhere in the project you'll get the following node_modules. | |||
``` | |||
| jest/ | |||
| ---- node_modules/ | |||
| -------- chalk/ | |||
| -------- diff/ | |||
| -------- pretty-format/ | |||
| -------- jest-matcher-utils/ (symlink) -> ../packages/jest-matcher-utils | |||
| ---- package.json | |||
| ---- packages/ | |||
| -------- jest-matcher-utils/ | |||
| ------------ node_modules/ | |||
| ---------------- chalk/ | |||
| ------------ package.json | |||
| -------- jest-diff/ | |||
| ------------ node_modules/ | |||
| ---------------- chalk/ | |||
| ------------ package.json | |||
... | |||
``` | |||
Packages like `diff`, `pretty-format` and the symlink to `jest-matcher-utils` were hoisted to the root node_modules directory, making the installation faster and smaller. The package `chalk` however could not be moved to the root because the root already depends on a different, incompatible version of `chalk`. | |||
Both of the structures above are compatible, but the latter is more optimal while still being correct regarding the Node.js module resolution logic. | |||
For avid Lerna users this is similar to bootstrapping code via the `--hoist` flag. | |||
If you run code inside the `jest-diff` Workspace, it will be able to resolve all its dependencies: | |||
- require('chalk') resolves to `./node_modules/chalk` | |||
- require('diff') resolves to `../../node_modules/diff` | |||
- require('pretty-format') resolves to `../../node_modules/pretty-format` | |||
- require('jest-matcher-utils') resolves to `../../node_modules/jest-matcher-utils` that is a symlink to `../packages/jest-matcher-utils` | |||
## Managing dependencies of Workspaces | |||
If you want to modify a dependency of a Workspace, just run the appropriate command inside the Workspace folder: | |||
``` | |||
$ cd packages/jest-matcher-utils/ | |||
$ yarn add left-pad | |||
✨ Done in 1.77s. | |||
$ git status | |||
modified: package.json | |||
modified: ../../yarn.lock | |||
``` | |||
Note that Workspaces don't have their own yarn.lock files, and the root yarn.lock contains all the dependencies for all the Workspaces. | |||
When you want to change a dependency inside a Workspace, the root yarn.lock will be changed as well as the Workspace's package.json. | |||
## Integrating with Lerna | |||
Do Yarn Workspaces make Lerna obsolete? | |||
Not at all. Yarn Workspaces are easily integrated with Lerna. | |||
Lerna provides a lot more than just bootstrapping a project and it has a community of users around it that have fine-tuned Lerna for their needs. | |||
Starting with Lerna 2.0.0, when you pass the flag [`--use-workspaces`](https://github.com/lerna/lerna#--use-workspaces) when running Lerna commands, it will use Yarn to bootstrap the project and also it will use `package.json/workspaces` field to find the packages instead of `lerna.json/packages`. | |||
This is how Lerna is configured for Jest: | |||
``` | |||
{ | |||
"lerna": "2.0.0", | |||
"npmClient": "yarn", | |||
"useWorkspaces": true | |||
} | |||
``` | |||
Jest relies on Yarn to bootstrap the project, and on Lerna for running the publish command(s). | |||
## What is next? | |||
Yarn Workspaces is the first step of what a package manager could do for managing monorepos as they become a more common solution to code sharing. | |||
At the same time we don't want to put all the possible monorepo features into Yarn. We want to keep Yarn focused and lean, and this means that Yarn and projects like Lerna will continue working together. | |||
Our next goal is to finalize Yarn 1.0, which is meant to summarize the work we have done on Yarn over the past year, and recognizing how reliable Yarn has become. We'll also share our thoughts on what we'd like to build next for Yarn then. | |||
Stay tuned. |
@@ -0,0 +1,11 @@ | |||
--- | |||
layout : post | |||
title : "Yarn 1.0 is Here" | |||
author : Burak Yigit Kaya | |||
author_url : "https://twitter.com/madbyk" | |||
date : 2017-09-07 8:00:00 | |||
categories : announcements | |||
share_text : "Yarn 1.0: Workspaces, auto-merging lockfiles, selective versions resolutions" | |||
--- | |||
After a long wait, [Yarn 1.0 is out](https://code.facebook.com/posts/274518539716230)! |
@@ -0,0 +1,234 @@ | |||
--- | |||
layout : post | |||
title : "nohoist in Workspaces" | |||
author : V. Sun | |||
author_url : "https://github.com/connectdotz" | |||
date : 2018-02-15 8:00:00 | |||
categories : announcements | |||
share_text : "" | |||
--- | |||
As wonderful as [yarn workspaces](https://yarnpkg.com/blog/2017/08/02/introducing-workspaces/) are, the rest of the community hasn't yet fully caught up with the monorepo hoisting scheme. The introducing of the [nohoist](https://github.com/yarnpkg/yarn/pull/4979) is the attempt to provide an easy-to-use mechanism, natively supported by yarn, for enabling workspaces to work with otherwise incompatible libraries. | |||
We hope this feature would ease the pain for monorepo developers and strike a balance between efficiency (hoisting as much as possible) and usability (unblock the libraries who haven't been adapted for workspaces). | |||
## What is the problem ? | |||
First, let's take a quick tour on how hoist work in standalone projects: | |||
To reduce redundancy, most package managers employ some kind of hoisting scheme to extract and flatten all dependent modules, as much as possible, into a centralized location. In a standalone project, the dependency tree can be reduced like this: | |||
<img src="/assets/posts/2018-02-15-nohoist/standalone-2.svg" width="100%" /> | |||
With hoist, we were able to eliminate duplicate "A@1.0" and "B@1.0", while preserving version variation (B@2.0) and maintaining the same root `package-1/node_modules`. Most module crawlers/loaders/bundlers can locate modules pretty efficiently by traversing down the "node_modules" tree from the project root. | |||
Then came the monorepo project, which introduced a new hierarchical structure that is not necessary linked by "node_modules". In such project, modules could be scattered in multiple locations: | |||
<img src="/assets/posts/2018-02-15-nohoist/monorepo-2.svg" width="100%"/> | |||
yarn workspaces can share modules across child projects/packages by hoisting them up to their parent project's node_modules: `monorepo/node_modules`. This optimization becomes even more prominent when considering these packages will most likely be dependent on each other (the main reason to have the monorepo), i.e. higher degree of redundancy. | |||
### Module not found!! | |||
While it might appear that we can access all modules from the project's root node_modules, we often build each package in its local project, where the modules might not be visible under its own node_modules. In addition, not all crawlers traverse [symlinks](https://github.com/facebook/metro/issues/1). | |||
Consequently, workspaces developers often witness "module not found" related errors when building from the child project: | |||
- can't find module "B@2.0" from project root "monorepo" (not able to follow symlink) | |||
- can't find module "A@1.0" from "package-1" (unaware of the module tree above in "monorepo") | |||
For this monorepo project to reliably find any module from anywhere, it needs to traverse each node_modules tree: "monorepo/node_modules" and "monorepo/packages/package-1/node_modules". | |||
## Why can't they be fixed? | |||
There are indeed many ways library owners can address these issues, such as multi-root, custom module map, clever traversing scheme, among others... However, | |||
1. not all the 3rd party libraries have the resource to adapt for monorepo environment | |||
1. the weakest link problem: javascript is great thanks to the massive 3rd-party libraries. However, that also means the complex tool chain is only as strong as the weakest link. A single non-adapted package deep down the tool chain could render the whole tool useless. | |||
1. the bootstrap problems: for example, react-native has provided a way to configure multi-root through `rn-cli.config.js`. But it won't help the bootstrap process like `react-native init` or `create-react-native-app`, which has no access of any such tooling before the app is created/installed. | |||
It is frustrating when a solution worked for a standalone project only fell short in the monorepo environment. The ideal solution lies in addressing the underlying libraries, but the reality is far from perfect and we all know that our projects can't wait... | |||
## What is "nohoist" ? | |||
Is there a simple yet universal mechanism that can allow these incompatible libraries working in the monorepo environment? | |||
Turns out there is, and is conveniently called _"nohoist"_, which has also been demonstrated in other monorepo tools like [lerna](https://github.com/lerna/lerna/blob/master/doc/hoist.md). | |||
"nohoist" enables workspaces to consume 3rd-party libraries not yet compatible with its hoisting scheme. The idea is to disable the selected modules from being hoisted to the project root. They were placed in the actual (child) project instead, just like in a standalone, non-workspaces, project. | |||
Since most 3rd-party libraries already worked in standalone projects, the ability to simulate such environment within workspaces should be able to unblock many known compatibility issues. | |||
### A word of caution | |||
While nohoist is useful, it does come with drawbacks. The most obvious one is the nohoist modules could be duplicated in multiple locations, denying the benefit of hoisting mentioned above. Therefore, we recommend to keep nohoist scope as small and explicit as possible in your project. | |||
## When will it be available? | |||
It is scheduled to be deployed with **1.4.2** | |||
## How to use it? | |||
Using nohoist is pretty straightforward. It is driven by the nohoist rules defined in package.json. Starting from 1.4.2, yarn will adopt a new workspaces config format to include the (optional) nohoist setting: | |||
``` | |||
// flow type definition: | |||
export type WorkspacesConfig = { | |||
packages?: Array<string>, | |||
nohoist?: Array<string>, | |||
}; | |||
``` | |||
For example: | |||
- under a private project root, with nohoist: | |||
``` | |||
"workspaces": { | |||
"packages": ["packages/*"], | |||
"nohoist": ["**/react-native", "**/react-native/**"] | |||
} | |||
``` | |||
- under a private project root, without nohoist: | |||
``` | |||
"workspaces": { | |||
"packages": ["packages/*"], | |||
} | |||
``` | |||
- under a private child project, with nohoist: | |||
``` | |||
"workspaces": { | |||
"nohoist": ["react-native", "react-native/**"] | |||
} | |||
``` | |||
_Note: for those who don't need nohoist, the old workspaces format will continue to be supported._ | |||
nohoist rules are just a collection of [glob patterns](https://github.com/isaacs/minimatch) used to match against the module path in its dependency tree. Module path is a virtual path of the dependency tree, not an actual file path, so no need to specify "node_modules" or "packages" in the nohoist pattern. | |||
### illustration | |||
Let's look at a simplified pseudo example to explain how nohoist can be used to prevent react-native from being hoisted in our monorepo project "monorepo". There are 3 packages under "monorepo": A, B and C: | |||
 | |||
the file system before `yarn install`: | |||
 | |||
the package.json file in project root "monorepo": | |||
``` | |||
// monorepo's package.json | |||
... | |||
"name": "monorepo", | |||
"private": true, | |||
"workspaces": { | |||
"packages": ["packages/*"], | |||
"nohoist": ["**/react-native", "**/react-native/**"] | |||
} | |||
... | |||
``` | |||
Let's take a closer look at the config: | |||
#### Scope: private | |||
nohoist is only available for private packages because workspaces are only available for private packages. | |||
#### glob patterns matching | |||
Internally, yarn constructs a virtual module path for each module based on its "original" (before hoisting) package dependency. If this path matched the nohoist patterns provided, it will be hoisted to the closest child project/package instead. | |||
##### module paths | |||
A | |||
- monorepo/A | |||
- monorepo/A/react-native | |||
- monorepo/A/react-native/metro | |||
- monorepo/A/Y | |||
B | |||
- monorepo/B | |||
- monorepo/B/X | |||
- monorepo/B/X/react-native | |||
- monorepo/B/X/react-native/metro | |||
C | |||
- monorepo/C | |||
- monorepo/C/Y | |||
##### nohoist patterns | |||
"**\*\*/react-native**": this tells yarn not to hoist the react-native package itself, no matter where it is. (shallow) | |||
- the use of globstar "\*\*" matches 0 to n elements prior to react-native, which means it will match any react-native occurrence no matter where it appear on the path. | |||
- the pattern ends with "react-native" means react-native's dependencies, such as "react-native/metro", will not match this pattern, thus the term "shallow". | |||
"**\*\*/react-native/\*\***": this tells yarn not to hoist any of the react-native's dependent libraries and their dependent libraries. (deep) | |||
- the pattern ends with "\*\*", unlike prefix globstar mentioned above, the suffix globstar matches 1 to n elements after react-native, which means only react-native's dependencies will match this pattern, but not react-native itself. | |||
- not only react-native's direct dependencies match this pattern, their dependencies and so on will too, thus the term "deep". | |||
Combining these 2 patterns (shallow + deep), they instruct yarn not to hoist react-native and all of its dependencies. | |||
Let's try some more patterns: | |||
- what if we only want to apply react-native nohoist for package A? | |||
``` | |||
"nohoist": ["A/react-native", "A/react-native/**"] | |||
``` | |||
- what if package A also needs to include package C when building the react-native app? | |||
``` | |||
"nohoist": ["A/react-native", "A/react-native/**", "A/C"] | |||
``` | |||
A symlink to package C will be created under package A's node_modules. | |||
#### File structure after hoist | |||
after `yarn install`, the file structure will look like this: | |||
 | |||
Module X and Y have been hoisted to root because "monorepo/A/Y", "monorepo/B/X" and "monorepo/C/Y" don't match any of the nohoist patterns. Note that even though "monorepo/B/X/react-native" matches the nohoist pattern, "monorepo/B/X" doesn't. Therefore the react-native modules will be left in package "B" while their original parent "X" being hoisted to the root. | |||
react-native and metro have all been placed under package A and B respectively because they matched the react-native nohoist patterns. Note that even though B does not directly depends on react-native, they are still hoisted to "B", just like in a standalone project. | |||
### how to turn off nohoist? | |||
nohoist is on by default. If yarn sees nohoist config in a private package.json, it will use it. | |||
To turn off nohoist, you can just remove the nohoist config from package.json, or set the flag `workspaces-nohoist-experimental false` via .yarnrc or `yarn config set workspaces-nohoist-experimental false`. | |||
### Working examples | |||
Now you have some basic idea of how nohoist work, it's time to play with the real thing... | |||
Below are the test projects we used when developing nohoist. They are now available in the [yarn-nohoist-examples](https://github.com/connectdotz/yarn-nohoist-examples): | |||
1. create react-native within yarn workspaces: | |||
=> Confirming we can pretty much follow react-native's guide like in standalone env | |||
1. create a more realistic monorepo project including both react and react-native: | |||
=> Make sure nohoist works for this command and more advanced use case. | |||
These are working examples, i.e. you should be able to clone and run it by following the instructions there. If not, please let us know. | |||
### Investigate | |||
What if things didn't happen as expected? Surprised how many modules in your local node_modules or nothing at all? Yarn has a powerful "[why](https://yarnpkg.com/en/docs/cli/why)" command that can report its hoisting reasons so you can investigate and indulge your curiosity... | |||
This will probably be best explained with an actual [example](https://github.com/connectdotz/yarn-nohoist-examples/tree/master/workspaces-examples/react-native#under-the-hood) | |||
## Conclusion | |||
nohoist is new and most likely needs a few round of polishing. Please do let us know if something didn't seem right. It had already made our workspaces a lot easier, hopefully it will do the same for you. | |||
We would also like to call on the library owners to adapt your libraries for monorepo environment so maybe one day we can retire nohoist and all sharable modules can be hoisted to their rightful places... | |||
## References | |||
- nohoist original proposal: [RFC #86](https://github.com/yarnpkg/rfcs/pull/86) | |||
- nohoist PR: [#4979](https://github.com/yarnpkg/yarn/pull/4979) | |||
- workspaces introduction: [Workspaces in Yarn](https://yarnpkg.com/blog/2017/08/02/introducing-workspaces/) |
@@ -0,0 +1,72 @@ | |||
--- | |||
layout : post | |||
title : "Dependencies Done Right" | |||
author : Maël Nison | |||
author_url : "https://twitter.com/arcanis" | |||
date : 2018-04-18 8:00:00 | |||
categories : announcements | |||
share_text : "Dependencies Done Right, or why peer dependencies can be the better choice" | |||
--- | |||
Let's say we want to write a React plugin. Since we'll need to require the `react` package, we add it to our dependencies like this: | |||
```json | |||
{ | |||
"name": "my-awesome-plugin", | |||
"dependencies": { | |||
"react": "^16.0.0" | |||
} | |||
} | |||
``` | |||
Then we run `yarn install`, everything works, we're happy, we publish our package to the World Wide Web, and then... | |||
Someone tries to install it, and it breaks. Not cool. | |||
We start getting reports from users saying that React is present multiple times in their dependency tree - once as top-level dependency of their project, and another time as a dependency of our plugin. This must be wrong! Since both of these `package.json` files list compatible versions of React, the package manager should surely merge them together into a single one, right? Turns out, it is not that simple. Let's see what happens and how we can remediate it! | |||
_If you're just interested in the solution and not knowing why it works this way, scroll to the last paragraph! Otherwise, keep reading!_ | |||
## A Tale of Semantics | |||
See, the fields in `package.json` all have a meaning. That's not new. But what are they exactly? What are their semantics? To borrow the C/C++ vocabulary, which behaviors are spec-defined, and which ones are [implementation defined](https://en.wikipedia.org/wiki/Unspecified_behavior#Implementation-defined_behavior) or, worse, [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior)? To answer this question, let's explain these fields and their guarantees in plain English: | |||
- The `dependencies` object guarantees that, for each entry, your package will be able to access the specified version of the dependency through `require()`. It also guarantees that all exported bins from those dependencies will be made available to your scripts (when running `yarn run <script name>`). | |||
- The `devDependencies` object guarantees that, for each entry, your package will be able to access the specified version of the dependency through `require()`, provided that your package is at the top of the dependency tree, and that the installation hasn't been run in production mode (`--production`). | |||
- The `peerDependencies` object guarantees that, for each entry, any package that requires you will be requested to provide a copy of the dependency listed here, ideally matching the version you requested. It also guarantees that you'll be able to access this dependency through `require()`, and finally **it also guarantees that the return of `require()` will be exactly the same version & instance than the one your parent would obtain if they were to `require()` it themselves**. | |||
And that's the catch. When you specify a dependency, package managers are not required to give you the exact same version from anywhere in the dependency tree! Doing so is an optimization, and while we try to do our best to remove duplicated packages from the tree, it's not always feasible or possible. Because we cannot guarantee it, _you should not rely on us doing it_. By spec, we are allowed to change the behavior from a version to another if we think it might give a better result. We technically could even disable hoisting altogether! | |||
> **Why is it not guaranteed?** The first reason is that hoisting the packages is not always possible. Consider the following case: both Foo and Bar depend on HelloWorld@1.0.0, and the top-level package depends on Foo, Bar, and HelloWorld@2.0.0. Because of how the Node resolution works, Foo and Bar copies of HelloWorld cannot be hoisted (they would conflict with the version required by the top-level), meaning that the result of their `require()` calls will be different instances. | |||
> | |||
> Another reason is that hoisting can be optimized for different things - do you want your dependency tree to use the latest possible versions - which possibly means a more stable application? Or do you want it to optimize for the size and merge all packages together as much as possible, even if it means not installing the latest patchs? In the end, your package managers usually have to follow an heuristic to make their decisions, which might sometimes lead to accepting different tradoffs from what you would originally expect. | |||
## Making Things Right | |||
Since we now know that using the regular `dependencies` field won't always merge our dependencies with similar ones used by others packages, what shall we use instead? The short answer is **peer dependencies**. | |||
See, peer dependencies have this particular property to ensure the dependencies of your package listed here should be the exact same ones with the ones used by your parent package in the dependency tree. Even better, they also guarantee that you will always get the _exact_ same instance with them, even if we disable the hoisting! This is literally why they exist. | |||
So without further ado, this is what we should have done: | |||
```json | |||
{ | |||
"name": "my-awesome-plugin", | |||
"peerDependencies": { | |||
"react": "^16.0.0" | |||
} | |||
} | |||
``` | |||
Of course, there is a slight inconvenience: our users will now have to add `react` to their own dependencies if they haven't done so already. If they don't, then they'll get a warning, and we won't be able to access it. | |||
Still, when talking about plugins, peer dependencies are always better! They give our users full control over which version of the core libraries they want to use (here, React), and guarantee that they will be shared consistently with any package that adds on them like our plugin. React won't get duplicated multiple times in the dependency tree anymore, decreasing the bundle size in the process and avoiding confusing `instanceof` checks that fail since objects are from different instances of React. | |||
## A Simple Rule | |||
To conclude, here's the rule on whether you should use dependencies or peer dependencies: | |||
- Are you attaching yourself to something else, like a plugin? If yes, use a peer dependency for those. | |||
- Is your dependency something that you could potentially replace with something you implement yourself? If yes, it's a dependency. |
@@ -0,0 +1,132 @@ | |||
--- | |||
layout : post | |||
title : "Ease the Transition to a Monorepo with Focused Workspaces" | |||
author : Bryan Wain | |||
author_url : "https://github.com/bdwain" | |||
date : 2018-05-18 00:00:00 | |||
categories : announcements | |||
share_text : "Ease the Transition to a Monorepo with Focused Workspaces" | |||
--- | |||
[Previously](/blog/2017/08/02/introducing-workspaces/), we wrote about monorepos and how Yarn Workspaces makes working with them simpler. Unfortunately, moving to a monorepo is not always an easy choice. Without the right tooling, a monorepo can often harm the developer experience instead of help it. | |||
## The Problem With Monorepos | |||
One of the main reasons projects usually move to a monorepo is because they make it easier to make changes across multiple packages by allowing you make those changes in a single pull request. One of the ways Yarn Workspaces improves this workflow is by automatically symlinking sibling package dependencies in the node_modules folder, allowing you to immediately see the results of a change to one package when working on another. | |||
Unfortunately, while symlinking sibling packages all of the time makes cross-package development easier, it comes with a big downside when you want to work on a single package. If package `A` depends on packages `B` and `C`, you need to build all three packages just to make a change in package `A`. In a monorepo with many packages, this can be a very slow process compared to a multirepo setup, where you would just pull down a pre-built version of `B` and `C` when you install. | |||
These opposing benefits of monorepo and multirepo development often leave developers with a difficult choice. Optimize for cross-package development with a monorepo or optimize for single-package development with many repos. We feel that this is a choice you should not have to make, and our goal with focused workspaces was to help turn Yarn Workspaces into a tool that gives you the full benefits of a monorepo without forcing you to give up the benefits of multiple repos. | |||
## Introducing Focused Workspaces | |||
`yarn install --focus` is a new installation option available in **1.7.0** that shallowly installs a workspace's sibling dependencies under its `node_modules` folder. Shallow installation can best be explained with an example. | |||
Imagine you have a monorepo with packages `A` and `B`, where `A` depends on `B`, and `B` depends on `External`, which is not part of the monorepo. A normal installation might result in something like this: | |||
``` | |||
| my_project/ | |||
| package.json | |||
| node_modules/ | |||
| A/ (symlink to packages/A) | |||
| B/ (symlink to packages/B) | |||
| External/ | |||
| packages/ | |||
| A/ | |||
| node_modules/ (empty) | |||
| B/ | |||
| node_modules/ (empty) | |||
``` | |||
The problem here is that if you want to run `A`, you need to build `B` as well. If you haven't made any changes to `B`, this is most likely going to be slower than installing `B` from the registry. | |||
If you were to go to `packages/A` and run `yarn install --focus` instead, the result would look like this: | |||
``` | |||
| my_project/ | |||
| package.json | |||
| node_modules/ | |||
| A/ (symlink to packages/A) | |||
| B/ (symlink to packages/B) | |||
| External/ | |||
| packages/ | |||
| A/ | |||
| node_modules/ | |||
| B/ (not a symlink. Pulled from registry. No need to build.) | |||
| B/ | |||
| node_modules/ (empty) | |||
``` | |||
This allows you to run `A` without rebuilding `B`. Because of the way node module resolution works, when building `A`, it will find `B` in `packages/A/node_modules/B` and avoid using the symlink at the root, which points to an unbuilt version of `B`. | |||
Yarn will always do a minimal install when using `--focus`. Note that `External` is not reinstalled under `A`. There is no need because when `A` tries to resolve `External`, it will already point at the hoisted version. | |||
#### Dealing With Version Conflicts | |||
Let's look at a slightly more complicated example. Imagine now that both `A` and `B` depend on `External`, but `A` depends on `External@1.0.0` and `B` depends on `External@2.0.0`. `A` and `B` can no longer share a copy of `External` because their versions are incompatible. | |||
A regular install would produce this: | |||
``` | |||
| my_project/ | |||
| package.json | |||
| node_modules/ | |||
| A/ (symlink to packages/A) | |||
| B/ (symlink to packages/B) | |||
| External/ (v2) | |||
| packages/ | |||
| A/ | |||
| node_modules/ | |||
| External (v1) | |||
| B/ | |||
| node_modules/ (empty) | |||
``` | |||
and a focused install would produce this: | |||
``` | |||
| my_project/ | |||
| package.json | |||
| node_modules/ | |||
| A/ (symlink to packages/A) | |||
| B/ (symlink to packages/B) | |||
| External/ (v2) | |||
| packages/ | |||
| A/ | |||
| node_modules/ | |||
| External (v1) | |||
| B/ | |||
| node_modules/ | |||
| External/ (v2) | |||
| B/ | |||
| node_modules/ (empty) | |||
``` | |||
Yarn needs to not only install `B` under `A`, but also install `External` under the nested copy of `B` to ensure that `B` uses v2 of `External` while `A` still uses v1. | |||
#### Unfocusing | |||
If you want to remove the shallow installations from a focused install, just rerun `yarn install` without `--focus`. All `node_modules` will return to the state they were in before you focused. `upgrade`, `add`, and `remove` will also remove any shallow installations. | |||
You can also focus on another workspace and the original workspace will unfocus. | |||
#### Focusing by Default | |||
If you would like to have a workspace always use `--focus` when installing, you can use the [CLI arguments](/lang/en/docs/yarnrc#toc-cli-arguments) feature of `.yarnrc` files. | |||
``` | |||
--install.focus true | |||
``` | |||
If you add that to `packages/A/.yarnrc`, you will always do a focused install when you run install from `A`, but not from any other packages or the root. | |||
If you instead add it to `packages/.yarnrc`, you will do a focused install from all packages under `packages/`. | |||
## What's Next? | |||
We hope focused workspaces helps make your migration to a monorepo easier. This is only a first iteration and further improvements will likely be made in the future. If you notice anything wrong or have any suggestions on how we can improve your experience with workspaces, please let us know. | |||
## References | |||
- Original Proposal: [RFC](https://github.com/yarnpkg/rfcs/blob/master/implemented/0000-focused-workspaces.md) | |||
- Implementation PR: [#5663](https://github.com/yarnpkg/yarn/pull/5663) |
@@ -0,0 +1,85 @@ | |||
--- | |||
layout : post | |||
title : "Yarn import now uses package-lock.json" | |||
author : Aram Drevekenin | |||
author_url : "https://github.com/imsnif" | |||
date : 2018-06-04 08:00:03 | |||
categories : announcements | |||
share_text : "Yarn import now uses package-lock.json" | |||
--- | |||
For a while now, the JavaScript ecosystem is a host to a few different dependency lock file formats, including yarn’s `yarn.lock` and npm’s `package-lock.json`. | |||
We are quite excited to announce that as of `1.7.0` yarn is [able to import](https://github.com/yarnpkg/yarn/pull/5745) its dependency tree from npm’s `package-lock.json` natively, without external tools or clunky processes. | |||
This will no doubt come as great news for developers working in mixed npm/yarn environments or wanting to try yarn out on existing projects. | |||
All you need to do is issue the `yarn import` command in a repository with a `package-lock.json` file, and yarn will use the resolution information from the existing `package-lock.json` file and a corresponding `yarn.lock` file will be created. | |||
This feature is one of the first fruits of a continuing collaboration between the maintainers of the two package managers. We feel strongly about the two tools being aware of each other and providing an easy transition path between them. If you are interested or want to help, head over to [the related GitHub issue](https://github.com/yarnpkg/yarn/issues/5654). | |||
## How does it work under the hood | |||
Previously, `yarn import` would rely on a package's `node_modules` directory to determine the fixed versions to which the the new yarn.lock file needs to resolve its semver ranges. Now, it falls back to this behaviour if it cannot find a `package-lock.json` file. | |||
When it does, yarn creates a dependency tree using [npm-logical-tree](https://github.com/npm/logical-tree) from the `package.json` and `package-lock.json` in the project's root directory. It then uses the fixed versions in that tree to create its own `yarn.lock` lockfile. | |||
The resulting `yarn.lock` will have all the exact fixed versions specified in `package-lock.json`. Ready to be installed and committed in your repository. | |||
## Limitations | |||
The two lockfile formats and contents are different. Each have their own priorities, [guarantees and trade-offs in terms of determinism, consistency and more](https://yarnpkg.com/blog/2017/05/31/determinism/). Since `yarn.lock` chooses only to store the logical dependency tree, preferring to future-proof for potential physical tree and hoisting optimizations, there are certain nuances that `package-lock.json` expresses that `yarn.lock` cannot. | |||
One example would be: | |||
// package-lock.json (slightly simplified for clarity) | |||
{ | |||
"name": "nuanced-dependency-tree", | |||
"dependencies": { | |||
"a": { | |||
"version": "9.9.9", | |||
"requires": { | |||
"c": "^1.0.0" | |||
}, | |||
"dependencies": { | |||
"c": { | |||
"version": "1.0.1" | |||
} | |||
} | |||
}, | |||
"b": { | |||
"version": "8.8.8", | |||
"requires": { | |||
"c": "^1.0.0" | |||
} | |||
}, | |||
"c": { | |||
"version": "1.0.5" | |||
} | |||
} | |||
} | |||
Here, we have both packages `a` and `b` which require the same semver range of package `c`: `^1.0.0` and get different versions: `1.0.1` and `1.0.5` respectively. | |||
This would be imported to yarn as: | |||
// yarn.lock (slightly simplified for clarity) | |||
a@9.9.9 | |||
version "9.9.9" | |||
dependencies: | |||
c "^1.0.0" | |||
b@8.8.8 | |||
version "8.8.8" | |||
dependencies: | |||
c "^1.0.0" | |||
c@^1.0.0 | |||
version "1.0.5" | |||
Here `b`'s dependency `c` would change its locked version from `1.0.1` to `1.0.5` because `yarn.lock` cannot express this duplication. Yarn chooses and aims to have a single resolved version for all compatible version ranges. While in most cases such minor changes should not have much effect - we encourage you to use this feature with care. You can still override ranges if you need to, using [the selective version resolutions feature in yarn](https://yarnpkg.com/en/docs/selective-version-resolutions/). | |||
## Future plans | |||
Currently, we’re planning to add some warnings to users who use both `yarn` and `npm` in the same repository to install packages. If there’s a need, we might also try to expand this feature to other lock file formats. If you’d like to point out other issues of interoperability, or try your hand at fixing them - we encourage you to [file an issue](https://github.com/yarnpkg/yarn/issues/new) or better, [fix one](https://github.com/yarnpkg/yarn/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+label%3Ahigh-priority) by sending a PR. | |||
_We highly recommend you to delete the `package-lock.json` file if you decide to use yarn in order to avoid future confusion and possible consistency issues_. |
@@ -0,0 +1,27 @@ | |||
--- | |||
layout : post | |||
title : "Recommended security update" | |||
author : "Maël Nison" | |||
author_url : "https://twitter.com/arcanis" | |||
date : 2019-07-12 23:44:03 | |||
categories : announcements | |||
share_text : "Recommended security update" | |||
--- | |||
We've been made aware of a potential attack vector in the way some data are stored in the lockfile. We recommend to [upgrade](https://yarnpkg.com/en/docs/install) Yarn to the latest 1.17.3 release as soon as you get the chance. We also recommend you to edit your lockfiles to replace any reference to the `http:` protocol: | |||
``` | |||
$ sed -i '' 's/http:/https:/g' yarn.lock | |||
``` | |||
## What happened? | |||
The Yarn registry is just a DNS alias to the npm registry. For a few months in 2018, the npm registry [returned http urls instead of the regular https ones](https://npm.community/t/some-packages-have-dist-tarball-as-http-and-not-https/285/40). Although the problem seems to have been corrected earlier this year, the lockfile entries generated during this period may still reference http urls and cause Yarn to send unencrypted authentication data over the network. | |||
## What's the mitigation? | |||
Starting from the 1.17.3, regardless of what the registries return, we'll enforce https on the three most common hostnames: `*.yarnpkg.com`, `*.npmjs.org`, and `*.npmjs.com`. Additionally, starting from the v2, we'll rethink our default policies to make using http [require an explicit acknowledgement](https://github.com/yarnpkg/berry/issues/293) of some form. | |||
-- | |||
Thanks to [@skovorodan](https://twitter.com/skovorodan) (operating on behalf of [Exodus](https://www.exodus.io/)) for the heads-up. |
@@ -0,0 +1,86 @@ | |||
--- | |||
layout: null | |||
--- | |||
# Redirect from default Netlify subdomain | |||
https://yarnpkg.netlify.com/* https://classic.yarnpkg.com/:splat 301! | |||
# Redirect from "legacy" subdomain, as we renamed it to "classic". | |||
https://legacy.yarnpkg.com/* https://classic.yarnpkg.com/:splat 301! | |||
/:lang/packages/ /lang/:lang/packages 200 | |||
/:lang/package/* /lang/:lang/package 200 | |||
/downloads/:version/:file https://github.com/yarnpkg/yarn/releases/download/v:version/:file | |||
# Nice short URLs to download the latest release | |||
/latest.tar.gz https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_version}}/yarn-v{{site.latest_version}}.tar.gz 302 | |||
/latest.tar.gz.asc https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_version}}/yarn-v{{site.latest_version}}.tar.gz.asc 302 | |||
/latest.msi https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_version}}/yarn-{{site.latest_version}}.msi 302 | |||
/latest.deb https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_version}}/yarn_{{site.latest_version}}_all.deb 302 | |||
/latest.rpm https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_version}}/yarn-{{site.latest_version}}-1.noarch.rpm 302 | |||
/latest.rpm https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_version}}/yarn-{{site.latest_version}}-1.noarch.rpm 302 | |||
/latest.js https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_version}}/yarn-{{site.latest_version}}.js 302 | |||
# Nice short URLs for latest RC | |||
# If Netlify supported regular expressions in their rewrite rules, these could | |||
# simply be a part of the rules above. Alas, they don't support it :( | |||
/latest-rc.tar.gz https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_rc_version}}/yarn-v{{site.latest_rc_version}}.tar.gz 302 | |||
/latest-rc.tar.gz.asc https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_rc_version}}/yarn-v{{site.latest_rc_version}}.tar.gz.asc 302 | |||
/latest-rc.msi https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_rc_version}}/yarn-{{site.latest_rc_version}}.msi 302 | |||
/latest-rc.deb https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_rc_version}}/yarn_{{site.latest_rc_version}}_all.deb 302 | |||
/latest-rc.rpm https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_rc_version}}/yarn-{{site.latest_rc_version}}-1.noarch.rpm 302 | |||
/latest-rc.js https://github.com/yarnpkg/yarn/releases/download/v{{site.latest_rc_version}}/yarn-{{site.latest_rc_version}}.js 302 | |||
# Comparison page to PNPM's benchmark repo | |||
/compare https://github.com/pnpm/node-package-manager-benchmark 302 | |||
:lang/compare https://github.com/pnpm/node-package-manager-benchmark 302 | |||
lang/:lang/compare https://github.com/pnpm/node-package-manager-benchmark 302 | |||
{% capture _redirects %} | |||
{% assign urls_sorted = site.pages | map: "url" | sort %} | |||
{% assign urls_filtered = "" | split: "," | pop %} | |||
{% for url in urls_sorted %} | |||
{% assign prefix = "/lang/en/" %} | |||
{% assign prefix_size = prefix | size %} | |||
{% assign prefix_chars = url | slice: 0, prefix_size %} | |||
{% if prefix_chars == prefix %} | |||
{% assign url_stripped = url | replace: prefix, "/" %} | |||
{% assign urls_filtered = urls_filtered | push: url_stripped %} | |||
{% endif %} | |||
{% endfor %} | |||
{% assign redirectsBase = "" | split: "," | pop %} | |||
{% assign redirectsLang = "" | split: "," | pop %} | |||
{% for url in urls_filtered %} | |||
{% for language in site.data.languages %} | |||
{% if language.enabled %} | |||
{% for accept_language in language.accept_languages %} | |||
{% capture redirect %}{{url}} /{{language.tag}}{{url}} 302 Language={{accept_language}}{% endcapture %} | |||
{% assign redirectsBase = redirectsBase | push: redirect %} | |||
{% endfor %} | |||
{% capture redirect %}/{{language.tag}}{{url}} /lang/{{language.tag}}{{url}} 200{% endcapture %} | |||
{% assign redirectsLang = redirectsLang | push: redirect %} | |||
{% endif %} | |||
{% endfor %} | |||
{% endfor %} | |||
{% capture newline %} | |||
{% endcapture %} | |||
{% endcapture %} | |||
# With Language | |||
{{redirectsLang | join: newline}} | |||
# Without Language | |||
{{redirectsBase | join: newline}} | |||
# renamed commands | |||
/:language/docs/cli/ls /:language/docs/cli/list 301 | |||
/:language/docs/cli/clean /:language/docs/cli/autoclean 301 | |||
# default to english | |||
/* /en/:splat 302 |
@@ -0,0 +1,26 @@ | |||
#goog-fixurl ul { | |||
margin: 0; | |||
padding: 0; | |||
list-style: none; | |||
} | |||
#goog-fixurl form { | |||
margin-top: 1rem; | |||
@extend .form-inline; | |||
} | |||
#goog-fixurl .other-things { | |||
display: none; | |||
} | |||
#goog-fixurl input[type='text'] { | |||
@extend .form-control; | |||
@extend .float-md-left; | |||
margin-right: 0.5rem; | |||
} | |||
#goog-fixurl input[type='submit'] { | |||
@extend .btn; | |||
@include button-outline-variant($yarn-blue-darkest); | |||
color: white; | |||
} |
@@ -0,0 +1,3 @@ | |||
.badge { | |||
-webkit-font-smoothing: antialiased; | |||
} |
@@ -0,0 +1,55 @@ | |||
body { | |||
overflow-y: scroll; // Avoid reflows on vertical content change on Linux | |||
padding-bottom: 3rem; | |||
color: #5a5a5a; | |||
} | |||
a code { | |||
color: $brand-primary; | |||
text-decoration: underline; | |||
} | |||
svg { | |||
vertical-align: top; | |||
} | |||
.logo-primary { | |||
fill: #2c8ebb; | |||
} | |||
.logo-secondary { | |||
fill: #ffffff; | |||
} | |||
.collapse.in { | |||
display: block; | |||
} | |||
.searching { | |||
main { | |||
display: none; | |||
} | |||
.ais-SearchBox-reset { | |||
visibility: initial; | |||
} | |||
} | |||
@mixin placeholder { | |||
&::-webkit-input-placeholder { | |||
@content; | |||
} | |||
&:-moz-placeholder { | |||
@content; | |||
} | |||
&::-moz-placeholder { | |||
@content; | |||
} | |||
&:-ms-input-placeholder { | |||
@content; | |||
} | |||
} | |||
// prettier-ignore | |||
$image-border-yarn-blue: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAAOCAYAAACPUsFyAAAKxklEQVRoge2b63OdVRXGfyR5c6G5kNL0ArSl3KyggFLFis4weBkVP8g/4Gf/FP4L/eAnv+gHpjqKDjOCxQG00AEKvQxNgKYXCE3SnnNykuqH9TyzV96eND1pDhTtmsmc877v3muv9ay197v3ybNu2/f8oTFgDGgS0gSGgTawQpElffbpc0ifbjMKDKT2TWAb0FLbC7o/ACwn/e43CiwCFdAARtQmy7L+hoEF3atkf0N/vtfW9SDQL32N9Myyoucrqd0AcK7mf136ExbtdM/jVbq3mNr063tfzYasy3jcQcGsmWxb1vWS+gyk8cbSWMv6br+NefbZ39HzIV37+xjwma6vJHsHZZ9zpQnMsz5eI8nOBdnT0vMryS77tSw8jEH22Tpb6mvcLbZlKX13fPK9TlJpjGEifh7PPvu6IT2eGyup76jsr+OScc95N1i7dp52ymWLx4fA9h7Zm+eSY76c+i1S5jiUGAxR8t99PqVgbr0kn7OshaltdvwhcOkbAB4Bfgn8FXgdOAA8DbxKTMJFDfYZ8DiRPEeBXcA3gWlgCnhCA40Cx+TcU8BZYDfwW+BD9d8FfCJDvgtsAe4CPhYIJ4H7KaDZWS9aO4ATsgngXtl5DPi32p3TszG1n0rXAI8RoB6V/lPqd5favC5bthJBuMDqCTwM7AXuA97T/a8ADwNzArgtXJ3E/Wl82w4l4bYBP5aePcAf1W8PcDdwEfg78AYl0QZlQ0XEbSuB+SwxYYbl20tqPwQcpywEFfB1YL/wu114/AH4GnBefiwQcRuSnffL9gvABPA28BdKgtXlCpGA3yfy4jUC+1FiApwlYtYn/Q8CZ4DvEHk3CxxS+3H1McZLum7I5y3AJWExLds9sXcJOy+stg3KwvqgbFggYrtLYw4Loy1qf4nykkDPDwA7dT1AxP+8no0RuZQx8ssFygt5hMhpiNgcJfIS4Bki/kek6wMiJgeJPBjR5zARy5Z0niXy4KL+PhI2o/KhKbvuE94DBP6vC4cxYs405d/t8uuoxviU1S+Tvg6fW4ncHpYNEwPAz4ggnyES43E5P0UsHN7BvAVMEon3KvAAEZRBYtKNaPB+4Hu6P04kyTzwDfW5G/iRDDijvuNyuEEsPvuAr1J2WVeIRJvVvTsVCCiLU5NIqleAGQHbls2LstuTHCLRZgRsv+zYn0B8TH4gm5rJhnkiUE318cI0SllgnKgTRPLMyZaPhe35hJntHAMeFXa75M+AsNipsYeF5TQl0X6iT+PQErYLwEO6/3ONPQq8QCyetuk5jTVFvJCGiThV6vMQMVkP6NlxYpJOJh1zwJOUhSG/RceJJL9bPnqR/pbG+CHwsvSitgeF3T0af5qI2U4i/uf03G9kL7jvSO/ZhMcikTeniXidkC2npW8+xWK3xvxQtj0JbCcWou1E/kyoreN9Qd8n5cscEd9pYhGfTe0XNcZMsulOPZ8k8m63rv0SPUjJgUEiRr8g5uS0+rV0/wTwbPIL4LJseCrpnQcOEzE/QczNaSJWO4k4P6y/Sj4doOxIjVWTeLl9RonxaSK/7NtFYsGqNN5eIr/337bv+UMXNNg8MWG8hfR211tjb+tHdK+ibDkdPIu3jv6+RAlypXE8aatavyU5Nd7hWT5W1I9nsPqY0a/vPu4Mc/W218fOirL1b6dnFauPHhbvgKBs6S0+Srmfk78luy+pTz7y5COb9Q2qrXWO6H7eNhuH2ykTvh436/UiXhGxyOMYL8uQ2tju0dTXukl9lzX+POVYZN8sS5S4D6f7OSYNSu55l+Hccmxtp2Obj+s+MrmPMbB/blv/bLH6WOc8qIgYuG09blDwdb4NcfWbv0WJhedOzgtPauNZ93eFq+eY2/iFaTs8d3yvfgQzvj7+2rfsk+OZMXNbHxfzkc65ldsOsRrn+s8FbaDvtn3PH/pPB8duyS25Jbek51L/MbQX0mv93cqN2LORvp36tNd5/r8o1/Kz/qy9xv3NGq+bNmv16bbvlyHOnXzrqZ99rP2j4YaV1uR69Pdasv0bsafTcfFGxstb45sBH+guxhtJyk5+1jHI12v1uR6b6j8BXGvcbn2pap/Xa9NmxbmXL69Ovl0Lx2vpuC7pW79J90pvQrlR+7vtfz3tbzZMu7Fns/Co3++U+BsZYz09NzJON9IL3Z+X7dcad9PkehegL5N8UVvdGz2e9XIH0q2eXh8vvgzHkS9a/i8w6mYB2sgEWe/3j82Qur7NWKm7XRi6PS5Y1nubrWXHRo4mnX5nWWvMnm67N9C+F1LHZTNzdTN+K7yZMOpFnzZcvQBdq/NGtujrbX83Q26GbW6vkmUzttv1WKz329MXtcX/vKWOy2bm6o2+jG4W6aUfFRTuhrkJZhqbel/nSuT/7ZsTYjG/wHyO3NY66xT6zEGoWM0vcNv8Q+E8hUPie0OpbZYGhUNR1dqYt1PnYmTeUD9rczrMgcp22CfjYN/MfTEHpF76sBY3JfNXjHNDdpqj5LaOHx38dXlK5h+Zz5FLMkh63dZS5/aMszqeuUwn840yvmYvW3KphDlJ46lvLnMxBi65MH6XEw4mrTqPGhS+0pbaONZr/3PctiR9maNT53dR05cZzVnqPLScB86dtThqmTeUeUed8t04oOd1Dp3nzmCHZ+5/icLnGa+17bRjblDik3lZHr8eN3ObLkl/c0AXxwjm7GkK9f4ywfj8hMJq3EtQuLcRrNOn5dRMMmqRoG9PSs8u3W9SWMR7KOzL8wTTc5xgmrqt6d7o2Szwa/V5m0iQOel6VG0eIhJiFniXwjC13hngTfUj+daisFNzvctOAbmHYHBOAi+q/yKFru/2FwkW+TmCcn5OY7cp7Fpj5SDPJlwfIGJhm0YpC+4OonzhYYJ1arr/qMbdLn9mCWb0HuB9jXmRYL2aBf4gwfJ+UbbNESzZt+SPY9yJ0TwE/JSrWdDHpbdJxH9A49+lMZ7Ts7cJVvodwO+TjtMEW3tRffZq3Pcp5TtDrGZ8/0mfp+TvNoLJb9b4u7L720R+LyUf5ygTqkFhp9dZ6weJObJfbXO8KyJeLncxi7jSPZervEzEH/lwTP6syO7dlNIcl/ycJGJrO82gfopSYgNlQa6I+LeSDV40W8Ix2+Q6tXdkywRRSvOR8H5W/riawS8IL4QN9TXJ+ARRUXFSNj9KeWnNAD8gykaO6PlzwAsDwK9QXQYRzPeIYK1Vz2EQtwK/IWpVpih072Fikrt+5IAcXNbg71OKGLcJ2MMEhXtCf/cRE85v0MsUSv1hItiH5eC9suVxoh7tb7LxY0odDXK+IR259iazctvStaDxpgTca3o+S0wuM6jtQ702yJPwDLEQma5/nEi0emGvi09H1cdvwTEiQT+l1Df9jtjBDFHeUF4Y5il1TjuF96vC/7hsuEIsVjvUZ1E4TQL/JGrNXIDYSdpq9wiRN17UTkq/y0BcZzYlLD6SvZeJiX6MiE2bUph6Wv0XiZeby13szw7Z9pLanxKWrot7Qu0nKLVOy8J2msjtP1MmeWZq36HPhXTfi3tFTLZJfebav1OyO++AKuHiOsIWMQ8WiLys5Nug7r2pP4+PMDqr6xEiZhVR67hftn2QbH9A7euseoj8cn3lEUrd5SXgH2pzJ6V85x4ivjME5q9I535hvULUo50n8vNf6vuG+h+RTYOyc7vaviTsvCYc+i9pQJWwuXILrwAAAABJRU5ErkJggg==); | |||
// prettier-ignore | |||
$image-border-white: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAAOCAMAAAC4jDFAAAAAnFBMVEX///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9FaQsiAAAAM3RSTlP69O/s8QoN9hIGBOjmfQEadiYfgnFTFr6TbGjORo6GpWJeuJuKOCzGsaqeMdeWP6HeWU5mrKx6AAAFQklEQVR4ATyMBXIEUAxCazAkFeavcP+jlmpc3wMtQQSwO8MWsa1zOVJExMeXXFyJTOqSBi6YJhLVeD9+pIgGUGpR4M9IKQyMsIMSLk4uEYkahjVjdheW9c/qpTizRWD0/QzOgiSwPcDf6UADWAmx2zbflJZVEFT1+7FjHB9f7CQQOcPYnywRBbYjKTDuTONu7W65/+GW/LcFlDs8Jecp6w/+L5/qP17PQz0zbP23bhC3DC2U6XLoCShWES1T+y6HYggkrGtZzrL3EM5VHBt+y0ZrGSN+Z2NjGUY1+g38GFnTMKDynadhsuya8AMp2WlVhXlPrTVfJoOD3qmmXjLcjLnPdXzYscH1wiCM1P57/sGLM7c39Zi9ntmCcgeb2i5jgzBHI6qzNDZKa33+1rowi2v7ReOIHtfrw+Z5xNcHThM+fn/A8bKoZd9LXaPnzWoqyzD0kkZWZyngUWvF8AN/ba4Zwr8/+TvYfpfvgRjgxFYWXVGVwveFeF8ux8/5cN6kXz+TeN8PF9Oe7vfjadou5/PnsxkJCXAk9EKYTs4gDAMRopQO6or0IXRCzKsmlGigKVWgKsS0BvMxQ6XcvKUMk+imqRj6HChgRNkaQLSrRLu2UzoAaqU0jCkQODmVEMayb8/GD13ZD4Of1VpVBILcR5DDVML1TgKpSKQUVJDIqe1SM5BAmZwoJZ0xRZEaOLdFpQBcwXZ/v7c2aXvqo9fQzTMgNHRdZ1oPU9YUFhX1/7FXFVl2wwBM9J4W9n6Wc4jc/26NM20HP5ZBIZMgDvjxYcKcKVjGI2K67cC+c4obsGr0cWk3LDShYoahHTZ0tGASJFtyaGR6yCJBSAGlEReHW0txetqSOjQolgQKa8j2jEmGGXsxQEMqI+ZIZGse27AVZy960RKqLCtHXLrEwQ0WKpEGNqAFNbJIw0soRFuu4lIeFhVs/3EWwKmOa8hnqvh8wv3Rni94Z3XCE7fdwTPtJPO5jHvyn1K9cZpPa6zz+/g4+QBOGl9+XrjnRfkPfH8STj80nBh88v3+pbd5ACeKOC53vLurcHrEWws8d94FfBwVt0zOacZzyl8EbL8/EFEe8/Piz7ZgVtHD1sgQ0QIbYFIqFsKsAjk0qFhlWTFZJE/NOLKX0NpsJYPOiHhwk+kRhqT34qLJKECWWrqVZE9Pim1YAnWGREQCQepwy0rBliRIayFmytgMQ0dMioWtPV4L87hgoya/vMQNleHDrZk05sPjJxLrQ0WCEAYA6F6vAUEh4wS7YEGn/f/HXXaPbu/mWaW9YjyCF7ZQrdUpCXMyjK23qkWicvz8fBywC9A5lpdfcpFzq1TcjLkqhTBLLWORIoNSXmqGLfNWqVVwr5YryZx3PW3vLdVu6aW05IVIYzEQM/h/O8MD0nvBbFYJVBISaAitxzEqjrvBp9G5E09gH8gNNQ8BD7TvE7E612ReeTIJUfbuYC6VEJuTBtjVxD0wJY9x3/HnQq+3jx8cv7tdjpcbHwSfIcZmMeVYYzrQAMnbtf21XB84DMMgAAA1uheGhiyRHeiw4sr5/9+67xc3C/3zsV0RctnJTBviotTNcoOg3Cb57d4MeVLKaotaDjGGIgRKP8/ZbDYr2iyp7Tr5POjzoVmUhAN8/7sEfLcJFD2mhOaDqLmqwwDbHXi1WPF7il8nSVrnOCiZl01bdnXnjSCoFVXtqibn2RAwTs1YlNe8wG+yAT7Djl1x/DzQf/a1QUViMc+xPjOLeFil5ttxcq6+5dMwPB7vYL6bmcdYsLTZoz+eb/eh5CQgS4qmcy3Ek74AfwYQ++m3uxQAAAAASUVORK5CYII=); |
@@ -0,0 +1,25 @@ | |||
a.card { | |||
display: block; | |||
color: inherit; | |||
text-decoration: none; | |||
&:hover { | |||
border-color: rgba(0, 0, 0, 0.2); | |||
} | |||
&:hover .text-primary { | |||
text-decoration: underline; | |||
} | |||
} | |||
.guide-card { | |||
.card-text { | |||
@include media-breakpoint-up('lg') { | |||
min-height: $line-height-base * $font-size-base * 5; | |||
} | |||
@include media-breakpoint-up('xl') { | |||
min-height: $line-height-base * $font-size-base * 4; | |||
} | |||
} | |||
} |
@@ -0,0 +1,307 @@ | |||
.rougeHighlight pre, | |||
.rougeHighlight, | |||
pre { | |||
background: $gray-dark; | |||
-webkit-font-smoothing: antialiased; | |||
border-left: 4px solid $yarn-blue; | |||
color: #ddd; | |||
} | |||
.rougeHighlight .hll { | |||
background-color: #272822; | |||
} | |||
/* Comment */ | |||
.rougeHighlight .c { | |||
color: #75715e; | |||
} | |||
/* Error */ | |||
.rougeHighlight .err { | |||
color: #960050; | |||
background-color: #1e0010; | |||
} | |||
/* Keyword */ | |||
.rougeHighlight .k { | |||
color: #66d9ef; | |||
} | |||
/* Literal */ | |||
.rougeHighlight .l { | |||
color: #ae81ff; | |||
} | |||
/* Name */ | |||
.rougeHighlight .n { | |||
color: #f8f8f2; | |||
} | |||
/* Operator */ | |||
.rougeHighlight .o { | |||
color: #f92672; | |||
} | |||
/* Punctuation */ | |||
.rougeHighlight .p { | |||
color: #f8f8f2; | |||
} | |||
/* Comment.Multiline */ | |||
.rougeHighlight .cm { | |||
color: #75715e; | |||
} | |||
/* Comment.Preproc */ | |||
.rougeHighlight .cp { | |||
color: #75715e; | |||
} | |||
/* Comment.Single */ | |||
.rougeHighlight .c1 { | |||
color: #75715e; | |||
} | |||
/* Comment.Special */ | |||
.rougeHighlight .cs { | |||
color: #75715e; | |||
} | |||
/* Generic.Emph */ | |||
.rougeHighlight .ge { | |||
font-style: italic; | |||
} | |||
/* Generic.Strong */ | |||
.rougeHighlight .gs { | |||
font-weight: bold; | |||
} | |||
/* Keyword.Constant */ | |||
.rougeHighlight .kc { | |||
color: #66d9ef; | |||
} | |||
/* Keyword.Declaration */ | |||
.rougeHighlight .kd { | |||
color: #66d9ef; | |||
} | |||
/* Keyword.Namespace */ | |||
.rougeHighlight .kn { | |||
color: #f92672; | |||
} | |||
/* Keyword.Pseudo */ | |||
.rougeHighlight .kp { | |||
color: #66d9ef; | |||
} | |||
/* Keyword.Reserved */ | |||
.rougeHighlight .kr { | |||
color: #66d9ef; | |||
} | |||
/* Keyword.Type */ | |||
.rougeHighlight .kt { | |||
color: #66d9ef; | |||
} | |||
/* Literal.Date */ | |||
.rougeHighlight .ld { | |||
color: #e6db74; | |||
} | |||
/* Literal.Number */ | |||
.rougeHighlight .m { | |||
color: #ae81ff; | |||
} | |||
/* Literal.String */ | |||
.rougeHighlight .s { | |||
color: #e6db74; | |||
} | |||
/* Name.Attribute */ | |||
.rougeHighlight .na { | |||
color: #a6e22e; | |||
} | |||
/* Name.Builtin */ | |||
.rougeHighlight .nb { | |||
color: #f8f8f2; | |||
} | |||
/* Name.Class */ | |||
.rougeHighlight .nc { | |||
color: #a6e22e; | |||
} | |||
/* Name.Constant */ | |||
.rougeHighlight .no { | |||
color: #66d9ef; | |||
} | |||
/* Name.Decorator */ | |||
.rougeHighlight .nd { | |||
color: #a6e22e; | |||
} | |||
/* Name.Entity */ | |||
.rougeHighlight .ni { | |||
color: #f8f8f2; | |||
} | |||
/* Name.Exception */ | |||
.rougeHighlight .ne { | |||
color: #a6e22e; | |||
} | |||
/* Name.Function */ | |||
.rougeHighlight .nf { | |||
color: #a6e22e; | |||
} | |||
/* Name.Label */ | |||
.rougeHighlight .nl { | |||
color: #f8f8f2; | |||
} | |||
/* Name.Namespace */ | |||
.rougeHighlight .nn { | |||
color: #f8f8f2; | |||
} | |||
/* Name.Other */ | |||
.rougeHighlight .nx { | |||
color: #a6e22e; | |||
} | |||
/* Name.Property */ | |||
.rougeHighlight .py { | |||
color: #f8f8f2; | |||
} | |||
/* Name.Tag */ | |||
.rougeHighlight .nt { | |||
color: #f92672; | |||
} | |||
/* Name.Variable */ | |||
.rougeHighlight .nv { | |||
color: #f8f8f2; | |||
} | |||
/* Operator.Word */ | |||
.rougeHighlight .ow { | |||
color: #f92672; | |||
} | |||
/* Text.Whitespace */ | |||
.rougeHighlight .w { | |||
color: #f8f8f2; | |||
} | |||
/* Literal.Number.Float */ | |||
.rougeHighlight .mf { | |||
color: #ae81ff; | |||
} | |||
/* Literal.Number.Hex */ | |||
.rougeHighlight .mh { | |||
color: #ae81ff; | |||
} | |||
/* Literal.Number.Integer */ | |||
.rougeHighlight .mi { | |||
color: #ae81ff; | |||
} | |||
/* Literal.Number.Oct */ | |||
.rougeHighlight .mo { | |||
color: #ae81ff; | |||
} | |||
/* Literal.String.Backtick */ | |||
.rougeHighlight .sb { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Char */ | |||
.rougeHighlight .sc { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Doc */ | |||
.rougeHighlight .sd { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Double */ | |||
.rougeHighlight .s2 { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Escape */ | |||
.rougeHighlight .se { | |||
color: #ae81ff; | |||
} | |||
/* Literal.String.Heredoc */ | |||
.rougeHighlight .sh { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Interpol */ | |||
.rougeHighlight .si { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Other */ | |||
.rougeHighlight .sx { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Regex */ | |||
.rougeHighlight .sr { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Single */ | |||
.rougeHighlight .s1 { | |||
color: #e6db74; | |||
} | |||
/* Literal.String.Symbol */ | |||
.rougeHighlight .ss { | |||
color: #e6db74; | |||
} | |||
/* Name.Builtin.Pseudo */ | |||
.rougeHighlight .bp { | |||
color: #f8f8f2; | |||
} | |||
/* Name.Variable.Class */ | |||
.rougeHighlight .vc { | |||
color: #f8f8f2; | |||
} | |||
/* Name.Variable.Global */ | |||
.rougeHighlight .vg { | |||
color: #f8f8f2; | |||
} | |||
/* Name.Variable.Instance */ | |||
.rougeHighlight .vi { | |||
color: #f8f8f2; | |||
} | |||
/* Literal.Number.Integer.Long */ | |||
.rougeHighlight .il { | |||
color: #ae81ff; | |||
} | |||
/* Generic.Subheading & Diff Unified/Comment? */ | |||
.rougeHighlight .gu { | |||
color: #75715e; | |||
} | |||
/* Generic.Deleted & Diff Deleted */ | |||
.rougeHighlight .gd { | |||
color: #f92672; | |||
} | |||
/* Generic.Inserted & Diff Inserted */ | |||
.rougeHighlight .gi { | |||
color: #a6e22e; | |||
} | |||
.language-diff .gi { | |||
color: $brand-success; | |||
background: lighten($brand-success, 37%); | |||
display: block; | |||
} | |||
.language-diff .gd { | |||
color: $brand-danger; | |||
background: lighten($brand-danger, 37%); | |||
display: block; | |||
} | |||
pre { | |||
max-width: 100%; | |||
margin-bottom: 1.2rem; | |||
border-radius: 3px; | |||
padding: 0.6rem 1.2rem; | |||
overflow-x: auto; | |||
-webkit-overflow-scrolling: touch; | |||
table { | |||
background: none; | |||
border: none; | |||
tbody { | |||
tr { | |||
background: none; | |||
display: flex; | |||
flex-flow: row nowrap; | |||
td { | |||
display: block; | |||
flex: 1 1; | |||
&.gutter { | |||
max-width: 40px; | |||
padding-left: 1rem; | |||
padding-right: 1rem; | |||
pre { | |||
max-width: 20px; | |||
color: $gray-light; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
.copyable { | |||
display: flex; | |||
width: 100%; | |||
background-color: $gray-lighter; | |||
padding: 1em; | |||
margin: 0.5em 0 0.5em; | |||
&--content { | |||
flex-grow: 1; | |||
background: none; | |||
color: #666666; | |||
min-width: 0; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
white-space: nowrap; | |||
} | |||
&--button { | |||
background: none; | |||
border: none; | |||
border-radius: 0; | |||
color: #666666; | |||
white-space: nowrap; | |||
cursor: pointer; | |||
&:focus { | |||
outline: none; | |||
} | |||
&__img { | |||
height: 1em; | |||
width: 1em; | |||
margin-right: 0.3em; | |||
} | |||
} | |||
} |
@@ -0,0 +1,244 @@ | |||
.details { | |||
&-links { | |||
&--link { | |||
color: #666666; | |||
padding: 0.5em 1em; | |||
margin: 0.2em; | |||
border-radius: 0.2em; | |||
border: 1px solid #cbcbcb; | |||
display: block; | |||
font-size: 1em; | |||
overflow: hidden; | |||
white-space: nowrap; | |||
text-overflow: ellipsis; | |||
img { | |||
margin-right: 0.2em; | |||
height: 1em; | |||
width: 2em; | |||
opacity: 0.5; | |||
} | |||
.copyable { | |||
background-color: inherit; | |||
padding: 0; | |||
margin: 0; | |||
width: calc(100% - 2.2em); | |||
a { | |||
color: #666666; | |||
} | |||
&--button { | |||
padding: 0; | |||
} | |||
} | |||
&__yarn { | |||
display: flex; | |||
align-items: center; | |||
} | |||
} | |||
} | |||
&-main { | |||
overflow: auto; | |||
} | |||
@include media-breakpoint-up(lg) { | |||
&-side { | |||
border-left: 1px solid #cbcbcb; | |||
} | |||
} | |||
&-doc { | |||
margin: 2em; | |||
margin-right: 0; | |||
&--title { | |||
display: inline-block; | |||
color: #666; | |||
border-bottom: dotted 1px; | |||
position: relative; | |||
a { | |||
color: inherit; | |||
text-decoration: none; | |||
} | |||
&:before { | |||
content: ''; | |||
position: absolute; | |||
left: -1em; | |||
top: 0.4em; | |||
width: 0.8em; | |||
height: 0.8em; | |||
background: { | |||
repeat: no-repeat; | |||
size: contain; | |||
} | |||
} | |||
&__readme:before { | |||
background-image: url('/assets/detail/ico-readme.svg'); | |||
} | |||
&__changelog:before { | |||
background-image: url('/assets/detail/ico-changelog.svg'); | |||
} | |||
} | |||
&--content { | |||
h1 { | |||
@extend .h4; | |||
} | |||
h2 { | |||
@extend .h5; | |||
} | |||
h3 { | |||
@extend .h6; | |||
} | |||
h4 { | |||
@extend .h6; | |||
} | |||
h5 { | |||
@extend .h6; | |||
} | |||
h6 { | |||
@extend .h6; | |||
} | |||
blockquote { | |||
@extend .blockquote; | |||
} | |||
table { | |||
@extend .table; | |||
} | |||
} | |||
} | |||
&-side { | |||
dt { | |||
font-weight: normal; | |||
align-self: flex-end; | |||
} | |||
dd { | |||
margin-bottom: 0; | |||
font-weight: bold; | |||
} | |||
dl div { | |||
margin-bottom: 0.5em; | |||
img { | |||
height: 1.4em; | |||
width: 1.4em; | |||
margin-right: 0.4em; | |||
align-self: center; | |||
} | |||
} | |||
h1 { | |||
font-size: 1.2em; | |||
} | |||
article { | |||
margin-bottom: 2em; | |||
} | |||
&--runkit { | |||
&:after { | |||
content: ''; | |||
width: 1em; | |||
height: 1em; | |||
background-size: contain; | |||
background-image: url('/assets/detail/ico-runkit.svg'); | |||
} | |||
} | |||
&--cdns { | |||
dd { | |||
text-overflow: ellipsis; | |||
white-space: nowrap; | |||
overflow: hidden; | |||
} | |||
} | |||
} | |||
&-files { | |||
&__list { | |||
list-style-type: none; | |||
padding-left: 0; | |||
// Add padding for subdirectories | |||
& & { | |||
padding-left: 1.5em; | |||
} | |||
li { | |||
padding: 0.2em; | |||
&:hover { | |||
background-color: #eceeef; | |||
border-radius: 0.2em; | |||
ul { | |||
background-color: white; | |||
border-radius: 0.2em; | |||
} | |||
} | |||
} | |||
} | |||
// File and directory names | |||
&__dirname, | |||
&__filename { | |||
line-height: 2em; | |||
} | |||
&__dirname::before, | |||
&__filename::before { | |||
content: ''; | |||
display: inline-block; | |||
height: 22px; | |||
margin-right: 4px; | |||
width: 22px; | |||
vertical-align: middle; | |||
} | |||
&__dirname::before { | |||
background-image: url(/assets/detail/ico-folder.svg); | |||
} | |||
&__filename::before { | |||
background-image: url(/assets/detail/ico-file.svg); | |||
} | |||
// Expand animation for subdirectories | |||
&-enter { | |||
max-height: 0; | |||
opacity: 0; | |||
transition: all 200ms ease-in; | |||
&-active { | |||
max-height: 500px; | |||
opacity: 1; | |||
} | |||
} | |||
// Collapse animation for subdirectories | |||
&-leave { | |||
max-height: 500px; | |||
opacity: 1; | |||
transition: all 200ms ease-out; | |||
&-active { | |||
max-height: 0; | |||
opacity: 0; | |||
} | |||
} | |||
} | |||
} | |||
.dotted { | |||
border-bottom: 1px dotted #666; | |||
margin: 0 0.5em 5px 0.5em; | |||
} | |||
.flex-grow { | |||
flex-grow: 1; | |||
} |
@@ -0,0 +1,17 @@ | |||
/** Algolia DocSearch **/ | |||
.search-input { | |||
padding: 1rem 1.5rem 1rem 2.5rem; | |||
background: white url('/assets/search.svg') no-repeat 1rem center; | |||
background-size: 1rem 1rem; | |||
} | |||
.algolia-autocomplete { | |||
width: 100%; | |||
.ds-dropdown-menu { | |||
min-width: unset !important; | |||
max-width: unset !important; | |||
width: 100% !important; | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
.feature { | |||
margin: 3rem 0; | |||
} | |||
.feature-image[width] { | |||
max-width: attr(width); | |||
} | |||
.feature-divider { | |||
margin: 3rem 0; | |||
} | |||
.feature-heading { | |||
font-size: 2.4rem; | |||
font-weight: 300; | |||
line-height: 1; | |||
letter-spacing: -0.05rem; | |||
margin-bottom: 1rem; | |||
span { | |||
display: inline-block; | |||
} | |||
} | |||
.feature-text { | |||
font-weight: 300; | |||
font-size: 1.1rem; | |||
color: $text-muted-er; | |||
} | |||
@include media-breakpoint-up('sm') { | |||
.feature-text { | |||
font-size: 1.3rem; | |||
} | |||
} | |||
@include media-breakpoint-down('md') { | |||
.feature-image { | |||
max-height: 340px; | |||
width: auto; | |||
} | |||
} | |||
@include media-breakpoint-up('md') { | |||
.feature-heading { | |||
font-size: 2.6rem; | |||
} | |||
.feature-image-speed { | |||
margin-bottom: -30px; | |||
} | |||
.feature-image-secure { | |||
margin-bottom: -30px; | |||
} | |||
} | |||
@include media-breakpoint-up('lg') { | |||
.feature-heading { | |||
font-size: 2.8rem; | |||
} | |||
.feature-image-secure { | |||
margin-top: -60px; | |||
margin-bottom: -60px; | |||
} | |||
} | |||
@include media-breakpoint-up('xl') { | |||
.feature-heading { | |||
margin-top: 3.5rem; | |||
} | |||
.feature-divider { | |||
margin: 5rem 0; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
.featurette { | |||
margin-top: 2rem; | |||
margin-bottom: 2rem; | |||
h2 { | |||
color: $gray; | |||
font-weight: 300; | |||
} | |||
p { | |||
color: $text-muted-er; | |||
font-weight: 300; | |||
font-size: 1.1rem; | |||
} | |||
} | |||
@include media-breakpoint-up('md') { | |||
.featurette { | |||
height: 8rem; | |||
margin-top: 0; | |||
margin-bottom: 3rem; | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
.footer { | |||
} | |||
.footer-divider { | |||
margin: 3rem 0; | |||
} | |||
@include media-breakpoint-down('xs') { | |||
.footer-right { | |||
margin-top: 1rem; | |||
padding-top: 1rem; | |||
border-top: 1px solid $gray-lighter; | |||
} | |||
} | |||
@include media-breakpoint-up('sm') { | |||
.footer-left { | |||
float: left; | |||
} | |||
.footer-right { | |||
float: right; | |||
text-align: right; | |||
} | |||
} | |||
@include media-breakpoint-down('md') { | |||
.footer-item { | |||
display: block; | |||
} | |||
} | |||
@include media-breakpoint-up('lg') { | |||
.footer-item + .footer-item::before { | |||
padding-left: 0.25em; | |||
padding-right: 0.25em; | |||
content: ' · '; | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
.guide-content, | |||
.blog-content { | |||
line-height: 1.7; | |||
p { | |||
margin-bottom: 1.2rem; | |||
} | |||
h1, | |||
h2, | |||
h3, | |||
h4, | |||
h5, | |||
h6, | |||
.h1, | |||
.h2, | |||
.h3, | |||
.h4, | |||
.h5, | |||
.h6 { | |||
margin-top: 2rem; | |||
margin-bottom: 1.2rem; | |||
code { | |||
background: $yarn-blue; | |||
color: $code-bg; | |||
font-weight: 600; | |||
-webkit-font-smoothing: antialiased; | |||
} | |||
} | |||
blockquote { | |||
margin-bottom: 1.2rem; | |||
padding: 1.2rem 1.2rem 0; | |||
border: 1px solid $gray-lighter; | |||
background: $gray-lightest; | |||
border-left: $border-radius solid $gray-light; | |||
border-radius: $border-radius; | |||
code { | |||
background-color: $gray-lighter; | |||
} | |||
} | |||
table { | |||
@extend .table; | |||
} | |||
.list-group-item-text { | |||
margin-bottom: 0; | |||
} | |||
.list-group-item-heading { | |||
margin-top: 0; | |||
margin-bottom: $list-group-item-heading-margin-bottom; | |||
} | |||
} | |||
.guide-controls { | |||
margin-top: $spacer-x; | |||
margin-bottom: $spacer-x; | |||
} |
@@ -0,0 +1,198 @@ | |||
.hero { | |||
position: relative; | |||
margin: 0; | |||
padding: 2rem 0; | |||
color: white; | |||
background-color: $yarn-blue-dark; | |||
background-size: 25px auto; | |||
-webkit-font-smoothing: antialiased; | |||
overflow: hidden; | |||
a { | |||
color: white; | |||
text-decoration: underline; | |||
} | |||
.alert { | |||
// Pull alert back into the margin on top of the hero, when visible. | |||
margin-top: -1.5rem; | |||
} | |||
} | |||
.hero::after { | |||
content: ''; | |||
display: block; | |||
position: absolute; | |||
z-index: 1; | |||
left: 0; | |||
right: 0; | |||
height: 7px; | |||
background: { | |||
image: $image-border-white; | |||
repeat: repeat-x; | |||
size: 144px auto; | |||
} | |||
} | |||
.hero::before { | |||
top: 0; | |||
background-position: top -4px center; | |||
} | |||
.hero::after { | |||
bottom: 0; | |||
background-position: bottom -4px center; | |||
} | |||
.navbar + .hero { | |||
margin-top: 0; | |||
} | |||
.hero-logo { | |||
position: absolute; | |||
top: 50%; | |||
right: -3.5rem; | |||
transform: translate(0, -50%); | |||
height: 140%; | |||
} | |||
.hero-logo .logo-primary { | |||
fill: $yarn-blue-darker; | |||
} | |||
.hero-logo .logo-secondary { | |||
fill: $yarn-blue; | |||
} | |||
.hero-title { | |||
position: relative; | |||
margin-bottom: 2.25rem; | |||
z-index: 1; | |||
font-size: 2.5em; | |||
font-weight: 700; | |||
text-transform: uppercase; | |||
text-shadow: 2px 3px $yarn-blue-darkest; | |||
} | |||
.hero-text { | |||
text-shadow: 2px 3px $yarn-blue-darkest; | |||
} | |||
.hero-prompt { | |||
text-align: center; | |||
font-size: 1.8rem; | |||
font-weight: 800; | |||
text-transform: uppercase; | |||
text-shadow: 2px 3px $yarn-blue-darkest; | |||
margin-bottom: 1rem; | |||
} | |||
.hero-btn { | |||
@include button-outline-variant($yarn-blue-darkest); | |||
@include button-size( | |||
$btn-padding-y-lg * 1.5, | |||
$btn-padding-x-lg * 1.5, | |||
$font-size-lg * 0.8, | |||
$btn-border-radius-lg | |||
); | |||
font-size: 1.2rem; | |||
color: white; | |||
border-width: 2px; | |||
text-transform: uppercase; | |||
font-weight: 700; | |||
letter-spacing: 0.03rem; | |||
@include hover { | |||
color: $yarn-blue-dark; | |||
background-color: white; | |||
border-color: white; | |||
} | |||
} | |||
.hero a.hero-btn { | |||
text-decoration: none; | |||
} | |||
.hero-ghbtn { | |||
display: inline-block; | |||
vertical-align: middle; | |||
} | |||
@include media-breakpoint-down('sm') { | |||
.hero-btn { | |||
display: block; | |||
width: 100%; | |||
} | |||
.hero-btn + .hero-btn { | |||
margin-top: 1rem; | |||
} | |||
} | |||
@include media-breakpoint-up('sm') { | |||
.hero { | |||
margin: 0 0 3rem 0; | |||
} | |||
.hero-title { | |||
font-size: 2.8em; | |||
text-shadow: 5px 5px $yarn-blue-darkest; | |||
} | |||
.hero-text { | |||
text-shadow: 5px 5px $yarn-blue-darkest; | |||
} | |||
.hero-prompt { | |||
font-size: 2.8rem; | |||
margin: 0 0 1.3rem; | |||
text-shadow: 5px 5px $yarn-blue-darkest; | |||
} | |||
.hero-btn + .hero-btn { | |||
margin-left: 1rem; | |||
} | |||
} | |||
@include media-breakpoint-down('md') { | |||
.hero-btn { | |||
padding-left: 2.5rem; | |||
padding-right: 2.5rem; | |||
} | |||
} | |||
@include media-breakpoint-up('md') { | |||
.hero-title { | |||
font-size: 3.7em; | |||
} | |||
.hero-prompt { | |||
font-size: 2.2rem; | |||
margin-bottom: 0; | |||
} | |||
} | |||
@include media-breakpoint-up('lg') { | |||
.hero-title { | |||
font-size: 4em; | |||
} | |||
.hero-prompt { | |||
font-size: 2.2rem; | |||
margin-bottom: 0; | |||
} | |||
.hero-ghbtn { | |||
margin-left: 2rem; | |||
} | |||
} | |||
@include media-breakpoint-up('xl') { | |||
.hero-title { | |||
font-size: 4.5em; | |||
} | |||
.hero-prompt { | |||
font-size: 3rem; | |||
margin-bottom: 0; | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
/* | |||
Monokai style - ported by Luigi Maselli - http://grigio.org | |||
*/ | |||
.hljs { | |||
background: #292b2c; | |||
-webkit-font-smoothing: antialiased; | |||
border-left: 4px solid #2c8ebb; | |||
color: #ddd; | |||
max-width: 100%; | |||
margin-bottom: 1.2rem; | |||
border-radius: 3px; | |||
padding: 0.6rem 1.2rem; | |||
overflow-x: auto; | |||
-webkit-overflow-scrolling: touch; | |||
} | |||
.hljs-tag, | |||
.hljs-keyword, | |||
.hljs-selector-tag, | |||
.hljs-literal, | |||
.hljs-strong, | |||
.hljs-name { | |||
color: #f92672; | |||
} | |||
.hljs-code { | |||
color: #66d9ef; | |||
} | |||
.hljs-class .hljs-title { | |||
color: white; | |||
} | |||
.hljs-attribute, | |||
.hljs-symbol, | |||
.hljs-regexp, | |||
.hljs-link { | |||
color: #bf79db; | |||
} | |||
.hljs-string, | |||
.hljs-bullet, | |||
.hljs-subst, | |||
.hljs-title, | |||
.hljs-section, | |||
.hljs-emphasis, | |||
.hljs-type, | |||
.hljs-built_in, | |||
.hljs-builtin-name, | |||
.hljs-selector-attr, | |||
.hljs-selector-pseudo, | |||
.hljs-addition, | |||
.hljs-variable, | |||
.hljs-template-tag, | |||
.hljs-template-variable { | |||
color: #a6e22e; | |||
} | |||
.hljs-comment, | |||
.hljs-quote, | |||
.hljs-deletion, | |||
.hljs-meta { | |||
color: #75715e; | |||
} | |||
.hljs-keyword, | |||
.hljs-selector-tag, | |||
.hljs-literal, | |||
.hljs-doctag, | |||
.hljs-title, | |||
.hljs-section, | |||
.hljs-type, | |||
.hljs-selector-id { | |||
font-weight: bold; | |||
} |
@@ -0,0 +1,13 @@ | |||
.icon { | |||
position: relative; | |||
display: inline-block; | |||
width: 20px; | |||
height: 20px; | |||
vertical-align: middle; | |||
top: -2px; | |||
} | |||
.icon-language { | |||
background-image: url(/assets/language.svg); | |||
margin-right: 4px; | |||
} |
@@ -0,0 +1,11 @@ | |||
.install-only-stable, | |||
.install-only-rc, | |||
.install-only-nightly { | |||
display: none; | |||
} | |||
.install-stable .install-only-stable, | |||
.install-rc .install-only-rc, | |||
.install-nightly .install-only-nightly { | |||
display: inherit; | |||
} |
@@ -0,0 +1,62 @@ | |||
$navbar-link-height: 0.425rem; | |||
$navbar-height: ($navbar-padding-y * 2) + ($navbar-link-height * 2) + | |||
($font-size-base * $line-height-base); | |||
.navbar-icon { | |||
position: relative; | |||
top: -2px; | |||
height: 20px; | |||
vertical-align: middle; | |||
path { | |||
fill: $navbar-light-color; | |||
.navbar-light .nav-item .nav-link:hover &, | |||
.navbar-light .nav-item .nav-link:focus & { | |||
fill: $navbar-light-hover-color; | |||
} | |||
.navbar-light .nav-item.active .nav-link &, | |||
.navbar-light .nav-item.open > .nav-link & { | |||
fill: $navbar-light-active-color; | |||
} | |||
} | |||
} | |||
.navbar-logo { | |||
float: left; | |||
height: 2.6rem; | |||
margin-bottom: -0.2rem; | |||
margin-right: $navbar-padding-x * 2; | |||
} | |||
.navbar-logo .logo-primary { | |||
fill: rgba(44, 142, 187, 1); | |||
transition: fill 500ms ease; | |||
} | |||
@include media-breakpoint-down('md') { | |||
.navbar-toggleable-md { | |||
clear: both; | |||
} | |||
.navbar-nav-right.navbar-nav .nav-item { | |||
float: left; | |||
margin-right: $navbar-padding-x; | |||
} | |||
.navbar-nav-right.navbar-nav .dropdown-menu { | |||
position: absolute; | |||
} | |||
} | |||
@include media-breakpoint-down('xs') { | |||
.navbar-nav-right.navbar-nav .dropdown { | |||
float: none; | |||
} | |||
} | |||
// Waiting on next release of bootstrap on cdnjs (.open was renamed to .show) | |||
.open { | |||
@extend .show; | |||
} |
@@ -0,0 +1,4 @@ | |||
.nav-inline { | |||
display: inline-block; | |||
vertical-align: top; | |||
} |
@@ -0,0 +1,78 @@ | |||
.news-container { | |||
position: relative; | |||
height: 40px; | |||
padding: 0 1em; | |||
text-decoration: none; | |||
line-height: 40px; | |||
margin: auto; | |||
@media (max-width: 760px) { | |||
height: auto; | |||
line-height: 30px; | |||
text-align: center; | |||
} | |||
background: #2188b6; | |||
color: rgba(255, 255, 255, 0.8); | |||
} | |||
.news-container-alert { | |||
position: relative; | |||
height: auto; | |||
text-decoration: none; | |||
margin: auto; | |||
} | |||
.news-overlay { | |||
position: absolute; | |||
left: 0; | |||
top: 0; | |||
bottom: 0; | |||
right: 0; | |||
} | |||
.news-inner { | |||
position: relative; | |||
z-index: 1; | |||
pointer-events: none; | |||
a { | |||
display: inline-block; | |||
pointer-events: all; | |||
color: inherit; | |||
&:hover { | |||
color: #ffffff; | |||
} | |||
.alert & { | |||
color: $link-color; | |||
&:hover { | |||
color: $link-color-hover; | |||
} | |||
} | |||
} | |||
} | |||
.news-line { | |||
display: inline-block; | |||
} | |||
.news-highlight { | |||
font-weight: bold; | |||
color: #ffffff; | |||
} |
@@ -0,0 +1,47 @@ | |||
.readMore { | |||
&--collapsed &--content { | |||
overflow: hidden; | |||
position: relative; | |||
margin-bottom: 0.2em; | |||
&:after { | |||
content: ''; | |||
position: absolute; | |||
left: 0; | |||
top: 0; | |||
height: 250px; | |||
width: 100%; | |||
box-shadow: inset 0 -100px 60px -60px white; | |||
pointer-events: none; | |||
} | |||
} | |||
&--content { | |||
overflow-x: auto; | |||
img { | |||
max-width: 100%; | |||
} | |||
} | |||
&--button { | |||
padding: 0.3em 0.8em; | |||
padding-right: 0; | |||
border-radius: 0.2em; | |||
color: #666; | |||
border: 1px solid #cbcbcb; | |||
background-color: transparent; | |||
cursor: pointer; | |||
&:active, | |||
&:focus { | |||
outline: none; | |||
box-shadow: inset 0 0 0 1px #cbcbcb; | |||
} | |||
img { | |||
width: 0.8em; | |||
margin: 0 1em; | |||
} | |||
} | |||
} |
@@ -0,0 +1,425 @@ | |||
/** Algolia Pkg Search **/ | |||
$ais-focus-color: rgba(black, 0.7); | |||
$ais-muted-color: rgba(black, 0.5); | |||
.full-searchbox .ais-SearchBox { | |||
background-color: mix(black, $yarn-blue, 15%); | |||
transition: background-color 0.1s ease; | |||
padding: 1.4em 0.3em; | |||
box-sizing: border-box; | |||
position: relative; | |||
&:before, | |||
&:after { | |||
content: ''; | |||
display: block; | |||
position: absolute; | |||
z-index: 1; | |||
left: 0; | |||
right: 0; | |||
height: 7px; | |||
background: { | |||
repeat: repeat-x; | |||
size: 144px auto; | |||
} | |||
} | |||
&:before { | |||
top: -3.5px; | |||
background-image: $image-border-white; | |||
} | |||
&:after { | |||
bottom: -3.5px; | |||
background-image: $image-border-yarn-blue; | |||
.searching & { | |||
background-image: $image-border-white; | |||
} | |||
} | |||
&-form { | |||
@extend .container; | |||
display: flex; | |||
background-color: white; | |||
border-radius: 0.2em; | |||
padding: 0.2em; | |||
min-height: 38.8px; | |||
transition: box-shadow 0.4s ease, background 0.4s ease; | |||
box-shadow: 0 2px 2px 0 rgba(85, 95, 110, 0.4); | |||
.active & { | |||
background-color: mix(black, white, 2%); | |||
box-shadow: none; | |||
} | |||
} | |||
&-input { | |||
order: 2; | |||
flex-grow: 1; | |||
background: none; | |||
border: none; | |||
border-radius: 0; | |||
font: inherit; | |||
color: $yarn-blue; | |||
margin-left: 0.5em; | |||
&:active, | |||
&:focus { | |||
outline: none; | |||
} | |||
@include placeholder { | |||
color: lighten($yarn-blue, 10%); | |||
font-style: italic; | |||
font-weight: 100; | |||
} | |||
} | |||
&-reset, | |||
&-submit { | |||
width: 2em; | |||
height: 2em; | |||
background: none; | |||
border: none; | |||
position: relative; | |||
cursor: pointer; | |||
svg { | |||
width: 1.6em; | |||
height: 1.6em; | |||
padding: 0.2em; | |||
fill: $yarn-blue; | |||
position: absolute; | |||
top: 0.2em; | |||
left: 0.2em; | |||
} | |||
} | |||
&-submit { | |||
order: 1; | |||
} | |||
&-reset { | |||
order: 3; | |||
visibility: hidden; | |||
} | |||
} | |||
.ais-Hits { | |||
&-item { | |||
padding: 1.5rem 1rem 2rem; | |||
border-bottom: 1px solid $gray-lighter; | |||
position: relative; | |||
} | |||
&-empty { | |||
padding: 2.5rem 0 2rem; | |||
border-bottom: 1px solid $gray-lighter; | |||
} | |||
} | |||
.ais-Hit { | |||
&-name { | |||
font-size: 1.625rem; | |||
font-weight: normal; | |||
color: $ais-focus-color; | |||
margin-right: 8px; | |||
position: relative; | |||
top: 2px; | |||
&:hover, | |||
&:visited, | |||
&:focus { | |||
text-decoration: none; | |||
color: rgba(black, 0.9); | |||
} | |||
} | |||
&-popular { | |||
font-size: 0.825rem; | |||
color: $ais-muted-color; | |||
margin-right: 8px; | |||
text-transform: uppercase; | |||
&::before { | |||
position: relative; | |||
display: inline-block; | |||
content: ''; | |||
background-position: bottom center; | |||
background-repeat: no-repeat; | |||
background-image: url('/assets/search/ico-download.svg'); | |||
width: 15px; | |||
height: 20px; | |||
margin-right: 3px; | |||
top: 2px; | |||
} | |||
&.hot-t4::before { | |||
background-image: url('/assets/search/ico-hot-t4.svg'); | |||
} | |||
&.hot-t3::before { | |||
background-image: url('/assets/search/ico-hot-t3.svg'); | |||
} | |||
&.hot-t2::before { | |||
background-image: url('/assets/search/ico-hot-t2.svg'); | |||
} | |||
&.hot-t1::before { | |||
background-image: url('/assets/search/ico-hot-t1.svg'); | |||
} | |||
} | |||
&-license { | |||
font-size: 0.75rem; | |||
border: solid 1px #ccc; | |||
color: $ais-muted-color; | |||
padding: 2px 4px; | |||
border-radius: 4px; | |||
margin-right: 8px; | |||
letter-spacing: 0.2px; | |||
} | |||
&-deprecated { | |||
font-size: 0.75rem; | |||
text-transform: uppercase; | |||
background-color: #ccc; | |||
border: solid 1px #ccc; | |||
color: white; | |||
padding: 2px 4px; | |||
border-radius: 4px; | |||
margin-right: 8px; | |||
letter-spacing: 0.2px; | |||
} | |||
&-version { | |||
font-size: 0.825rem; | |||
color: $ais-muted-color; | |||
font-weight: bold; | |||
max-width: 90px; | |||
text-overflow: ellipsis; | |||
overflow: hidden; | |||
white-space: nowrap; | |||
display: inline-block; | |||
vertical-align: middle; | |||
} | |||
&-ownerLink { | |||
font-size: 0.69rem; | |||
font-weight: bold; | |||
color: $ais-focus-color; | |||
text-transform: uppercase; | |||
letter-spacing: 0.3px; | |||
margin-right: 8px; | |||
} | |||
&-ownerAvatar { | |||
border-radius: 4px; | |||
margin-right: 4px; | |||
position: relative; | |||
top: -2px; | |||
} | |||
&-description { | |||
font-size: 0.875rem; | |||
color: $ais-muted-color; | |||
margin: 0.6rem 0; | |||
max-width: calc(100% - 120px); | |||
@include media-breakpoint-down(xs) { | |||
max-width: 100%; | |||
} | |||
&::first-letter { | |||
text-transform: uppercase; | |||
} | |||
} | |||
&-lastUpdate { | |||
font-size: 0.825rem; | |||
color: $ais-muted-color; | |||
font-style: italic; | |||
} | |||
&-keywords { | |||
font-size: 0.825rem; | |||
color: $ais-muted-color; | |||
margin-left: 1em; | |||
font-style: italic; | |||
&:before { | |||
display: inline-block; | |||
content: ''; | |||
background: url('/assets/search/ico-tag.svg') no-repeat; | |||
width: 16px; | |||
height: 8px; | |||
margin-right: 4px; | |||
opacity: 0.5; | |||
} | |||
} | |||
&-keyword { | |||
color: $ais-muted-color; | |||
em { | |||
font-style: italic; | |||
} | |||
&:hover { | |||
cursor: pointer; | |||
} | |||
} | |||
&-links { | |||
position: absolute; | |||
top: calc(50% - 12px); | |||
right: 1rem; | |||
@include media-breakpoint-down(xs) { | |||
position: inherit; | |||
} | |||
} | |||
&-link-homepage, | |||
&-link-npm, | |||
&-link-gitlab, | |||
&-link-bitbucket, | |||
&-link-github { | |||
margin-left: 0.8em; | |||
display: block; | |||
float: right; | |||
a { | |||
display: block; | |||
opacity: 0.5; | |||
text-indent: -9000px; | |||
height: 100%; | |||
background-position: center; | |||
background-repeat: no-repeat; | |||
&:hover { | |||
opacity: 0.7; | |||
} | |||
} | |||
} | |||
&-link-homepage a { | |||
background-image: url('/assets/search/ico-home.svg'); | |||
width: 26px; | |||
height: 26px; | |||
} | |||
&-link-npm a { | |||
background-image: url('/assets/search/ico-npm.svg'); | |||
width: 34px; | |||
height: 26px; | |||
} | |||
&-link-github a { | |||
background-image: url('/assets/search/ico-github.svg'); | |||
width: 26px; | |||
height: 26px; | |||
} | |||
&-link-bitbucket a { | |||
background-image: url('/assets/search/ico-bitbucket.svg'); | |||
width: 26px; | |||
height: 26px; | |||
} | |||
&-link-gitlab a { | |||
background-image: url('/assets/search/ico-gitlab.svg'); | |||
width: 26px; | |||
height: 26px; | |||
} | |||
&-typescript { | |||
margin-left: 0.2em; | |||
width: 0.8em; | |||
height: 0.8em; | |||
vertical-align: baseline; | |||
} | |||
} | |||
.ais-Pagination { | |||
margin: 2em auto; | |||
@extend .pagination; | |||
&-item { | |||
@extend .page-item; | |||
&--selected { | |||
@extend .active; | |||
} | |||
&--disabled { | |||
@extend .disabled; | |||
visibility: visible; | |||
opacity: 0.3; | |||
cursor: default; | |||
} | |||
} | |||
&-link { | |||
@extend .page-link; | |||
} | |||
} | |||
.ais-CurrentRefinements { | |||
&-list { | |||
list-style-type: none; | |||
padding: 0; | |||
margin: 0 0.5em; | |||
font-size: 0.8125rem; | |||
} | |||
&-category { | |||
display: inline-block; | |||
border: 1px solid $gray-lighter; | |||
border-radius: 2px; | |||
margin-right: 0.5em; | |||
padding: 0 0.5em; | |||
} | |||
&-delete { | |||
background: none; | |||
border: none; | |||
} | |||
} | |||
#pkg-featured { | |||
margin-top: 4em; | |||
.pkg-featured-pkg { | |||
padding: 1rem; | |||
min-height: 120px; | |||
border-bottom: solid 1px $gray-lighter; | |||
p { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
margin-bottom: 0; | |||
} | |||
.ais-Hit-keywords { | |||
margin-left: 0; | |||
a { | |||
color: $ais-muted-color; | |||
} | |||
} | |||
} | |||
} | |||
.search-footer { | |||
text-align: center; | |||
} | |||
.ais-Highlight-highlighted { | |||
border-bottom: dotted 1px; | |||
font-style: normal; | |||
} |
@@ -0,0 +1,44 @@ | |||
.tabs { | |||
margin: 1rem 0; | |||
.tab-content { | |||
border: 1px solid $nav-tabs-border-color; | |||
border-top-width: 0; | |||
border-radius: 0 0 $nav-tabs-border-radius $nav-tabs-border-radius; | |||
} | |||
.tab-pane { | |||
padding: $spacer; | |||
> { | |||
h1, | |||
h2, | |||
h3, | |||
h4, | |||
h5, | |||
h6, | |||
p, | |||
ul, | |||
ol, | |||
pre, | |||
blockquote, | |||
.highlighter-rouge { | |||
&:first-child { | |||
margin-top: 0; | |||
} | |||
&:last-child { | |||
margin-bottom: 0; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.nav-tabs { | |||
@extend .bg-faded; | |||
border-radius: $nav-tabs-border-radius $nav-tabs-border-radius 0 0; | |||
border: 1px solid $nav-tabs-border-color; | |||
padding: ($spacer / 2); | |||
padding-bottom: 0; | |||
} |
@@ -0,0 +1,11 @@ | |||
.toc::before { | |||
content: '#'; | |||
} | |||
@include media-breakpoint-up('md') { | |||
.toc::before { | |||
position: absolute; | |||
display: inline-block; | |||
right: 100%; | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
.user { | |||
display: block; | |||
position: relative; | |||
height: 8rem; | |||
padding: 0.5rem; | |||
margin: 0 0 1rem; | |||
background: white; | |||
border: 1px solid $card-border-color; | |||
border-radius: $border-radius; | |||
img { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%); | |||
max-width: 200px; | |||
max-height: 100px; | |||
} | |||
&-target { | |||
position: relative; | |||
top: -140px; | |||
&:target + a { | |||
z-index: 1; | |||
border-color: transparent; | |||
box-shadow: 0 0 0 5px $brand-primary; | |||
animation: 1s tada 0.5s 1 both; | |||
} | |||
} | |||
} | |||
@keyframes tada { | |||
0%, | |||
100% { | |||
transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 0deg); | |||
} | |||
10%, | |||
20% { | |||
transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); | |||
} | |||
30%, | |||
50%, | |||
70%, | |||
90% { | |||
transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); | |||
} | |||
40%, | |||
60%, | |||
80% { | |||
transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 24"> | |||
<g fill="#666"> | |||
<path d="M18.5 0h-17C.673 0 0 .673 0 1.5v21c0 .827.673 1.5 1.5 1.5h17c.827 0 1.5-.673 1.5-1.5v-21c0-.827-.673-1.5-1.5-1.5zm.5 22.5a.5.5 0 0 1-.5.5h-17a.5.5 0 0 1-.5-.5V20h1.092c.207.58.757 1 1.408 1 .827 0 1.5-.673 1.5-1.5S4.327 18 3.5 18c-.65 0-1.2.42-1.408 1H1v-4h1.092c.207.58.757 1 1.408 1 .827 0 1.5-.673 1.5-1.5S4.327 13 3.5 13c-.65 0-1.2.42-1.408 1H1v-4h1.092c.207.58.757 1 1.408 1 .827 0 1.5-.673 1.5-1.5S4.327 8 3.5 8c-.65 0-1.2.42-1.408 1H1V5h1.092c.207.58.757 1 1.408 1C4.327 6 5 5.327 5 4.5S4.327 3 3.5 3c-.65 0-1.2.42-1.408 1H1V1.5a.5.5 0 0 1 .5-.5h17a.5.5 0 0 1 .5.5v21zm-16-3a.5.5 0 1 1 1.002.002A.5.5 0 0 1 3 19.5zm0-5a.5.5 0 1 1 1.002.002A.5.5 0 0 1 3 14.5zm0-5a.5.5 0 1 1 1.002.002A.5.5 0 0 1 3 9.5zm0-5a.5.5 0 1 1 1.002.002A.5.5 0 0 1 3 4.5z"/> | |||
<path d="M16.5 10h-9a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1zm0-4h-9a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1zm0 8h-9a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1zm-2 4h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1z"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,7 @@ | |||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | |||
<g fill-rule="nonzero" fill="#666"> | |||
<path d="M12 23.5C5.659 23.5.5 18.341.5 12S5.659.5 12 .5 23.5 5.659 23.5 12 18.341 23.5 12 23.5zm0-22C6.21 1.5 1.5 6.21 1.5 12S6.21 22.5 12 22.5 22.5 17.79 22.5 12 17.79 1.5 12 1.5z"/> | |||
<path d="M12 12.5a.5.5 0 0 1-.5-.5V6a.5.5 0 0 1 1 0v4.793l2.146-2.146a.5.5 0 0 1 .707.707l-3 3A.5.5 0 0 1 12 12.5z"/> | |||
<path d="M12 20.5c-4.687 0-8.5-3.813-8.5-8.5 0-4.687 3.813-8.5 8.5-8.5 4.687 0 8.5 3.813 8.5 8.5 0 4.687-3.813 8.5-8.5 8.5zm0-16c-4.136 0-7.5 3.364-7.5 7.5s3.364 7.5 7.5 7.5 7.5-3.364 7.5-7.5-3.364-7.5-7.5-7.5z"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,6 @@ | |||
<svg viewBox="0 0 18 24" xmlns="http://www.w3.org/2000/svg"> | |||
<g fill-rule="nonzero" fill="#666"> | |||
<path d="M7.147 11.853a.5.5 0 0 0 .707 0L8 11.707V13.5a.5.5 0 0 0 1.001 0v-3a.5.5 0 0 0-.854-.354l-1 1a.5.5 0 0 0 0 .707zM10.854 11.853l.146-.146V13.5a.5.5 0 0 0 1.001 0v-3a.5.5 0 0 0-.854-.354l-1 1a.5.5 0 0 0 .707.707zM13.854 11.853l.146-.146V13.5a.5.5 0 0 0 1.001 0v-3a.5.5 0 0 0-.854-.354l-1 1a.5.5 0 0 0 .707.707zM6 10.5a.5.5 0 0 0-.5-.5h-2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5v-3zM5 13H4v-2h1v2zM10.854 17.853l.146-.146V19.5a.5.5 0 0 0 1.001 0v-3a.5.5 0 0 0-.854-.354l-1 1a.5.5 0 0 0 .707.707zM13.854 17.853l.146-.146V19.5a.5.5 0 0 0 1.001 0v-3a.5.5 0 0 0-.854-.354l-1 1a.5.5 0 0 0 .707.707zM4.692 16.038a.5.5 0 0 0-.545.108l-1 1a.5.5 0 0 0 .707.707L4 17.707V19.5a.5.5 0 0 0 1.001 0v-3a.5.5 0 0 0-.309-.462zM8.5 16h-2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5zM8 19H7v-2h1v2z"/> | |||
<path d="M17.5 0h-11a.513.513 0 0 0-.194.039c-.019.008-.033.023-.05.034-.038.022-.078.042-.11.073l-6 6c-.023.023-.036.054-.054.081-.018.027-.041.05-.053.08A.508.508 0 0 0 0 6.5v17a.5.5 0 0 0 .5.5h17a.5.5 0 0 0 .5-.5V.5a.5.5 0 0 0-.5-.5zM6 1.707V6H1.707L6 1.707zM17 23H1V7h5.5a.5.5 0 0 0 .5-.5V1h10v22z"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,3 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 17"> | |||
<path fill="#666666" d="M13.806 4.068h-3.182V.886A.637.637 0 0 0 9.988.25h-8.91a.637.637 0 0 0-.636.636V12.34c0 .352.285.637.637.637h3.18v3.182c0 .35.286.635.637.635h8.91a.637.637 0 0 0 .635-.636V4.704a.637.637 0 0 0-.636-.637zm-9.546.637v7H1.715V1.523H9.35v2.545H4.898a.637.637 0 0 0-.637.637zm8.91 10.818H5.533V5.34h7.637v10.182z"/> | |||
</svg> |
@@ -0,0 +1,3 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"> | |||
<path fill="#666666" d="M13.79 30.593c.603 0 1.207-.23 1.66-.69l18.86-18.86c.92-.92.92-2.414 0-3.334-.92-.92-2.414-.92-3.333 0L13.785 24.9l-7.762-7.76c-.92-.92-2.414-.92-3.333 0-.92.92-.92 2.414 0 3.334l9.43 9.43c.462.458 1.066.69 1.67.69z"/> | |||
</svg> |
@@ -0,0 +1,3 @@ | |||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | |||
<path d="M23.956 11.314a.478.478 0 0 0-.124-.175c-.014-.013-.02-.031-.036-.042-.007-.005-.017-.005-.024-.01-.009-.006-.014-.015-.024-.021l-2.245-1.283c.3-.289.497-.68.497-1.122 0-.597-.333-1.135-.868-1.404l-3.664-1.832.425-1.063.288-.144a.965.965 0 0 0 .776.101l3.854-1.102A1.648 1.648 0 0 0 24 1.64C24 .736 23.264 0 22.36 0h-.46c-.372 0-.736.128-1.025.359l-2.804 2.243a.965.965 0 0 0-.356.731l-.439.219a.498.498 0 0 0-.241.262l-.465 1.162-1.397-.698c-1.067-.534-2.435-.245-3.196.668l-4.26 5.112-7.345 1.959c-.028.008-.049.025-.075.037-.015.007-.033.004-.048.013-.009.005-.013.016-.021.021a.497.497 0 0 0-.156.157l-.01.021a.486.486 0 0 0-.054.203c0 .011-.008.02-.008.031v7a.5.5 0 0 0 .252.434l7 4a.5.5 0 0 0 .397.043l16-5A.499.499 0 0 0 24 18.5v-7c0-.017-.01-.031-.011-.048a.506.506 0 0 0-.033-.138zM21.501 1.14A.64.64 0 0 1 21.9 1h.461a.64.64 0 0 1 .176 1.255l-3.589 1.026-.001-.005c-.011-.023-.031-.037-.045-.057l2.599-2.079zm-8.755 4.446A1.628 1.628 0 0 1 13.998 5c.251 0 .503.06.731.171l1.469.734-.101.252-4.855 2.914a.5.5 0 0 0 .515.857l5-3a.497.497 0 0 0 .207-.243l.132-.33 3.591 1.795a.57.57 0 0 1 .315.514.57.57 0 0 1-.432.553l-.243.061-.004.001-13.457 3.364 5.88-7.057zm-7.669 8.18a.498.498 0 0 0 .545.218l1.68-.42 1.441.865a.504.504 0 0 0 .686-.171.5.5 0 0 0-.171-.686l-.585-.351 1.922-.48 1.148.688a.504.504 0 0 0 .686-.171.5.5 0 0 0-.171-.686l-.291-.174 1.923-.481.852.512a.5.5 0 0 0 .519-.855l1.923-.481.559.335a.5.5 0 0 0 .686-.17.493.493 0 0 0 .01-.478l1.943-.486 1.874 1.071-14.697 4.593-5.754-3.288 4.804-1.281-1.493 1.791a.501.501 0 0 0-.039.586zM7 22.638L1 19.21v-5.848l6 3.428v5.848zm16-4.506L8 22.82v-5.953l15-4.687v5.952z" fill-rule="nonzero" fill="#666"/> | |||
</svg> |
@@ -0,0 +1,8 @@ | |||
<svg viewBox="0 0 24 21" xmlns="http://www.w3.org/2000/svg"> | |||
<g fill-rule="nonzero" fill="#828282"> | |||
<path d="M12 20.208a.503.503 0 0 1-.375-.169l-11.5-13a.497.497 0 0 1-.072-.554l3-6A.5.5 0 0 1 3.5.208h17a.5.5 0 0 1 .447.276l3 6a.5.5 0 0 1-.073.555l-11.5 13a.496.496 0 0 1-.374.169zM1.098 6.63L12 18.954 22.902 6.63l-2.71-5.421H3.808L1.1 6.63z"/> | |||
<path d="M23.5 7.208H.5a.5.5 0 0 1 0-1h23a.5.5 0 0 1 0 1z"/> | |||
<path d="M16.5 7.208a.5.5 0 0 1-.4-.2L12 1.542 7.9 7.008a.524.524 0 0 1-.414.2.498.498 0 0 1-.402-.222l-4-6a.5.5 0 1 1 .832-.554l3.608 5.412L11.6.409c.188-.252.61-.252.8 0l4.076 5.435L20.084.432a.5.5 0 0 1 .832.554l-4 6a.502.502 0 0 1-.402.223l-.014-.001z"/> | |||
<path d="M12 20.208a.501.501 0 0 1-.473-.336l-4.5-13a.5.5 0 0 1 .945-.327L12 18.18l4.027-11.635a.5.5 0 1 1 .945.327l-4.5 13a.499.499 0 0 1-.472.336z"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,6 @@ | |||
<svg viewBox="0 0 24 8" xmlns="http://www.w3.org/2000/svg"> | |||
<g fill-rule="nonzero" fill="#666"> | |||
<path d="M6.5 7.5h-3C1.57 7.5 0 5.93 0 4S1.57.5 3.5.5h3C8.43.5 10 2.07 10 4S8.43 7.5 6.5 7.5zm-3-6A2.503 2.503 0 0 0 1 4c0 1.378 1.122 2.5 2.5 2.5h3C7.878 6.5 9 5.378 9 4S7.878 1.5 6.5 1.5h-3zM20.5 7.5h-3C15.57 7.5 14 5.93 14 4S15.57.5 17.5.5h3C22.43.5 24 2.07 24 4s-1.57 3.5-3.5 3.5zm-3-6A2.503 2.503 0 0 0 15 4c0 1.378 1.122 2.5 2.5 2.5h3C21.878 6.5 23 5.378 23 4s-1.122-2.5-2.5-2.5h-3z"/> | |||
<path d="M16.5 4.5h-9a.5.5 0 0 1 0-1h9a.5.5 0 0 1 0 1z"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,5 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | |||
<path d="M23.7 16.8l-.3-.3H23c-.2 0-.5 0-.6-.2v-.6c.3 0 .3-.2.3-.4V15c-1-1-2-1.6-3-1.8H19l-.2.4c0 .3-.3.5-.5.5s-.5 0-.5-.4c0 0 0-.3-.2-.4h-.4c-1 .2-2.2.8-3 1.7 0 0-.2.2 0 .3v.4c.3 0 .3.4.2.6s-.4.3-.6.2h-.4l-.3.3-.3 1.8.3 1.7.3.3h.4c.2 0 .5 0 .6.2v.6c-.3 0-.3.2-.3.4v.4c1 1 2 1.5 3 1.7h.5l.2-.5c0-.3.3-.4.5-.4s.5.2.5.5c0 0 0 .3.2.4h.3c1.3-.3 2.3-1 3-1.8.2 0 .3-.3.2-.4 0-.2 0-.3-.2-.4-.2 0-.2-.4 0-.6 0-.2.3-.3.5-.2h.4l.3-.3.3-1.7-.3-1.8zm-.8 2.8c-.7 0-1.2.2-1.5.7-.2.5-.2 1 0 1.5-.4.5-1 1-1.7 1-.2-.4-.7-.7-1.3-.7s-1 .4-1.3 1c-.7-.3-1.3-.7-1.8-1.2.3-.4.3-1 0-1.5-.2-.5-.7-.8-1.3-.7v-1-1c.7 0 1.2-.3 1.5-.8.2-.5.2-1 0-1.5.4-.5 1-1 1.7-1 .2.4.7.8 1.3.8s1-.3 1.3-.8c.7.2 1.3.6 1.8 1-.3.5-.3 1 0 1.6.2.5.7.7 1.3.7v2z" fill="#666"/> | |||
<path d="M18.5 16C17 16 16 17.3 16 18.7s1 2.5 2.5 2.5 2.5-1 2.5-2.4-1-2.5-2.5-2.5zm0 4c-.8 0-1.5-.6-1.5-1.4s.7-1.5 1.5-1.5 1.5.7 1.5 1.6c0 .8-.7 1.5-1.5 1.5z" fill="#666"/> | |||
<path d="M11.5 19L6 20 3 4l17-3 2.2 12c0 .3.3.5.6.4.2 0 .4-.3.4-.5L21 .4c0-.3-.3-.5-.6-.4L14 1 1.6 0s-.2 0-.3.2L1 .5l-1 18c0 .3.2.5.4.5l4.4.5.2 1c0 .4.3.6.5.6l6.2-1c.2 0 .4-.3.4-.5 0-.3-.2-.5-.5-.5zM1 18L2 1l8 .8L2.5 3l-.3.3v.3l2.6 15L1 18z" fill="#666"/> | |||
</svg> |
@@ -0,0 +1,3 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"> | |||
<path d="M6.5 2a.5.5 0 1 0 0 1 .5.5 0 0 0 0-1zm3 0a.5.5 0 1 0 0 1 .5.5 0 0 0 0-1zm-6 0a.5.5 0 1 0 0 1 .5.5 0 0 0 0-1zm22.6 22.1L24 26.3v-6.8a.5.5 0 0 0-1 0v6.8L20.9 24a.5.5 0 0 0-.8.8l3 3h.2a.5.5 0 0 0 .4 0h.2l3-3a.5.5 0 0 0-.8-.8zM15.5 26H3a2 2 0 0 1-2-2V5h25v10.5a.5.5 0 0 0 1 0V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v21a3 3 0 0 0 3 3h12.5a.5.5 0 1 0 0-1zM1 3c0-1.1.9-2 2-2h21a2 2 0 0 1 2 2v1H1V3zm22.5 14a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13zm0 12a5.5 5.5 0 1 1 0-11 5.5 5.5 0 0 1 0 11z"/> | |||
</svg> |
@@ -0,0 +1,7 @@ | |||
<svg viewBox="0 0 24 22" xmlns="http://www.w3.org/2000/svg"> | |||
<g fill-rule="nonzero" fill="#666"> | |||
<path d="M18.5 17h-5a.5.5 0 0 1 0-1h5c2.481 0 4.5-2.019 4.5-4.5S20.981 7 18.5 7c-.273.004-.538-.171-.573-.422C17.474 3.398 14.711 1 11.5 1A6.508 6.508 0 0 0 5 7.5c0 .28.024.578.076.938a.5.5 0 0 1-.12.402c-.097.109-.248.147-.384.169a.932.932 0 0 1-.105-.01C2.57 9 1 10.57 1 12.5S2.57 16 4.5 16h5a.5.5 0 0 1 0 1h-5A4.505 4.505 0 0 1 0 12.5a4.507 4.507 0 0 1 4.022-4.475A6.391 6.391 0 0 1 4 7.5C4 3.364 7.364 0 11.5 0c3.565 0 6.651 2.561 7.346 6.023C21.758 6.232 24 8.609 24 11.5c0 3.033-2.467 5.5-5.5 5.5z"/> | |||
<path d="M11.5 22a.504.504 0 0 1-.354-.146l-2-2a.5.5 0 0 1 .707-.707L11 20.293V11.5a.5.5 0 0 1 1 0v10a.5.5 0 0 1-.5.5z"/> | |||
<path d="M11.5 22a.5.5 0 0 1-.354-.853l2-2a.5.5 0 0 1 .707.707l-2 2A.498.498 0 0 1 11.5 22z"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,6 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | |||
<g fill="#666"> | |||
<path d="M7.1 14.5l1.816-2.724c.153-.23.09-.54-.14-.693-.227-.153-.538-.092-.692.14l-2 3c-.112.167-.112.386 0 .554l2 3c.097.145.255.223.416.223.096 0 .192-.027.277-.085.23-.153.292-.463.14-.693L7.1 14.5zm3.224 4.468c.058.022.117.032.176.032.202 0 .393-.124.468-.324l3-8c.098-.26-.033-.547-.292-.644-.256-.097-.547.033-.644.292l-3 8c-.098.26.033.547.292.644zM16.9 14.5l-1.816 2.723c-.153.23-.09.54.14.693.084.057.18.084.276.084.16 0 .32-.078.416-.222l2-3c.112-.168.112-.387 0-.555l-2-3c-.154-.23-.465-.292-.693-.14-.23.154-.292.464-.14.694L16.9 14.5z" /> | |||
<path d="M20.5 0h-11c-.067 0-.133.014-.194.04-.02.007-.033.022-.05.033-.038.022-.078.042-.11.073l-6 6c-.023.023-.036.054-.054.08-.018.028-.04.05-.053.08-.028.062-.04.127-.04.194v17c0 .276.222.5.5.5h17c.274 0 .5-.224.5-.5V.5c0-.276-.226-.5-.5-.5zM9 1.707V6H4.707L9 1.707zM20 23H4V7h5.5c.276 0 .5-.224.5-.5V1h10v22z"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,6 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"> | |||
<g fill="#666"> | |||
<path d="M27 26.5H3c-1.7 0-3-1.3-3-3v-15c0-1.7 1.3-3 3-3h4.5c1.3 0 2.4.8 2.8 2.1v.1l.1.5c.3.8 1 1.3 1.9 1.3H27c1.7 0 3 1.3 3 3v11c0 1.7-1.3 3-3 3zM3 6.5c-1.1 0-2 .9-2 2v15c0 1.1.9 2 2 2h24c1.1 0 2-.9 2-2v-11c0-1.1-.9-2-2-2H12.3c-1.3 0-2.4-.8-2.8-2.1v-.1l-.1-.5C9.1 7 8.3 6.5 7.5 6.5H3z"/> | |||
<path d="M28.5 11c-.3 0-.5-.2-.5-.5v-4c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2 0 .3-.2.5-.5.5S1 6.8 1 6.5c0-1.7 1.3-3 3-3h22c1.7 0 3 1.3 3 3v4c0 .3-.2.5-.5.5z"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,3 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | |||
<path d="M8 16h-.3-.2-.2H7l-1.4 1.6v.8h.8l.6-.7v1.8c0 .3.2.5.5.5s.5-.2.5-.5v-1.8l.6.7h.8v-.8L8 16zm15.7.7l-.3-.3H23c-.2 0-.5 0-.6-.2v-.6c.3 0 .3-.2.3-.3V15c-1-1-2-1.7-3-2-.3 0-.4 0-.5.2l-.2.3c0 .3-.3.5-.5.5s-.5-.2-.5-.5c0 0 0-.2-.2-.3h-.4c-1 0-2.2.8-3 1.7 0 0-.2 0 0 .3v.3c.3.2.3.4.2.7 0 .2-.4.2-.6 0h-.4c0 .2-.2.3-.3.4l-.3 1.8.3 1.8.3.3h.4c.2 0 .5 0 .6.2v.6c-.3 0-.3.2-.3.3v.4c1 1 2 1.7 3 2 .3 0 .4 0 .5-.2l.2-.3c0-.3.3-.5.5-.5s.5.2.5.5c0 0 0 .2.2.3h.3c1.2 0 2.3-.8 3-1.7.2 0 .3 0 .2-.3 0 0 0-.2-.2-.3-.2-.2-.2-.4 0-.7 0-.2.3-.2.5 0h.4c0-.2.2-.3.3-.4l.3-1.8-.3-1.8zm-.8 2.8c-.7 0-1.2.3-1.5.7-.2.5-.2 1 0 1.6-.4.5-1 .8-1.7 1-.2-.5-.7-.8-1.3-.8s-1 .3-1.3.8c-.7-.2-1.3-.5-1.8-1 .3-.5.3-1 0-1.6-.2-.4-.7-.7-1.3-.7v-1-1c.7 0 1.2-.3 1.5-.8.2-.4.2-1 0-1.5.4-.5 1-.8 1.7-1 .2.5.7.8 1.3.8s1-.3 1.3-.8c.7.2 1.3.5 1.8 1-.3.5-.3 1 0 1.6.2.4.7.7 1.3.7v2zm-10 3l-1 .3V1.2L22 4v8.8c0 .2.2.5.5.5s.5-.3.5-.5V3.5c0-.2-.2-.4-.4-.5l-11-3h-.2L.4 3c-.2 0-.4.3-.4.5v17c0 .2.2.4.4.5l11 3h.2l1.7-.5c.3 0 .4-.3.4-.6 0-.4-.4-.5-.7-.4zM4 3.2l3-.8v9l-.5.6L6 11H5l-.5.8-.5-.5V3zM11 23L1 20V4l2-.7V12l1 1h1l.5-.8.6.7H7l1-1V2l3-.8v21.6zm7.5-6.8C17 16 16 17 16 18.5s1 2.5 2.5 2.5 2.5-1 2.5-2.5-1-2.5-2.5-2.5zm0 4c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5z" fill="#666"/> | |||
</svg> |