Giter Club home page Giter Club logo

esbuild-sass-plugin's Introduction

cooltext394785080075403 image

Build Status

A plugin for esbuild to handle Sass & SCSS files.

Features

  • PostCSS & CSS modules
  • support for constructable stylesheet to be used in custom elements or dynamic style to be added to the html page
  • Support for Sass Embedded Async API. (thanks to @NathanBeddoeWebDev)
  • caching
  • url rewriting
  • pre-compiling (to add global resources to the sass files)

Breaking Changes (...maybe)

  • It turned out that sass-embedded is not available on every platform (this sucks!) so, in order to improve the compatibility of the plugin I had to make it a peer dependency. Once installed, it can be used by setting the new option embedded to true

Install

$ npm i esbuild-sass-plugin

Usage

Just add it to your esbuild plugins:

import {sassPlugin} from 'esbuild-sass-plugin'

await esbuild.build({
  ...
  plugins: [sassPlugin()]
})

Options

You can pass a series of options to the plugin that are a superset of Sass compile string options.
The following are the options specific to the plugin with their defaults whether provided:

Option Type Default
filter regular expression (in Go syntax) /.(s[ac]ss|css)$/
type "css"
"style"
"lit-css"
"css-text"
(css:string,nonce?:string)=>string
"css"
cache boolean or Map true (there is one Map per namespace)
transform function
loadPaths string[] []
precompile function
importMapper function
cssImports boolean false
nonce string
prefer string preferred package.json field
quietDeps boolean false
embedded boolean false
Two main options control the plugin: filter which has the same meaning of filter in esbuild
allowing to select the URLs handled by a plugin instance and then type that's what specifies how the css should be rendered and imported.

filter

The default filter is quite simple but also quite permissive. When specifying a custom regex bear in mind that this is in Go syntax

If you have URLs in your imports and you want the plugin to ignore them you can't just a filter expression like: /^(?!https?:).*\.(s[ac]ss|css)$/ because Go regex engine doesn't support lookarounds but you can use esbuild's external option to ignore these imports or try a solution like this one.

You can try to list multiple plugin instances in order so that the most specific RegEx come first:

await esbuild.build({
  ...
  plugins: [
    sassPlugin({
      filter: /\.module\.scss$/,
      transform: postcssModules()
    }),
    sassPlugin({
      filter: /\.scss$/
    }),
  ],
  ...   
})

embedded

This option enables the usage of the faster sass-embedded and is false by default just for compatibility reason.

Make sure that the sass-embedded has been installed as a peer dependency or add it manually to your project if your package manager doesn't do that for you then set this option to true and enjoy the speed boost!

type

The example in Usage uses the default type css and will use esbuild CSS loader so your transpiled Sass will be in index.css alongside your bundle.

In all other cases esbuild won't process the CSS content which instead will be handled by the plugin.

if you want url() resolution or other processing you have to use postcss like in this example

NOTE: Since version 2.7.0 the css type works also with postcss, CSS modules and more in general with any transformation function by keeping an internal cache of CSS chunks (virtual CSS files) importing them in the module wrapping the contents

type: "local-css"

This mode uses esbuild's built-in CSS modules support (i.e. the local-css loader). Use this for lightweight Sass integration that then leverages esbuild's built-in CSS processing features:

await esbuild.build({
  ...
  plugins: [
    sassPlugin({
      filter: /\.module\.scss$/,
      type: 'local-css'
    }),
    sassPlugin({
      filter: /\.scss$/
      type: 'css'
    }),
  ],
  ...   
})

type: "style"

In this mode the stylesheet will be in the javascript bundle and will be dynamically added to the page when the bundle is loaded.

type: "css-text"

You can use this mode if you want to use the resulting css text as a string import

await esbuild.build({
  ...
  plugins: [sassPlugin({
    type: "css-text",
    ...   // for the options availanle look at 'SassPluginOptions' in index.ts
  })]
})

...and in your module do something like

import cssText from './styles.scss'

customElements.define('hello-world', class HelloWorld extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
    this.sheet = new CSSStyleSheet();
    this.sheet.replaceSync(cssText);
    this.shadowRoot.adoptedStyleSheets = [this.sheet];
  }
}

type: "lit-css"

Or you can import a lit-element css result using type: "lit-css"

import styles from './styles.scss'

@customElement("hello-world")
export default class HelloWorld extends LitElement {

  static styles = styles

  render() {
    ...
  }
}

type: 'function'

You can now provide your own module factory as type. It has to be a function that receives 2 parameters the css text and the nonce token and returns the source content to be added in place of the import.

Look in test/fixtures folder for more usage examples.

cache

The cache is enabled by default and can be turned off with cache: false. Each plugin instance creates and maintain its own cache (as a Map) and this cache lives for the duration of the build. If you want to pass a Map to preserve the cache amongst subsequent builds bear in mind that sharing the very same cache between different instances might work just fine or it might lead to issues if the contents are incompatible.

If you are not sure of what to do just keep a separate Map for each plugin instance.

cssImports

when this is set to true the plugin rewrites the node-modules relative URLs starting with the ~ prefix so that esbuild can resolve them similarly to what css-loader does.

Although this practice is kind of deprecated nowadays some packages out there still use this notation (e.g. formio)
so I added this feature to help in cases like this one.

nonce

in presence of Content-Security-Policy (CSP) the nonce option allows to specify the nonce attribute for the dynamically generated <style>

If the nonce string is a field access starting with window, process or globalThis it is left in the code without quotes.

sassPlugin({
  type: 'style',
  nonce: 'window.__esbuild_nonce__'
})

This allows to define it globally or to leave it for a subsequent build to resolve it using esbuild define.

define: {'window.__esbuild_nonce__': '"12345"'}

prefer

when this option is specified it allows to import npm packages which have sass or style fields preferring those to main.

NOTE: This is an experimental feature

  • it replaces the internal use of require.resolve with browserify resolve.sync
  • it only applies to import prefixed by ~

importMapper

A function to customize/re-map the import path, both import statements in JavaScript/TypeScript code and @import in Sass/SCSS are covered.
You can use this option to re-map import paths like tsconfig's paths option.

