CINXE.COM
Sass: Request for Comments: Package Importer
<!doctype html> <html lang="en" class="no-js"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="description" content="Syntactically Awesome Style Sheets" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Sass: Request for Comments: Package Importer</title> <link rel="alternate" type="application/atom+xml" href="/feed.xml" /> <link rel="apple-touch-icon" sizes="152x152" type="image/png" href="/icon.png"> <link rel="icon" type="image/png" sizes="152x152" href="/icon.png"> <link rel="icon" type="image/x-icon" href="/favicon.ico" /> <link href="https://fonts.googleapis.com/css?family=Source+Code+Pro::ital,wght@0,400;0,600;1,400|Source+Sans+Pro:300,400,600|Source+Serif+Pro" media="screen" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/@docsearch/css@alpha" media="screen" rel="stylesheet" /> <link href="/assets/dist/css/sass.css" media="screen" rel="stylesheet" /> <noscript> <link href="/assets/dist/css/noscript.css" media="screen" rel="stylesheet" /> </noscript> <!-- Google Analytics (https://developers.google.com/analytics/devguides/collection/gtagjs). --> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-535380-14"></script> <script> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'UA-535380-14'); </script> </head><body data-layout="body" class="blog 039-rfc-package-importer"> <!--[if lt IE 9]> <p class="browserupgrade"> You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security. </p> <![endif]--> <header class="sl-r-banner" itemtype="https://schema.org/WPHeader" itemscope="itemscope" role="banner"> <div class="sl-c-alert" style="--sl-background--alert: black"> <div class="sl-l-container"> <p><strong>Free Palestine</strong></p> </div> </div> <div class="sl-c-pop-stripe"></div> <div class="sl-l-container"> <div class="sl-l-grid sl-l-grid--full sl-l-large-grid--fit sl-l-large-grid--center sl-l-large-grid--gutters"> <p class="sl-l-grid__column sl-r-banner__brand"> <a href="/"><img height="48" alt="Sass" src="/assets/img/logos/logo.svg"></a> </p> <nav class="sl-r-banner__navigation sl-l-grid__column sl-l-large-grid sl-l-large-grid__column--auto-size sl-c-list-horizontal-wrapper" itemtype="https://schema.org/SiteNavigationElement" itemscope="itemscope" role="navigation" aria-label="Site navigation"> <ul> <li><a href="/playground">Playground</a></li> <li><a href="/install">Install</a></li> <li><a href="/guide">Learn Sass</a></li> <li><a href="/blog">Blog</a></li> <li><a href="/documentation">Documentation</a></li> <li><a href="/community">Get Involved</a></li> <li> <div id="docsearch"></div> </li> </ul> </nav> </div> </div> </header> <main class="content" id="main-content" itemprop="mainContentOfPage" role="main"> <h1 class="sl-l-container">Request for Comments: Package<span class="widont"> </span>Importer</h1> <div class="sl-l-container sl-color--white-background sl-l-section"> <p class="sl-c-attribution">Posted 26 September 2023 by James Stuckey<span class="widont"> </span>Weber</p> <div class="sl-l-container sl-l-container--small"> <p>Sass users often need to use styles from a dependency to customize an existing theme or access styling utilities. Historically, Sass has not specified a standard method for using packages from dependencies. This has led to a variety of domain-specific solutions, including the <code>~</code> prefix in Webpack, and adding <code>node_modules</code> to <code>loadPaths</code>.</p> <p>This has been a common pain point, and can make it difficult to rely on dependencies. It can also make it more difficult to move your project to a new build<span class="widont"> </span>process.</p> <h2 id="package-importers" tabindex="-1">Package Importers<a class="anchor" href="#package-importers"><span class="visuallyhidden">Package Importers permalink</span></a></h2> <p>We are proposing a new type of importer that allows users to use the <code>pkg:</code> <span class="caps">URL</span> scheme to direct Sass to resolve the dependency <span class="caps">URL</span> using the resolution standards and conventions for a specific<span class="widont"> </span>environment.</p> <p>To address the largest use case, we are proposing a built-in Package Importer for the Node ecosystem. Our recommendation is for package authors to define a <code>sass</code> <a href="https://nodejs.org/api/packages.html#conditional-exports">conditional export</a> for entry points to their package in their distributed <code>package.json</code>. For example, a <code>package.json</code> containing:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"exports"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"."</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"sass"</span><span class="token operator">:</span> <span class="token string">"./src/scss/index.scss"</span><span class="token punctuation">,</span> <span class="token property">"import"</span><span class="token operator">:</span> <span class="token string">"./dist/js/index.mjs"</span><span class="token punctuation">,</span> <span class="token property">"default"</span><span class="token operator">:</span> <span class="token string">"./dist/js/index.js"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"./utils"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"sass"</span><span class="token operator">:</span> <span class="token string">"./src/scss/_utils.scss"</span><span class="token punctuation">,</span> <span class="token property">"default"</span><span class="token operator">:</span> <span class="token string">"./dist/js/utils.js"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>would allow a package consumer to<span class="widont"> </span>write:</p> <pre class="language-scss"><code class="language-scss"><span class="token keyword">@use</span> <span class="token string">"pkg:library"</span><span class="token punctuation">;</span> <span class="token keyword">@use</span> <span class="token string">"pkg:library/utils"</span><span class="token punctuation">;</span></code></pre> <p>We wanted to allow Sass package consumers to start using <code>pkg:</code> URLs without requiring changes from the package authors. Because many Sass packages already define their entrypoints in a variety of ways, the Node Package Importer also supports several other ways of defining entrypoints, which cover the majority of implementations we observed. These resolve in the following<span class="widont"> </span>order:</p> <ol> <li> <p><code>sass</code>, <code>style</code>, or <code>default</code> condition in package.json <code>exports</code> that resolves to a Sass or <span class="caps">CSS</span><span class="widont"> </span>file.</p> </li> <li> <p>If there is not a subpath, then find the root<span class="widont"> </span>export:</p> <ol> <li> <p><code>sass</code> key at package.json<span class="widont"> </span>root.</p> </li> <li> <p><code>style</code> key at package.json<span class="widont"> </span>root.</p> </li> <li> <p><code>index</code> file at package root, resolved for file extensions and<span class="widont"> </span>partials.</p> </li> </ol> </li> <li> <p>If there is a subpath, resolve that path relative to the package root, and resolve for file extensions and<span class="widont"> </span>partials.</p> </li> </ol> <p>To better understand and allow for testing against the recommended algorithm, a <a href="https://github.com/oddbird/sass-pkg-test">Sass pkg: test</a> repository has been made with a rudimentary implementation of the<span class="widont"> </span>algorithm.</p> <h2 id="details" tabindex="-1">Details<a class="anchor" href="#details"><span class="visuallyhidden">Details permalink</span></a></h2> <p>Because the Node ecosystem is primarily concerned with distributing JavaScript, we needed to find a way to allow package authors to distribute Sass. In many ways, the closest analog is to TypeScript types, as type definition files are distributed alongside compiled code, and are often not the primary export in a<span class="widont"> </span>package.</p> <p>Based on our <a href="https://github.com/oddbird/sass-pkg-test/tree/main/analysis">analysis</a> of over 400 packages for design libraries and frameworks, we anticipate package consumers will be able to use <code>pkg:</code> URLs for many packages without package authors needing to make changes. Package authors will be able to better specify and document how to consume the<span class="widont"> </span>packages.</p> <p>Our analysis also showed that packages distributing Sass almost always did so alongside JavaScript code. Some packages do specify a Sass file as a <code>sass</code> key or a <span class="caps">CSS</span> file as a <code>style</code> key at the <code>package.json</code> root, both of which will be supported by the Node Package Importer. We observed little usage of <code>main</code> or <code>module</code> keys for <span class="caps">CSS</span> or Sass, which will not be<span class="widont"> </span>supported.</p> <p>While we observed low usage currently of <a href="https://nodejs.org/api/packages.html#conditional-exports">conditional exports</a> fields for specifying Sass and <span class="caps">CSS</span> files, we expect this to grow as package authors adopt conditional exports. In addition, build tools like <a href="https://github.com/vitejs/vite/pull/7817">Vite</a>, <a href="https://github.com/parcel-bundler/parcel/blob/2d2400ded4615375ee6bd53ef77b4857ad1591dd/packages/transformers/sass/src/SassTransformer.js#L163">Parcel</a> and <a href="https://github.com/webpack-contrib/sass-loader/blob/02df41203adfda96959e56abb43bd35a89ec11ba/src/utils.js#L514">Sass Loader for Webpack</a> all currently resolve Sass paths using the <code>sass</code> and <code>style</code> custom conditions.</p> <p>Users will need to opt-in to the new Package Importer by importing the <code>nodePackageImporter</code> from <code>sass</code> and including it in the list of <code>importers</code>. This won’t be available for Sass in the<span class="widont"> </span>browser.</p> <pre class="language-js"><code class="language-js"><span class="token keyword">const</span> sass <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'sass'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> sass<span class="token punctuation">.</span><span class="token function">compile</span><span class="token punctuation">(</span><span class="token string">'style.scss'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">importers</span><span class="token operator">:</span> <span class="token punctuation">[</span>sass<span class="token punctuation">.</span>nodePackageImporter<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <h2 id="next-steps" tabindex="-1">Next steps<a class="anchor" href="#next-steps"><span class="visuallyhidden">Next steps permalink</span></a></h2> <p>This is still in the proposal phase, so we are open to feedback. Review the proposal <a href="https://github.com/sass/sass/blob/main/proposal/package-importer.d.ts.md">on Github</a>, and <a href="https://github.com/sass/sass/issues/new">open an issue</a> with any thoughts or concerns you may<span class="widont"> </span>have.</p> </div> </div> <div class="sl-c-alert"> <div class="sl-l-container sl-c-list-horizontal-wrapper" aria-labelledby="release-nav"> <ul class="sl-l-grid--justify-center"> <li id="release-nav">Current Releases:</li> <li><span class="release-name"><a href="/dart-sass">Dart Sass</a> <a href="https://github.com/sass/dart-sass/releases/tag/1.85.0">1.85.0</a></span></li> <li><span class="release-name"><a href="/libsass">LibSass</a> <a href="https://github.com/sass/libsass/releases/tag/3.6.6">3.6.6</a></span></li> <li><span class="release-name"><a href="/ruby-sass">Ruby Sass</a> <span aria-label="coffin" role="presentation">⚰</span></span></li> <li class="sl-l-grid__column sl-l-large-grid__column--auto-size"><a href="/implementation">Implementation Guide</a></li> </ul> </div> </div> </main> <footer class="site-footer contentinfo" itemtype="https://schema.org/WPFooter" itemscope="itemscope" role="contentinfo"> <div class="sl-l-container"> <div class="sl-l-grid sl-l-grid--full sl-l-large-grid--fit sl-l-large-grid--center sl-l-large-grid--gutters"> <div class="sl-l-grid__column"> <p>Sass © 2006–2025 the Sass team, and numerous contributors. It is available for use and modification under the <a href="https://github.com/sass/dart-sass/blob/main/LICENSE"><span class="caps">MIT</span><span class="widont"> </span>License</a>.</p> <nav class="sl-c-list-horizontal-wrapper sl-c-list-wrap"> <ul> <li><a href="https://github.com/sass">Sass on GitHub</a></li> <li><a href="https://github.com/sass/sass-site">Website Source Code</a></li> <li><a href="/styleguide">Style Guide</a></li> <li><a href="/community-guidelines">Community Guidelines</a></li> </ul> </nav> </div> <div class="sl-l-grid__column sl-l-large-grid__column--auto-size sl-l-large-grid--justify-right sl-c-footer-link-grid"> <a class="twitter-follow-button" href="https://twitter.com/SassCSS" data-show-count="false" data-size="large">Follow @SassCSS</a> <a class="sl-image-link" href="https://www.netlify.com"><img alt="Deploys by Netlify" src="https://www.netlify.com/v3/img/components/netlify-color-bg.svg"/></a> <a class="sl-image-link mac-stadium-icon" href="https://www.macstadium.com/"><img alt="Powered by MacStadium" class="mac-stadium-icon" src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png"></a> </div> </div> </div> </footer> <script src="https://cdn.jsdelivr.net/npm/@docsearch/js@alpha"></script> <script> docsearch({ container: '#docsearch', appId: 'Q9MULQONSV', indexName: 'sass-lang', apiKey: '2ebc7881b79986f58dc2f424769bf3fc', transformItems: function(hits) { var domain = window.location.origin + "/"; hits.forEach(function(hit) { if (! hit.url.startsWith(domain)) { hit.url = hit.url.replace(/^https?:\/\/[^\/]+\//, domain); } }); return hits; } }); </script> <script> !function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https'; if (! d.getElementById(id)) { js = d.createElement(s); js.id = id; js.src = p + '://platform.twitter.com/widgets.js'; fjs.parentNode.insertBefore(js, fjs); } }(document, 'script', 'twitter-wjs'); </script> <script src="/assets/dist/js/sass.js"></script> <!-- Current page: /blog/rfc-package-importer/ --> </body> </html>