e.g. given this tsconfig.json which maps image files paths

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@img/*": [
        "./assets/images/*"
      ]
    }
  }
}

now you can resolve these paths with importMapper

await esbuild.build({
  ...,
  plugins: [sassPlugin({
    importMapper: (path) => path.replace(/^@img\//, './assets/images/')
  })]
})

precompile

- Rewriting relative url(...)s

If your sass reference resources with relative urls (see #48) esbuild will struggle to rewrite those urls because it doesn't have idea of the imports that the Sass compiler has gone through. Fortunately the new importer API allows to rewrite those relative URLs in absolute ones which then esbuild will be able to handle.

Here is an example of how to do the url(...) rewrite (make sure to handle \ in Windows)

const path = require('path')

await esbuild.build({
  ...,
  plugins: [sassPlugin({
    precompile(source, pathname) {
      const basedir = path.dirname(pathname)
      return source.replace(/(url\(['"]?)(\.\.?\/)([^'")]+['"]?\))/g, `$1${basedir}/$2$3`)
    }
  })]
})

- Globals and other Shims (like sass-loader's additionalData)

Look for a complete example in the precompile fixture.

Prepending a variable for a specific pathname:

const context = { color: "blue" }

await esbuild.build({
  ...,
  plugins: [sassPlugin({
    precompile(source, pathname) {
      const prefix = /\/included\.scss$/.test(pathname) ? `
            $color: ${context.color};
          ` : env
      return prefix + source
    }
  })]
})

Prepending an @import of globals file only for the root file that triggered the compilation (to avoid nested files from importing it again):

const context = { color: "blue" }

await esbuild.build({
  ...,
  plugins: [sassPlugin({
    precompile(source, pathname, isRoot) {
      return isRoot ? `@import '/path/to/globals.scss';\n${source}` : source
    }
  })]
})

transform

async (this: SassPluginOptions, css: string, resolveDir?: string) => Promise<string>

It's a function which will be invoked before passing the css to esbuild or wrapping it in a module.
It can be used to do PostCSS processing and/or to create modules like in the following examples.

NOTE: Since v1.5.0 transform can return either a string or an esbuild LoadResult object.
This is what postcssModules uses to pass Javascript modules to esbuild bypassing the plugin output altogether.

- PostCSS

The simplest use case is to invoke PostCSS like this:

const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const postcssPresetEnv = require('postcss-preset-env')

esbuild.build({
  ...,
  plugins: [sassPlugin({
    async transform(source, resolveDir) {
      const {css} = await postcss([autoprefixer, postcssPresetEnv({stage: 0})]).process(source)
      return css
    }
  })]
})

- CSS Modules

A helper function is available to do all the work of calling PostCSS to create a CSS module. The usage is something like:

const {sassPlugin, postcssModules} = require('esbuild-sass-plugin')

esbuild.build({
  ...,
  plugins: [sassPlugin({
    transform: postcssModules({
      // ...put here the options for postcss-modules: https://github.com/madyankin/postcss-modules
    })
  })]
})

postcssModules produces Javascript modules which are handled by esbuild's js loader

postcssModules also accepts an optional array of plugins for PostCSS as second parameter.

Look into fixture/css-modules for the complete example.

NOTE: postcss and postcss-modules have to be added to your package.json.

quietDeps

In order for quietDeps to correctly identify external dependencies the url option is defaulted to the importing file path URL.

The url option creates problems when importing source SASS files from 3rd party modules in which case the best workaround is to avoid quietDeps and mute the logger if that's a big issue.

pnpm

There's a working example of using pnpm with @material design in issue/38

Benchmarks

Windows 11 Pro - i7-490K CPU @ 4.00GHz - RAM 32GB - SSD 500GB

Given 24 × 24 = 576 lit-element files & 576 imported CSS styles plus the import of the full bootstrap 5.1

sass-embedded sass-embedded (no cache) dart sass dart sass (no cache)
initial build 731.312ms 779.363ms 2.450s 2.450s
rebuild (.ts change) 170.35ms 188.861ms 179.125ms 1.710s
rebuild (.ts change) 155.802ms 167.413ms 176.849ms 1.576s
rebuild (.scss change) 203.746ms 160.601ms 188.164ms 1.575s
rebuild (.scss change) 152.733ms 144.754ms 145.835ms 1.520s

esbuild-sass-plugin's People

Contributors

3eif avatar ccapndave avatar dlepaux avatar glromeo avatar gravllift avatar hsiaosiyuan0 avatar kohlmannj avatar livog avatar mattlewis92 avatar maxiruani avatar minnozz avatar pc-erin avatar rassell avatar rubik0000 avatar shellscape avatar sospartan avatar toastal avatar wfleming avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

esbuild-sass-plugin's Issues

Relative Url Rewriting

Sass doesn't like relative urls in non-entry files. Webpack gets around this shortcoming with the resolve-url-loader plugin, which uses sourcemaps generated by Sass to rewrite the urls after the compilation has completed.

Esbuild doesn't yet output sourcemaps into the build output proper, but this plugin could still have access to them by adding a sourcemap option to the sass call and then saving the map property that's returned. With those maps in memory, similar relative url rewriting could be achieved.

nonce global issue

Hi Gianluca,

thanks for your update for nonce previously. we have another a issue with nonce

we use esbuild-sass-plugin like this,

app A has app B as dependency, app B used esbuild-sass-plugin to esbuild.

following code is app B code

esbuild.build({
  entryPoints: ['src/index.ts'],
  outdir: 'dist/esm',
  bundle: true,
  sourcemap: true,
  minify: true,
  splitting: true,
  format: 'esm',
  target: 'esnext',
  plugins: [nodeExternalsPlugin(), sassPlugin({
    type: 'style',
  })],
})

since nonce need to be dynamic for each app used app B.
we cant just hardcoded nonce like this

esbuild.build({
  entryPoints: ['src/index.ts'],
  outdir: 'dist/esm',
  bundle: true,
  sourcemap: true,
  minify: true,
  splitting: true,
  format: 'esm',
  target: 'esnext',
  plugins: [nodeExternalsPlugin(), sassPlugin({
    type: 'style',
    nonce:'12345'
  })],
})

so is possible to fix this line (if nonce not pass) to

style.setAttribute("nonce", window.__esbuild_nonce__);

so in this way its very easy to config in app A, just add

window.__esbuild_nonce__ = "dynamic_12345"

since i cant find esbuild_nonce in esbuild, so i just make this variable up, but webpack has __webpack_nonce__

thanks

@glromeo

README precompile example path separator issue

There's something strange going on with path separators on Windows in the precompile example in the readme. The path.dirname call correctly reads out the base directory as, for example, C:\\folder\\subFolder, and the path is successfully combined with the item in the url call, generating something like C:\\folder\\subFolder/./assets/image/png.

If I add a dummy plugin to esbuild, I can see that an onResolve call will then be attempted to be made to C:foldersubFolder/./assets/image/png by this plugin.

I was able to resolve the issue by either double escaping the windows path seperator...

return source.replace(
  /(url\(['"]?)(\.\.?\/)([^'")]+['"]?\))/g,
  `$1${baseDir.replace(/\\/g, '\\\\')}/$2$3`
);

... or by using / instead:

return source.replace(
  /(url\(['"]?)(\.\.?\/)([^'")]+['"]?\))/g,
  `$1${baseDir.replace(/\\/g, '/')}/$2$3`
);

I'm not exactly sure what's going on there, but at least its workaroundable.

SourceMap fix was removed in v2.1.0

Hi, thanks for awesome plugin.
Recently I checked release notes and was amazed that you did a fix for sourcemaps exactly what I wanted.
But unfortunately the fix disappeared later from the code.

My config:

const buildResult = await esbuild.build({
			entryPoints: ['./src/index.ts'],
			loader: {'.svg': 'dataurl'},
			format: 'cjs',
			target: ['es2019'],
			bundle: true,
			minify: true,
			sourcemap: true,
			outfile: './dist/demobook.min.js',
			plugins: [sassPlugin({
				type: 'css-text',
				style: 'compressed',
				sourcemap: false,
			})]
		});

As you can see I want to have sourcemap only for javascript, not for sass.

Please kindly refer to history below.

Original fix I refer to: b2382a7

Was removed during this change: ab26090#diff-57716bc53661ccdb4571d571f1a5493294848965ae6068dbb3d9a1167d2cc1a6R59

Also it would be nice to have the same case for sourcemap (instead of sourceMap) option field name, the same like in esbuild.

Pure esbuild sass files with relative assets fails to load

I do have a relative import issue:

✘ [ERROR] Could not resolve "./NunitoSans-Bold.ttf"
    sass-plugin-0:/....../LoadingAlert.scss:51:11:

The file import a relative sass file

I found other issues on the repo like @import "../../../fonts/fonts.scss"; and this file is importing the font.

I do see other similar issues on the repo but I don't really get it... I try to find if this is due to dart-sass but no clue either....
Currently this is working with webpack but I do think like #19 this ise due to special resolver.

What can I do to solve my issue?

Use the plugin with esbuild watch mode

Hello, and thanks for the plugin.

I'm trying to use the plugin with my build following the README instructions without any additional configuration and using a simple import in my React code, but I can't make it work with watch mode, so when I make changes to my ./styles/main.scss file the build doesn't run again.

// esbuild.js
const { sassPlugin } = require('esbuild-sass-plugin');

...
  plugins: [sassPlugin()],
...
// app.js
...
import './styles/main.scss';

export default App => (
  ...
);

Thank you for any help you can provide.

Unable to resolve exports path in package.json

Not sure if this is an issue with esbuild in general, or if there's anything that can be done with the plugins.

Take the package swiper as an example. In its package.json it lists all its exports such as

{
    "exports": {
        ...
        "./scss": "./swiper.scss",
        ...
    }
}

So if I write import "swiper/scss"; it will resolve to "swiper/swiper.scss". And also I couldn't use the latter instead, or it will give me an error saying that this path is not exported by the package.
Now the problem is, since "swiper/scss" is not a path ending in ".scss", this plugin fails to intercept such importing. I could, of course, manually add it to the filter to make it work, but that wouldn't be practical if there're many such imports in my project. The plugin should be able to intercept, besides explicitly written paths, resolved path as well.

If esbuild is in watch mode and there is an error in sass, the plugin stops working

So far everything has been working great except for this one thing.

My setup:

import esbuild from "esbuild";
import { sassPlugin } from "esbuild-sass-plugin";

esbuild.build({
	entryPoints: [
		"test.scss",
	],
	bundle: false,
	outfile: "test.css",
	sourcemap: true,
	plugins: [
		sassPlugin({
		})
	],
	watch: (error, result) => {
		if (error)
			console.error("rebuild SASS failed")
		else
			console.log("rebuild SASS succeeded")
	}
}).catch(() => process.exit(1));

If I do any valid changes to test.scss, everything works great.

When there is an error in tests.scss (missing semicolon), the error is displayed as expected. But when fixing the error or doing any other changes to test.scss nothing happens. You then have to manually restart the script.

scoped scss

New to esbuild with this sass-plugin and trying to recreate the usual behavior of scoped modules in react:

import styles from 'test.scss'

...

<h1 className={styles.classInsideSCSS}>title</h1>

does this plugin support this or would it require more to it?

Thanks!

Issues importing fonts via url when bundling

I'm running into issues when attempting to use the open-iconic package with this plugin. My esbuild configuration looks like so:

esbuild.build({
  entryPoints: ['./Assets/Scripts/index.ts', './Assets/Styles/styles.scss'],
  outdir: 'wwwroot',
  bundle: true,
  minify: argumentObj.mode !== 'development',
  sourcemap: argumentObj.mode === 'development',
  format: 'esm',
  loader: {
    '.eot': 'file',
    '.woff': 'file',
    '.ttf': 'file',
    '.svg': 'file',
    '.otf': 'file',
  },
  plugins: [sassPlugin()],
});

styles.scss contains the line...

@import '~open-iconic/font/css/open-iconic';

which in turn contains...

$iconic-font-path: '../fonts/' !default;

@font-face {
  font-family: 'Icons';
  src: url('#{$iconic-font-path}open-iconic.eot');
  src: url('#{$iconic-font-path}open-iconic.eot?#iconic-sm') format('embedded-opentype'), url('#{$iconic-font-path}open-iconic.woff') format('woff'), url('#{$iconic-font-path}open-iconic.ttf') format('truetype'), url('#{$iconic-font-path}open-iconic.otf') format('opentype'), url('#{$iconic-font-path}open-iconic.svg#iconic-sm') format('svg');
  font-weight: normal;
  font-style: normal;
}

and finally when run, the build returns this exception

 > sass:./Assets/Styles/styles.scss:4:11: error: Could not resolve "../fonts/open-iconic.eot"
    4 │   src: url("../fonts/open-iconic.eot");
      ╵            ~~~~~~~~~~~~~~~~~~~~~~~~~~

 > sass:./Assets/Styles/styles.scss:5:11: error: Could not resolve "../fonts/open-iconic.eot?#iconic-sm"
    5 │   src: url("../fonts/open-iconic.eot?#iconic-sm") format("embedded-opentype"), url("../fonts/open-iconic.woff") format("woff"), url("../fonts/open-iconic.ttf") format("truetype"), url("../fonts/open-iconic.otf") format("opentype"), url("../fonts/open-iconic.svg#iconic-sm") format("svg");
      ╵            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 > sass:./Assets/Styles/styles.scss:5:83: error: Could not resolve "../fonts/open-iconic.woff"
    5 │   src: url("../fonts/open-iconic.eot?#iconic-sm") format("embedded-opentype"), url("../fonts/open-iconic.woff") format("woff"), url("../fonts/open-iconic.ttf") format("truetype"), url("../fonts/open-iconic.otf") format("opentype"), url("../fonts/open-iconic.svg#iconic-sm") format("svg");
      ╵                                                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~

 > sass:./Assets/Styles/styles.scss:5:132: error: Could not resolve "../fonts/open-iconic.ttf"
    5 │   src: url("../fonts/open-iconic.eot?#iconic-sm") format("embedded-opentype"), url("../fonts/open-iconic.woff") format("woff"), url("../fonts/open-iconic.ttf") format("truetype"), url("../fonts/open-iconic.otf") format("opentype"), url("../fonts/open-iconic.svg#iconic-sm") format("svg");
      ╵                                                                                                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~

 > sass:./Assets/Styles/styles.scss:5:184: error: Could not resolve "../fonts/open-iconic.otf"
    5 │   src: url("../fonts/open-iconic.eot?#iconic-sm") format("embedded-opentype"), url("../fonts/open-iconic.woff") format("woff"), url("../fonts/open-iconic.ttf") format("truetype"), url("../fonts/open-iconic.otf") format("opentype"), url("../fonts/open-iconic.svg#iconic-sm") format("svg");
      ╵                                                                                                                                                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~

 > sass:./Assets/Styles/styles.scss:5:236: error: Could not resolve "../fonts/open-iconic.svg#iconic-sm"
    5 │   src: url("../fonts/open-iconic.eot?#iconic-sm") format("embedded-opentype"), url("../fonts/open-iconic.woff") format("woff"), url("../fonts/open-iconic.ttf") format("truetype"), url("../fonts/open-iconic.otf") format("opentype"), url("../fonts/open-iconic.svg#iconic-sm") format("svg");
      ╵    

If I set bundle to false in the esbuild configuration, the file executes without error, however the relative url paths remain in the final source:

@font-face {
  font-family: "Icons";
  src: url(../fonts/open-iconic.eot);
  src:
    url(../fonts/open-iconic.eot?#iconic-sm) format("embedded-opentype"),
    url(../fonts/open-iconic.woff) format("woff"),
    url(../fonts/open-iconic.ttf) format("truetype"),
    url(../fonts/open-iconic.otf) format("opentype"),
    url(../fonts/open-iconic.svg#iconic-sm) format("svg");
  font-weight: normal;
  font-style: normal;
}

which doesn't help me, as the output is not located in the node_modules folder.

Altering the loader setting in the esbuild configuration seems to have no effect one way or another - I receive identical results with all types set to dataurl or the object missing entirely.

Versions:
[email protected]
[email protected]

Sourcemaps messed up when using @use (or @import)

When using @use or @import the sourcemaps aren't correct anymore:

Selection_008

I made a small reproduction again: https://github.com/Epskampie/esbuild-sass-sourcemaps

As you see, if you run npm run esbuild:build and open with_use.html in your browser the sourcemaps are wrong.
When your run npm run sass:build they are correct.

Also, in without_use.html they are correct in both cases.

The problem seems to occur only in the "root" sass file, if you @import deeper it starts working again.

Thanks for the great plugin BTW!

Can't import modules when using pnpm

I am trying to import @material/data-table/data-table but I get this error Cannot find module '@material/animation/_functions' from it works fine if I use npm instead of pnpm. If I add the --shamefully-hoist flag to pnpm then it also works fine. I assume it's related to pnpm putting dependencies in .pnpm and sass maybe does not know how to find them.

plugin and fortawesome?

hello, I tried your plugin and it works great, except when I try to add fortawesome which also adds font files into the scss files. (i actually try to converting our build from webpack to esbuild, our speedup is way above 10x)

currently we defined it the following way:

$fa-font-path: "~@fortawesome/fontawesome-pro/webfonts";
@import '~@fortawesome/fontawesome-pro/scss/fontawesome';
@import '~@fortawesome/fontawesome-pro/scss/solid';
@import '~@fortawesome/fontawesome-pro/scss/brands';
@import '~@fortawesome/fontawesome-pro/scss/regular';
@import '~@fortawesome/fontawesome-pro/scss/light';

and the sassPlugin config:

    sassPlugin({ 
        importer: createImporter(),
        includePaths: [
            path.resolve(__dirname, './node_modules'),
        ],
     }), 

I also configured loaders for the font files:

loaders: { 
    '.png': 'file',
    '.svg': 'file',
    '.ttf': 'file',
    '.woff': 'file',
    '.woff2': 'file',
    '.eot': 'file',
}

(there is also a free version, which just uses fontawesome-free and does not support everything.)

at the moment we basically use webpack and dart-sass, we configured it so that the font files gets put into the build output directory, we tought that the same works with esbuild/this plugin.
unfortunatly it fails with:

sass-plugin-0:/LONG_PATH/ClientApp/scss/site.scss:15450:11: error: Could not resolve "~@fortawesome/fontawesome-pro/webfonts/fa-solid-900.woff2" (mark it as external to exclude it from the bundle)
sass-plugin-0:/LONG_PATH/ClientApp/scss/site.scss:15450:93: error: Could not resolve "~@fortawesome/fontawesome-pro/webfonts/fa-solid-900.ttf" (mark it as external to exclude it from the bundle)
sass-plugin-0:/LONG_PATH/ClientApp/scss/site.scss:15467:11: error: Could not resolve "~@fortawesome/fontawesome-pro/webfonts/fa-brands-400.woff2" (mark it as external to exclude it from the bundle)
sass-plugin-0:/LONG_PATH/ClientApp/scss/site.scss:15467:94: error: Could not resolve "~@fortawesome/fontawesome-pro/webfonts/fa-brands-400.ttf" (mark it as external to exclude it from the bundle)
sass-plugin-0:/LONG_PATH/ClientApp/scss/site.scss:17356:11: error: Could not resolve "~@fortawesome/fontawesome-pro/webfonts/fa-regular-400.woff2" (mark it as external to exclude it from the bundle)

am I missing something? on how to make this work, I could not find good examples about that problem.
(the files exist at node_modules/@fortawesome/fontawesome-pro/webfonts)

Edit:

what's strange is that our own font files are correctly exported. i.e. we also include OpenSans that are not coming from node_modules and they work correctly.

Can't find stylesheet to import

I have looked at similar issues filed about this but was unable to find a solution. I also tried the 2.0-alpha version.

If you clone this repo: https://github.com/foundation/proton-sites-template

After you install, run a yarn build. You will see that it fails trying to load a Sass library. Here is a snippet from the esbuild script.

await esbuild.build({
       ...
        plugins     : [
            sassPlugin({includePaths: [
                "node_modules/foundation-sites/scss/",
                "node_modules/motion-ui/src/",
            ]}),
        ],
    }).catch(() => process.exit(1));

If I run sass directly from the CLI. It works like a charm.

$ sass -I node_modules/foundation-sites/scss -I node_modules/motion-ui/src src/styles/site.scss output.css

Question: is there some way to remain the path in `url()` as is?

For example, I currently have something like:

@font-face
  font-family: 'Open Sans'
  src: url('/public/open-sans.woff2')

And the url here is the path directly to where the font file would be in the output directory (since I would manually copy the public folder to the output directory) instead of where the source of the font is.

Currently, with bundle: true (i.e. I want to bundle the .sass files), The only way I have figured out to make this piece of code work is to change the url path to the source of the font, and set loader: { '.woff2': 'file' } or loader: { '.woff2': 'dataurl' } to esbuild. But both of the settings are not really meet my need, since with the both loaders the font file is copied twice, one manually and one by esbuild, and with file the filename is renamed and the url is remapped. On the other hand, not setting the loader would throw the error of Could not resolve "/public/open-sans.woff2", and settings exclude: \.woff2$\ in this plugin cannot solve this error.

What I want to achieve is to make the path inside url('/public/open-sans.woff2') remains as is after the bundling. I would appreciate if someone could give me some advice.

Add support for the incremental/watch mode

It's now possible to tell esbuild which files are being watched for the incremental/watch mode to work. Just return the watchFiles in the onResolve. Take a look at this, for an example:

https://github.com/koluch/esbuild-plugin-sass/blob/main/index.ts#L45

Could you please support this in this plugin, so that it would also watch for changes to our sass files?

I'm switching from esbuild-plugin-sass to this one because I want to be able to use the style element creation approach for some of my CSS sources. But I miss the automatic rebuilding feature when the stylesheet changes. Could you please take a look at this?

Imports from node_modules

I'm trying to use the material-components-web package https://github.com/material-components/material-components-web.

I can create node_modules specific imports like - @use "../../../node_modules/@material/theme" - but within the material components themselves, there are additional @forward, @use, @import statements that will not resolve.

Ideally I'd like to be able to use

@use "@material/theme" with (
  $primary: white,
  $secondary: #285c86,
  $on-primary: white,
  $on-secondary: white,
  $background: #17243b,
  $surface: #17243bc7,
);

or

@use '@material/button/mdc-button';

Is there a way to support sass components from node_modules like this? Or is this an esbuild issue? There is a similar issue here, with a suggested solution - koluch/esbuild-plugin-sass#15

Cache doesn't let apply the changes to the output file.

Sup @glromeo, I'm here again abusing of your time.

While working on the SASS files in my project, I noticed the changes I made weren't being applied to the output file even when my dev server detected the "changes" and reloaded the browser.

I knew it because I checked the output CSS file and the changes weren't there.

So I disabled the cache in the plugin and everything started working again.

This was my config with the cache enabled:

const esbuild = require('esbuild');
const chokidar = require('chokidar');
const { sassPlugin } = require('esbuild-sass-plugin');
const globalsPlugin = require('esbuild-plugin-globals');
const pkg = require('./package.json');

const DEV = process.argv.includes('--dev');
const cache = new Map();
const getTime = time => ((Date.now() - time) / 1000).toFixed(2);

const sharedConfig = {
    define: { 'process.env.NODE_ENV': DEV ? '"development"' : '"production"' },
    bundle: true,
    minify: !DEV,
    incremental: DEV,
    sourcemap: DEV && 'inline',
    external: ['react', 'react-is', 'react-dom', 'styled-components'],
    loader: { '.js': 'jsx' },
    plugins: [sassPlugin({ cache, implementation: 'node-sass' })],
};

const buildMain = async () => {
    const time = Date.now();
    await esbuild.build({
        entryPoints: ['src/app.js'],
        outfile: pkg.main,
        platform: 'node',
        format: 'cjs',
        ...sharedConfig,
    });
    console.log(`CJS build time: ${getTime(time)}`);
};

const buildModule = async () => {
    const time = Date.now();
    await esbuild.build({
        entryPoints: ['src/app.js'],
        outfile: pkg.module,
        platform: 'node',
        format: 'esm',
        ...sharedConfig,
    });
    console.log(`ESM build time: ${getTime(time)}`);
};

const buildBrowser = async () => {
    const time = Date.now();
    await esbuild.build({
        entryPoints: ['src/index.js'],
        outfile: pkg.browser,
        platform: 'browser',
        format: 'iife',
        globalName: 'awNavigation',
        ...sharedConfig,
        plugins: sharedConfig.plugins.concat([
            globalsPlugin({
                react: 'React',
                'react-is': 'ReactIs',
                'react-dom': 'ReactDOM',
                'styled-components': 'styled',
            }),
        ]),
    });
    console.log(`UMD build time: ${getTime(time)}`);
};

const buildDev = async () => {
    const watcher = chokidar.watch('src', { ignoreInitial: true });
    const result = await buildBrowser();
    watcher.on('change', result.rebuild);
};

const buildProd = async () =>
    await Promise.all([buildMain(), buildModule(), buildBrowser()]);

if (DEV) {
    buildDev();
} else {
    buildProd();
}

I hope this can help you to find the possible issue.

Compatible with esbuild-plugin-pnp?

I'm having trouble getting your plugin to work when using Yarn 2's Pnp feature. I get a lot of the following errors:

> .yarn/cache/sass-npm-1.20.1-bd1594cc3f-16f0ce7a4d.zip/node_modules/sass/sass.dart.js:5241:29: error: [plugin: sass-plugin] MyComponent.scss: no such file or directory

when importing Sass with import styles from "./MyComponent.scss"

Reproduction at https://github.com/onox/yarn-esbuild-bug/tree/2d64c69b780ced693ae9889b38fe3cda1d954976 Run with yarn install and then yarn build.

I also had to add a dummy node_modules folder.

NoSuchMethodError: method not found: 'get$iterator'

When I run my esbuild command using the below config I receive the following error everywhere scss files are imported:

 > src/init.ts:1:7: error: [plugin: sass-plugin] NoSuchMethodError: method not found: 'get$iterator' (J.getInterceptor$ax(...).get$iterator is not a function)
    1 │ import "./index.scss";

I'm new to esbuild so apologies if it's not setup correctly but I'm stuck as to how to solve this?

esbuild.js:

import path, { dirname } from "path";
import { fileURLToPath } from "url";

import { build } from "esbuild";
import { ScssModulesPlugin as scssModulesPlugin } from "esbuild-scss-modules-plugin";
import { sassPlugin } from "esbuild-sass-plugin";
import svgPlugin from "esbuild-plugin-svg";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const scssModulesPluginCache = new Map();

build({
  entryPoints: ["./src/index.js"],
  outdir: "build/",
  bundle: true,
  minify: true,
  target: ["es2020"],
  loader: {
    ".js": "jsx",
    ".png": "file"
  },
  plugins: [
    scssModulesPlugin({
      cache: scssModulesPluginCache,
      scssOptions: {
        sourceMap: true,
        includePaths: path.resolve(__dirname, "node_modules")
      },
      generateScopedName: "[name]__[local]___[hash:base64:5]"
    }),
    sassPlugin({ includePaths: path.resolve(__dirname, "node_modules") }),
    svgPlugin()
  ]
}).catch(() => process.exit(1));

includePaths doesn't work?

I'm trying to use includePaths, but can't get it to work. I've made a tiny reproduction repo: https://github.com/Epskampie/esbuild-sass-includepaths

As you can see npm run sass:build works, npm run esbuild:build error out with:
error: Cannot find module 'color/_colors'

Some relevant code:

// bundler.ts
import esbuild from 'esbuild';
import { sassPlugin } from 'esbuild-sass-plugin';

esbuild
    .build({
      plugins: [
        sassPlugin({
          includePaths: [ 'scss_utils', ],
        }),
      ],
      outdir: 'dist',
      entryPoints: ['src/a.scss'],
    })
    .catch((e) => console.error('esbuild', e.message));
// src/a.scss
@use 'colors';

body {
  color: colors.$red;
}
// scss_utils/_colors.scss
$red: red;

Incorrect extension parsing

When resolving an import for a pathname containing a folder with a period and a file with no extensions, such as @import './Assets.Styles/variables';, the extension is incorrectly interpreted as .Styles/variables, rather than an empty string by this block of code:

function resolveRelativeImport(loadPath: string, filename: string): string | null {
const absolute = resolve(loadPath, filename)
let ext = absolute.lastIndexOf('.')
if (ext >= 0) {
return resolveImport(absolute.slice(0, ext), absolute.slice(ext))
} else {
return resolveImport(absolute)
}
}

node-sass dependency

Hi!

Is there a particular reason that node-sass is a peer dependency? npm 7 now installs peer dependencies automatically so the plugin installs both sass and node-sass.

Can we remove it if it is not required for the plugin functionality?

Thanks!

esbuild loader for woff2 being ignored

Hi,

First of all thanks for this awesome plugin!

I have a (minor?) issue with it (or maybe I'm just doing something wrong). I'm being ignored trying to use a custom font...

my code (not all code copied so is easier to follow):

Header.tsx:

import styles from './Header.module.scss';

export function Header() {

Header.module.scss:

@import '../../../styles/main.scss';

.desktopHeader {
      font-family: 'Font-Italic';

main.scss:

@import './color-pallete.scss';
@import './theme.scss';
@import './fonts.scss';

fonts.scss:

@font-face {
    font-family: 'Font-Italic';
    font-style: normal;
    font-weight: 400;
    src: url('../assets/fonts/Pro/Pro-Italic.woff2') format('woff2');
}

esbuild:

const { build } = require('esbuild');
const { sassPlugin, postcssModules } = require('esbuild-sass-plugin');
const { dtsPlugin } = require('esbuild-plugin-d.ts');

build({
  bundle: true,
  sourcemap: true,
  minify: true,
  splitting: true,
  format: 'esm',
  target: ['esnext'],
  entryPoints: ['src/Header.tsx'],
  outdir: 'dist/',
  loader: {
    '.woff': 'dataurl',
    '.woff2': 'dataurl',
  },
  plugins: [
    sassPlugin({
      type: 'css-text',
      transform: postcssModules({}),
    }),
    dtsPlugin(),
  ],
}).catch(e => console.error(e.message));

i have tried diferent loaders with no luck...

many thanks in advance

Support nodeLinker pnp

Hello,

I was taking in consideration to move from yarn nodeLinker with node_modules to the pnp version, and I was wondering have you take a look on how this awesome plugin you created could reach packages with this new way ?

I know there is @yarnpkg/esbuild-plugin-pnp and it works perfectly for my javascript assets but when it comes to compile my stylesheets trouble are coming my way.

If you have any ideas or questions, let me know

loadPaths needs "path" module to be imported

Description

If you use the loadPaths - Option the plugin throws an error "✘ [ERROR] [plugin sass-plugin] path is not defined".
If you import import path from 'path'; this error is gone.

Expected Behaviour

Path module is included by plugin itself

Actual Behaviour

Path module has to be included by user

Plugin fails when the import is an HTTP link

Howdy there, I'm trying to convert our build process to use esbuild, and working with your plugin so far has been great, but I'm having an issue where the plugin tries to resolve links that are used as imports when bundle: true. Here is a link to a repo that I used to reproduce the error, just pull it, install, and run node index.js and you'll see the error. I'll also paste the error here for easy reading. We are using this to download stuff from a proprietary CDN, so i used font awesome as a placeholder, but its the same error.

 > sass:./src/index.scss:1:12: error: [plugin: sass-plugin] Cannot find module 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'                            
Require stack:                                                                                                                                                                                 
- /Users/kthomas/sass-esbuild-repro/node_modules/esbuild-sass-plugin/lib/plugin.js                                                                                                             
- /Users/kthomas/sass-esbuild-repro/node_modules/esbuild-sass-plugin/lib/index.js                                                                                                              
- /Users/kthomas/sass-esbuild-repro/index.js                                                                                                                                                   
      1 │ @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css");
        ╵             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   node_modules/esbuild-sass-plugin/lib/plugin.js:172:18: note: This error came from the "onLoad" callback registered here
    172 │             build.onLoad({ filter: /^([^.]|\.\.?[^/])/, namespace: "sass" }, cached(requireResolve, transform));
        ╵                   ~~~~~~
    at setup (/Users/kthomas/sass-esbuild-repro/node_modules/esbuild-sass-plugin/lib/plugin.js:172:19)
    at handlePlugins (/Users/kthomas/sass-esbuild-repro/node_modules/esbuild/lib/main.js:736:23)
    at Object.buildOrServe (/Users/kthomas/sass-esbuild-repro/node_modules/esbuild/lib/main.js:1021:7)
    at /Users/kthomas/sass-esbuild-repro/node_modules/esbuild/lib/main.js:1745:17
    at new Promise (<anonymous>)
    at Object.build (/Users/kthomas/sass-esbuild-repro/node_modules/esbuild/lib/main.js:1744:14)
    at build (/Users/kthomas/sass-esbuild-repro/node_modules/esbuild/lib/main.js:1620:51)
    at Object.<anonymous> (/Users/kthomas/sass-esbuild-repro/index.js:4:1)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)

In the repo i used a css file at first (the "no error" commit), same settings just without the sass plugin, and it worked fine, once I included the plugin (the "yes error" commit) it got very angry. I've tried all sorts of stuff to try and ignore the import. I tried marking it as external, using sass's importer option, i tried making a esbuild plugin to try and filter out http links and not doing anything to them, but I couldn't get that to work, I never got it to select the link I was looking for. I also tried using @use instead of @import, slightly different error, Can't find stylesheet to import but same essence.

So yeah, I'm kind of lost, if I can fix it with a custom plugin, cool, I will do that, I don't know how to do that but I'll try to figure it out. If you need to change something in this project, I would appreciate the assistance. I can see this being a bit of a tossup in terms of what to do, for me I would just want it to ignore it and let the browser fetch it. However, I can see an argument for fetching the external css file and concatenating it to the file on compile time. That'd slow things down, but if you're making a static site then you probably wouldn't mind.

Add support for workspaces when resolving imports

The current importer finds the first node_modules directory and tries to resolve imports in that directory. However, when you use yarn or npm workspaces, a node_modules directory may also exist in the parent directory (not sure how many levels) and I believe they should all be tried when resolving an import.

why not set a resolve directory when the type is "css"?

I got this error when using the plugin:
image

but, in your code, this looks like intentional:

function transform(path, type) {
      let contents = path.endsWith(".css") ? fs_1.readFileSync(path, "utf-8") : renderSync(path);
      return type === "css" ? {
          contents: contents,
          loader: "css",
      } : {
          contents: makeModule(contents, type),
          loader: "js",
          resolveDir: path_1.dirname(path)
      };
  }

so, why not set a resolve directory when the type is "css"? and how to resolve this problem?

Import node_modules not working when bundle is set to true

When I'm trying to import a file from the node_modules with the symbol ~ then it will give me the following error. Writing the full path manually works.

@import '~dialog-polyfill/dist/dialog-polyfill.css';
esbuild.build({
  entryPoints: [
    '....'
  ],
  bundle: true,
  outdir: './css',
  plugins: [esbuildSass.sassPlugin()]
})
.catch(() => {
  process.exit(1)
})

✘ [ERROR] Could not resolve "~dialog-polyfill/dist/dialog-polyfill.css"

    src/sass/formio.form.scss:2:8:
      2 │ @import '~dialog-polyfill/dist/dialog-polyfill.css';

scoped-url example compile error

file path test/fixture/scoped-url

version:2.2.2

step

cd test/fixture/scoped-url
yarn build 

bug

yarn run v1.22.10
$ node esbuild
✘ [ERROR] Could not resolve "red-stroke.png"

    src/index.scss:4:14:
      4 │   background: url(red-stroke.png);
        ╵               ~~~~~~~~~~~~~~~~~~~

  You can mark the path "red-stroke.png" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "green-stroke.png"

    src/index.scss:10:14:
      10 │   background: url(green-stroke.png);
         ╵               ~~~~~~~~~~~~~~~~~~~~~

  You can mark the path "green-stroke.png" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "blue-stroke.png"

    src/index.scss:16:14:
      16 │   background: url(blue-stroke.png);
         ╵               ~~~~~~~~~~~~~~~~~~~~

  You can mark the path "blue-stroke.png" as external to exclude it from the bundle, which will
  remove this error.

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Multiple files with the same name results in only one file being imported

Hey there!

We noticed a problem when multiple .scss files with the same name but in different folders are imported via JS. The resulting output file only includes the styles of one of the files.

Example:

entrypoint.js

import './component_a'
import './component_b'

component_a/index.js

import './style.scss'

component_a/style.scss

.component_a {
  background: blue;
}

component_b/index.js

import './style.scss'

component_b/style.scss

.component_b {
  background: yellow;
}

Expected Output

/* sass:component_a/style.scss */
.component_a {
  background: blue;
}

/* sass:component_b/style.scss */
.component_b {
  background: yellow;
}

Actual Output

/* sass:./style.scss */
.component_b {
  background: yellow;
}

Output from esbuild without plugin

When running it without esbuild-sass-plugin and regular CSS, esbuild seems to do it correctly and outputs the following:

/* component_a/style.css */
.component_a {
  background: blue;
}

/* component_b/style.css */
.component_b {
  background: yellow;
}

Possible Solution

I played around with the compiled source and was able to get it working correctly with the following change in plugin.js inside build.onResolve which returns the path relative to the basedir:

build.onResolve({ filter: /\.(s[ac]ss|css)$/ }, useExclude((args) => {
    const fullPath = path_1.resolve(args.resolveDir, args.path)
    const relativePath = fullPath.replace(`${options.basedir}/`, '')
    return { path: relativePath, namespace: "sass", pluginData: args };
}));

After this change, the output is as follows:

/* sass:component_a/style.scss */
.component_a {
  background: blue;
}

/* sass:component_b/style.scss */
.component_b {
  background: yellow;
}

Let me know if I missed something or can help any further. If needed, I can also create a reproduction repository.

Cheers!

How do I filter module and global CSS and SCSS files?

plugins: [
  // do module.scss/css first
  sassPlugin({
    filter: /.module.(s[ac]ss|css)$/,
    type: 'style',
    transform: postcssModules({
        // ...put here the options for postcss-modules: https://github.com/madyankin/postcss-modules
    })
  }),
  // do scss/css second
  sassPlugin({
    filter: /.(s[ac]ss|css)$/,
    type: 'style'
  })
];

This is what I'm doing right now we have the following files.

  • foo.css // global
  • bar.scss // global
  • foo.module.css // local CSS module
  • bar.module.scss // local CSS module

why `require.resolve` is set to cwd ?

return require(require.resolve(module, { paths: [basedir] }));

when my esbuild cli was installed in global,and also installed node-sass, then get:

Cannot find module 'node-sass', make sure it's installed. e.g. yarn add -D node-sass Error: Cannot find module 'node-sass'

But, i don't want to install 'node-sass' in my project directory, because it only need in building time and has nothing to do with the project business.

FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal.

When trying to use the implementation: "node-sass" option, I'm running into presumably a libSass error, since the stacktrace is printing C++ errors. I'm not exactly sure what the issue is. Running node-sass from bin doesn't error out like this.

I just don't really know what to look at. I don't know how libSass works, or how the plugin imports and uses it.

FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal.
 1: 0x10dac97e5 node::Abort() (.cold.1) [/.asdf/installs/nodejs/16.2.0/bin/node]
 2: 0x10c77cb39 node::Abort() [/.asdf/installs/nodejs/16.2.0/bin/node]
 3: 0x10c77ccb5 node::OnFatalError(char const*, char const*) [/Users/zachary/.asdf/installs/nodejs/16.2.0/bin/node]
 4: 0x10c8fb800 v8::V8::ToLocalEmpty() [/Users/zachary/.asdf/installs/nodejs/16.2.0/bin/node]
 5: 0x11138b8dc CallbackBridge<Sass_Value*, void*>::operator()(std::__1::vector<void*, std::__1::allocator<void*> >) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
 6: 0x11138b555 sass_importer(char const*, Sass_Importer*, Sass_Compiler*) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
 7: 0x1113d9d20 Sass::Context::call_loader(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*, Sass::ParserState&, Sass::Import*, std::__1::vector<Sass_Importer*, std::__1::allocator<Sass_Importer*> >, bool) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
 8: 0x111493e93 Sass::Parser::parse_import() [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
 9: 0x11148cd8e Sass::Parser::parse_block_node(bool) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
10: 0x11148ad10 Sass::Parser::parse_block_nodes(bool) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
11: 0x111489f94 Sass::Parser::parse() [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
12: 0x1113d7c0c Sass::Context::register_resource(Sass::Include const&, Sass::Resource const&) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
13: 0x1113dc077 Sass::File_Context::parse() [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
14: 0x1114e0e1d sass_compiler_parse [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
15: 0x1114e0a7a sass_compile_context(Sass_Context*, Sass::Context*) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
16: 0x11138e0a3 render_file_sync(Nan::FunctionCallbackInfo<v8::Value> const&) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
17: 0x11138ee21 Nan::imp::FunctionCallbackWrapper(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/zachary/projects/assets/node_modules/node-sass/vendor/darwin-x64-93/binding.node]
18: 0x10c96e905 v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) [/Users/zachary/.asdf/installs/nodejs/16.2.0/bin/node]
19: 0x10c96ded8 v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) [/Users/zachary/.asdf/installs/nodejs/16.2.0/bin/node]
20: 0x10c96d48f v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) [/Users/zachary/.asdf/installs/nodejs/16.2.0/bin/node]
21: 0x10d1fa399 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit [/Users/zachary/.asdf/installs/nodejs/16.2.0/bin/node]
Abort trap: 6

`cache` does not track dependencies on other files

Example:

  • Cache is enabled by passing a Map to the cache option
  • entry.scss imports bar.scss
  • After the initial build, bar.scss is changed
  • An incremental build is started
  • transform is called for entry.scss
  • entry.scss is in the cache and that file itself has not been changed, so the cached transform result with the old contents of bar.scss is returned
  • The cache is never queried for bar.scss
  • The old contents of bar.scss end up in the bundle

v2.0.2 on npm build missing lib folder

For some reason 2.0.2 on npm doesn't seem to contain the lib folder, so our builds are failing
image

For reference, 2.0.1 does:
image

Workaround for now is install 2.0.1

Bump esbuild version to v0.13

I see the package.json is still using esbuild v0.12, and could you please bump esbuild to v0.13 if there is no compatibility issue?

Use `url()` with SVG images

I have been trying for a few days and can't seem to figure this one out. I have gone up and down the docs for this plugin & svgr and cannot figure it. I apologize if there is a simple solution or if this problem is out of scope of esbuild-sass-plugin.

Everything I try ends up with the same error:

Error: Build failed with 1 error:
../assets/stylesheets/application.scss:6470:20: ERROR: Cannot use "../assets/images/logo-mono.svg" as a URL
    at failureErrorWithLog (/workspace/node_modules/esbuild/lib/main.js:1603:15)
    at /workspace/node_modules/esbuild/lib/main.js:1249:28
    at runOnEndCallbacks (/workspace/node_modules/esbuild/lib/main.js:1034:63)
    at buildResponseToResult (/workspace/node_modules/esbuild/lib/main.js:1247:7)
    at /workspace/node_modules/esbuild/lib/main.js:1356:14
    at /workspace/node_modules/esbuild/lib/main.js:666:9
    at handleIncomingPacket (/workspace/node_modules/esbuild/lib/main.js:763:9)
    at Socket.readFromStdout (/workspace/node_modules/esbuild/lib/main.js:632:7)
    at Socket.emit (events.js:400:28)
    at addChunk (internal/streams/readable.js:293:12)
    at readableAddChunk (internal/streams/readable.js:267:9)
    at Socket.Readable.push (internal/streams/readable.js:206:10)
    at Pipe.onStreamRead (internal/stream_base_commons.js:188:23)
 {
  errors: [
    {
      detail: undefined,
      location: [Object],
      notes: [],
      pluginName: '',
      text: 'Cannot use "../assets/images/logo-mono.svg" as a URL'
    }
  ],
  warnings: []
}

The actual line is background-image: url(@img/logo-mono.svg);.

Current Esbuild config:

/* eslint-disable import/no-extraneous-dependencies */

const path = require('path');
const ImportGlobPlugin = require('esbuild-plugin-import-glob').default;
const svgrPlugin = require('esbuild-plugin-svgr');
const coffeeScriptPlugin = require('esbuild-coffeescript');
const sassPlugin = require('esbuild-sass-plugin').default;
const inlineImage = require('esbuild-plugin-inline-image');

const esbuild = require('esbuild');

const cwd = (pathname) => path.join(process.cwd(), pathname);

esbuild.build({
  entryPoints: ['application.js', 'react.js'], // TODO: Iterate on dir
  bundle: true,
  define: { // https://esbuild.github.io/api/#define
    process: '{"browser":null,"env":{"NODE_ENV":"development"}}', // TODO: Capture actual env
    global: 'window',
  },
  outdir: cwd('app/assets/builds'),
  absWorkingDir: cwd('app/javascript'),
  watch: true, // TODO: false in prod
  minify: false, // TODO: true in prod
  sourcemap: true,
  logLevel: 'debug', // TODO: Remove after regression testing
  external: ['fs', 'fs/promises', 'process', 'fs-extra'],
  tsconfig: cwd('tsconfig.json'),
  loader: {
    '.js': 'jsx',
    '.svg': 'file', // TODO: file? dataurl? text????
  },
  plugins: [
    svgrPlugin(), // Svg images
    sassPlugin({
      // importMapper: (pathname) => pathname.replace(/^@img\//, './assets/images/'),
      transform: async (rawSource, resolveDir) => {
        // TODO: Still working here, definitely not finalized
        const source = rawSource.replace(/@img/, path.resolve(__dirname, 'app/assets/images/'));

        const re = /url\(.*.svg\)/gm;
        const svgPaths = source.match(re);
        if (svgPaths === null) return source;

        console.log(source.match(re));
        // console.log(resolveDir);
        return source;
      },
    }),
    ImportGlobPlugin(), // Import with glob support (`/**/*.coffee`)
    coffeeScriptPlugin(), // Transpile CoffeeScript - TODO: Remove after permanently transpiling CoffeeScript
    inlineImage(), // Add support for other, non SVG, images.
  ],
}).catch((err) => {
  console.log(err); // eslint-disable-line no-console
  process.exit(1);
});

I am porting over a legacy Rails build system to Esbuild. There are plenty of things to get done but I think the config should be fleshed out enough for us to look at this problem. Looking at the frontend currently in production, our SVG's used in CSS are separate files that the browser requests. I though of base encoding the svg images and embedding them into the CSS file but I would rather not. I know total load time would be longer with all the round trips to fetch all images but I am concerned of the size increase in the CSS file since we have dozens of svg's.

Please let me know if you require any other information. Absolutely willing to open a PR if this is a todo list item. Although, I would appreciate some guidance on your preferred implementation.

Support for previous methods of import in SASS

Hi - thanks for making this plugin, it is really useful.

My issue is SASS dependencies often use the now (depreciated) ~ (tilda) to import from node modules.

This produces the following error on the plugin.

error: Cannot find module 'mathsass/dist/math' from <repo>
  ╷
1 │ @import '~mathsass/dist/math';
  │         ^^^^^^^^^^^^^^^^^^^^^
  ╵

For record, this is is produced the following line of code @import "~@aotearoan/sass-farbig"; in theme.scss using the latest versions of esbuild and esbuild-sass-plugin.

I have also tried the includePaths option, producing the same error

[
    sassPlugin({
        type: "style",
        includePaths: [path.resolve(__dirname, "node_modules")],
    }),
],

Webpack, Rollup are other compilers all have support for this edge case. Do you have any advice on what to do if dependencies include this syntax and I am using esbuild and this sass plugin?

Jacob

Doesn't support path with non-ascii characters

As previously mentioned in issue #61, this plugin goes wrong if the imported .scss file makes another import, and if the full path of the latter file contains non-ascii characters. I believe this is a plugin-specific issue as this error doesn't occur with esbuild in general.

image

As can be seen in the error message here, it somehow escapes the non-ascii characters internally, and therefore the file of course doesn't exists. This error doesn't occur if we only import one .scss file without any further importing inside it.

The current workaround is of course by putting the project in a path consists of ascii characters only, and then everything works fine.

Support for new math.div Sass module

I have some SCSS that uses "/" to perform a division operation, but this produces a deprecation warning:

DEPRECATION WARNING: Using / for division is deprecated and will be removed in Dart Sass 2.0.0.

If I change this to use "math.div" I get the following error:

error: [plugin: sass-plugin] There is no module with the namespace "math"

Suggestions?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.