Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

esbuild-sass-plugin

Package Overview
Dependencies
Maintainers
1
Versions
90
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

esbuild-sass-plugin - npm Package Compare versions

Comparing version 2.0.0-alpha.1 to 2.0.0-alpha.3

lib/cache.d.ts

31

lib/index.d.ts

@@ -1,28 +0,13 @@

import { OnLoadResult } from "esbuild";
import { Importer, Value } from "sass";
import { sassPlugin } from "./plugin";
export declare type Type = "css" | "style" | "css-text" | "lit-css";
export declare type SassPluginOptions = {
import { OnLoadResult } from 'esbuild';
import { StringOptions } from 'sass';
import { sassPlugin } from './plugin';
export declare type Type = 'css' | 'style' | 'css-text' | 'lit-css';
export declare type SassPluginOptions = StringOptions<'sync'> & {
filter?: RegExp;
importMapper?: (url: string) => string;
implementation?: string;
includePaths?: string[];
basedir?: string;
type?: Type;
cache?: Map<string, Map<string, CachedResult>> | boolean;
importer?: Importer | Importer[];
functions?: {
[key: string]: (...args: Value[]) => Value | void;
};
includePaths?: string[];
indentedSyntax?: boolean;
indentType?: "space" | "tab";
indentWidth?: number;
linefeed?: "cr" | "crlf" | "lf" | "lfcr";
outputStyle?: "compressed" | "expanded";
sourceMap?: boolean | string;
sourceMapContents?: boolean;
sourceMapEmbed?: boolean;
sourceMapRoot?: string;
cache?: Map<string, CachedResult> | boolean;
transform?: (css: string, resolveDir: string, filePath: string) => string | OnLoadResult | Promise<string | OnLoadResult>;
quietDeps?: boolean;
precompile?: (source: string, path: string) => string;

@@ -32,3 +17,3 @@ };

export { sassPlugin };
export { makeModule, postcssModules } from "./utils";
export { makeModule, postcssModules } from './utils';
export declare type CachedResult = {

@@ -35,0 +20,0 @@ mtimeMs: number;

@@ -0,0 +0,0 @@ "use strict";

@@ -1,3 +0,3 @@

import { Plugin } from "esbuild";
import { SassPluginOptions } from "./index";
import { Plugin } from 'esbuild';
import { SassPluginOptions } from './index';
export declare function sassPlugin(options?: SassPluginOptions): Plugin;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sassPlugin = void 0;
const fs = __importStar(require("fs"));
const fs_1 = require("fs");
const path_1 = require("path");
const utils_1 = require("./utils");
const sass = __importStar(require("sass"));
const url_1 = require("url");
let pluginIndex = 0;
const cache_1 = require("./cache");
const render_1 = require("./render");
function sassPlugin(options = {}) {

@@ -35,186 +13,47 @@ var _a;

}
const type = (_a = options.type) !== null && _a !== void 0 ? _a : "css";
if (options["picomatch"] || options["exclude"] || typeof type !== "string") {
console.log("The type array, exclude and picomatch options are no longer supported, please refer to the README for alternatives.");
if (options.includePaths) {
console.log(`'includePaths' option is deprecated, please use 'loadPaths' instead`);
}
function pathResolve({ resolveDir, path, importer }) {
return (0, path_1.resolve)(resolveDir || (0, path_1.dirname)(importer), path);
options.loadPaths = Array.from(new Set([
...options.loadPaths || (0, utils_1.modulesPaths)(),
...options.includePaths || []
]));
const type = (_a = options.type) !== null && _a !== void 0 ? _a : 'css';
if (options['picomatch'] || options['exclude'] || typeof type !== 'string') {
console.log('The type array, exclude and picomatch options are no longer supported, please refer to the README for alternatives.');
}
function requireResolve({ resolveDir, path, importer }) {
if (!resolveDir) {
resolveDir = (0, path_1.dirname)(importer);
const requireOptions = { paths: ['.', ...options.loadPaths] };
function resolvePath(basedir, path) {
if (options.importMapper) {
path = options.importMapper(path);
}
const mapper = options.importMapper;
if (mapper) {
path = mapper(path);
if (utils_1.RELATIVE_PATH.test(path)) {
return (0, path_1.resolve)(basedir, path);
}
const paths = options.includePaths ? [resolveDir, ...options.includePaths] : [resolveDir];
return require.resolve(path, { paths });
}
function readCssFileSync(path) {
return { css: (0, fs_1.readFileSync)(path, "utf-8"), watchFiles: [path] };
}
function resolveSassImport(pathname, ext) {
if (ext) {
let filename = pathname + ext;
if (fs.existsSync(filename)) {
return filename;
}
const index = filename.lastIndexOf(path_1.sep);
filename = index >= 0 ? filename.slice(0, index) + path_1.sep + "_" + filename.slice(index + 1) : "_" + filename;
if (fs.existsSync(filename)) {
return filename;
}
return null;
}
else {
return resolveSassImport(pathname, ".scss")
|| resolveSassImport(pathname, ".sass")
|| resolveSassImport(pathname + path_1.sep + "index");
requireOptions.paths[0] = basedir;
return require.resolve(path, requireOptions);
}
}
function renderSync(path) {
const basedir = (0, path_1.dirname)(path);
let source = fs.readFileSync(path, "utf8");
if (options.precompile) {
source = options.precompile(source, path);
}
const { css, loadedUrls } = sass.compileString(source, {
importer: {
load(canonicalUrl) {
const filename = path_1.sep === "/" ? canonicalUrl.pathname : canonicalUrl.pathname.slice(1);
let contents = fs.readFileSync(filename, "utf8");
if (options.precompile) {
contents = options.precompile(contents, filename);
}
switch ((0, path_1.extname)(filename)) {
case ".scss":
return { contents, syntax: "scss" };
case ".css":
return { contents, syntax: "css" };
default:
return { contents, syntax: "indented" };
}
},
canonicalize(url) {
let filename;
if (url.startsWith("~")) {
filename = require.resolve(url.slice(1), { paths: [basedir] });
}
else if (url.startsWith("file://")) {
filename = path_1.sep === "/" ? url.slice(7) : url.slice(8);
}
else {
filename = url;
}
if (options.importMapper) {
filename = options.importMapper(filename);
}
filename = (0, path_1.resolve)(basedir, filename);
let ext = filename.lastIndexOf(".");
if (ext >= 0) {
filename = resolveSassImport(filename.slice(0, ext), filename.slice(ext));
}
else {
filename = resolveSassImport(filename);
}
if (filename) {
return (0, url_1.pathToFileURL)(filename);
}
else {
return null;
}
}
},
logger: {
warn(message, options) {
console.log(message, options);
},
debug(message, options) {
console.log(message, options);
}
}
});
return {
css: css.toString(),
watchFiles: loadedUrls.map(url => url.pathname.slice(1))
};
}
const cache = !options.cache
? null
: options.cache instanceof Map
? options.cache
: new Map();
function collectStats(watchFiles) {
return Promise.all(watchFiles.map(filename => fs_1.promises.stat(filename)));
}
function maxMtimeMs(stats) {
return stats.reduce((max, { mtimeMs }) => Math.max(max, mtimeMs), 0);
}
const RELATIVE_PATH = /^\.\.?\//;
const namespace = `sass-plugin-${pluginIndex++}`;
return {
name: "sass-plugin",
name: 'sass-plugin',
setup: function (build) {
var _a;
const { namespace, sourcemap, watched } = (0, utils_1.getContext)(build.initialOptions);
build.onResolve({ filter: (_a = options.filter) !== null && _a !== void 0 ? _a : /\.(s[ac]ss|css)$/ }, (args) => {
if (RELATIVE_PATH.test(args.path)) {
return { path: pathResolve(args), namespace, pluginData: args };
}
else {
return { path: requireResolve(args), namespace, pluginData: args };
}
const { resolveDir, path, importer } = args;
const basedir = resolveDir || (0, path_1.dirname)(importer);
return { path: resolvePath(basedir, path), namespace, pluginData: args };
});
let cached;
if (cache) {
cached = (transform) => async ({ path, pluginData: args }) => {
let group = cache.get(args.resolveDir);
if (!group) {
group = new Map();
cache.set(args.resolveDir, group);
}
try {
let cached = group.get(args.path);
if (cached) {
let watchFiles = cached.result.watchFiles;
let stats = await collectStats(watchFiles);
for (const { mtimeMs } of stats) {
if (mtimeMs > cached.mtimeMs) {
cached.result = await transform(watchFiles[0]);
cached.mtimeMs = maxMtimeMs(stats);
break;
}
}
return cached.result;
}
let result = await transform(path);
group.set(args.path, {
mtimeMs: maxMtimeMs(await collectStats(result.watchFiles)),
result
});
return result;
}
catch (error) {
group.delete(args.path);
throw error;
}
};
}
else {
cached = (transform) => ({ path, pluginData: args }) => {
return transform(path);
};
}
const lastWatchFiles = build.initialOptions.watch ? {} : null;
async function transform(path) {
const renderSync = (0, render_1.createRenderer)(options, sourcemap);
build.onLoad({ filter: /./, namespace }, (0, cache_1.useCache)(options, async (path) => {
var _a;
try {
let { css, watchFiles } = path.endsWith(".css") ? readCssFileSync(path) : renderSync(path);
watchFiles = [...watchFiles];
if (lastWatchFiles) {
lastWatchFiles[path] = watchFiles;
let { css, watchFiles } = renderSync(path);
if (watched) {
watched[path] = watchFiles;
}
if (options.transform) {
const out = await options.transform(css, (0, path_1.dirname)(path), path);
if (typeof out !== "string") {
if (typeof out !== 'string') {
return {

@@ -232,5 +71,5 @@ contents: out.contents,

}
return type === "css" ? {
return type === 'css' ? {
contents: css,
loader: "css",
loader: 'css',
resolveDir: (0, path_1.dirname)(path),

@@ -240,3 +79,3 @@ watchFiles

contents: (0, utils_1.makeModule)(css, type),
loader: "js",
loader: 'js',
resolveDir: (0, path_1.dirname)(path),

@@ -249,7 +88,6 @@ watchFiles

errors: [{ text: err.message }],
watchFiles: (_a = lastWatchFiles === null || lastWatchFiles === void 0 ? void 0 : lastWatchFiles[path]) !== null && _a !== void 0 ? _a : [path]
watchFiles: (_a = watched === null || watched === void 0 ? void 0 : watched[path]) !== null && _a !== void 0 ? _a : [path]
};
}
}
build.onLoad({ filter: /./, namespace }, cached(transform));
}));
}

@@ -256,0 +94,0 @@ };

@@ -1,5 +0,19 @@

import { Type } from "./index";
import { AcceptedPlugin } from "postcss";
import PostcssModulesPlugin from "postcss-modules";
import { OnLoadResult } from "esbuild";
import { Type } from './index';
import { AcceptedPlugin } from 'postcss';
import PostcssModulesPlugin from 'postcss-modules';
import { BuildOptions, OnLoadResult } from 'esbuild';
import { Syntax } from 'sass';
export declare const RELATIVE_PATH: RegExp;
export declare function modulesPaths(): string[];
export declare function fileSyntax(filename: string): Syntax;
export declare type PluginContext = {
instance: number;
namespace: string;
sourcemap: boolean;
watched: {
[path: string]: string[];
} | null;
};
export declare function getContext(buildOptions: BuildOptions): PluginContext;
export declare function sourceMappingURL(sourceMap: any): string;
export declare function makeModule(contents: string, type: Type): string;

@@ -6,0 +20,0 @@ export declare type PostcssModulesParams = Parameters<PostcssModulesPlugin>[0] & {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.postcssModules = exports.makeModule = void 0;
exports.postcssModules = exports.makeModule = exports.sourceMappingURL = exports.getContext = exports.fileSyntax = exports.modulesPaths = exports.RELATIVE_PATH = void 0;
const path_1 = require("path");
const fs_1 = require("fs");
exports.RELATIVE_PATH = /^\.\.?\//;
function modulesPaths() {
let path = process.cwd();
let { root } = (0, path_1.parse)(path);
let found = [];
while (path !== root) {
const filename = (0, path_1.resolve)(path, 'node_modules');
if ((0, fs_1.existsSync)(filename)) {
found.push(filename);
}
path = (0, path_1.resolve)(path, '..');
}
return [...found];
}
exports.modulesPaths = modulesPaths;
function fileSyntax(filename) {
if (filename.endsWith('.scss')) {
return 'scss';
}
else if (filename.endsWith('.css')) {
return 'css';
}
else {
return 'indented';
}
}
exports.fileSyntax = fileSyntax;
const SASS_PLUGIN_CONTEXT = Symbol();
function getContext(buildOptions) {
let descriptor = Object.getOwnPropertyDescriptor(buildOptions, SASS_PLUGIN_CONTEXT);
if (descriptor === undefined) {
Object.defineProperty(buildOptions, SASS_PLUGIN_CONTEXT, descriptor = {
value: {
instances: 0
}
});
}
const instance = descriptor.value.instances++;
return {
instance,
namespace: `sass-plugin-${instance}`,
sourcemap: !!buildOptions.sourcemap,
watched: buildOptions.watch ? {} : null
};
}
exports.getContext = getContext;
function sourceMappingURL(sourceMap) {
const data = Buffer.from(JSON.stringify(sourceMap), 'utf8').toString('base64');
return `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${data} */`;
}
exports.sourceMappingURL = sourceMappingURL;
function requireModule(module, includePaths) {

@@ -20,3 +73,3 @@ try {

export default \`
${cssText.replace(/([$`\\])/g, "\\$1")}\`;
${cssText.replace(/([$`\\])/g, '\\$1')}\`;
`;

@@ -26,6 +79,6 @@ const cssResultModule = cssText => `\

export default css\`
${cssText.replace(/([$`\\])/g, "\\$1")}\`;
${cssText.replace(/([$`\\])/g, '\\$1')}\`;
`;
const styleModule = cssText => `\
const css = \`${cssText.replace(/([$`\\])/g, "\\$1")}\`;
const css = \`${cssText.replace(/([$`\\])/g, '\\$1')}\`;
document.head

@@ -37,7 +90,7 @@ .appendChild(document.createElement("style"))

function makeModule(contents, type) {
if (type === "style") {
if (type === 'style') {
return styleModule(contents);
}
else {
return type === "lit-css" ? cssResultModule(contents) : cssTextModule(contents);
return type === 'lit-css' ? cssResultModule(contents) : cssTextModule(contents);
}

@@ -49,4 +102,4 @@ }

const includePaths = (_a = options.includePaths) !== null && _a !== void 0 ? _a : [(_b = options.basedir) !== null && _b !== void 0 ? _b : process.cwd()];
const postcss = requireModule("postcss", includePaths);
const postcssModulesPlugin = requireModule("postcss-modules", includePaths);
const postcss = requireModule('postcss', includePaths);
const postcssModulesPlugin = requireModule('postcss-modules', includePaths);
return async (source, dirname, path) => {

@@ -64,4 +117,4 @@ let cssModule;

return {
contents: `${makeModule(css, "style")}export default ${cssModule};`,
loader: "js"
contents: `${makeModule(css, 'style')}export default ${cssModule};`,
loader: 'js'
};

@@ -68,0 +121,0 @@ };

{
"name": "esbuild-sass-plugin",
"version": "2.0.0-alpha.1",
"version": "2.0.0-alpha.3",
"description": "esbuild plugin for sass/scss files supporting both css loader and css result import (lit-element)",

@@ -10,10 +10,8 @@ "main": "lib/index.js",

"sass",
"scss",
"LICENSE",
"README.MD",
"tsconfig.json"
"scss"
],
"files": [
"lib",
"src"
"LICENSE",
"README.MD"
],

@@ -34,18 +32,5 @@ "author": "Gianluca Romeo <glromeo@gmail.com> (https://github.com/glromeo/esbuild-sass-plugin)",

"build:watch": "tsc --watch",
"test": "nyc mocha **/*.test.ts"
"test": "c8 mocha test/**/*.test.ts",
"coverage": "c8 report --reporter=text-lcov | coveralls"
},
"nyc": {
"extension": [
".ts"
],
"exclude": [
"**/*.d.ts",
"test/**/*.ts"
],
"reporter": [
"text"
],
"all": false,
"cache": true
},
"mocha": {

@@ -57,24 +42,15 @@ "require": [

"dependencies": {
"esbuild": "^0.14.5",
"picomatch": "^2.3.0",
"resolve": "^1.20.0",
"sass": "^1.45.0"
"esbuild": "^0.14.6",
"sass": "^1.45.1"
},
"devDependencies": {
"@types/chai": "^4.3.0",
"@types/chai-string": "^1.4.2",
"@types/mocha": "^9.0.0",
"@types/node": "^16.11.12",
"@types/resolve": "^1.20.1",
"@types/node": "^17.0.2",
"@types/sass": "^1.43.1",
"chai": "^4.3.4",
"chai-string": "^1.5.0",
"mocha": "^9.1.3",
"nyc": "^15.1.0",
"mocha-toolkit": "^1.0.7",
"postcss": "^8.4.5",
"postcss-modules": "^4.2.2",
"postcss-url": "^10.1.3",
"ts-node": "^10.4.0",
"source-map": "^0.7.3",
"typescript": "^4.5.4"
}
}

@@ -6,6 +6,8 @@ ![cooltext394785080075403](https://user-images.githubusercontent.com/160981/136289874-26ce7269-ea08-47dd-be31-9bf0ef7a0b8d.png)

A plugin for [esbuild](https://esbuild.github.io/) to handle Sass & SCSS files.
**NOTE:** This is the 2.x branch of the plugin, if you want to use node-sass or an old feature please stick to the 1.x branch
**NOTE:** This is the 2.x branch of the plugin, if you want to use node-sass or an old feature please stick to the 1.x
branch
### Features
* **PostCSS** & **CSS modules**

@@ -19,9 +21,12 @@ * support for **constructable stylesheet** to be used in custom elements or `dynamic style` to be added to the html page

### Breaking Changes
* `type` has been simplified and now accepts only a string.
If you need different types in a project you can use more than one instance of the plugin.
You can have a look at the **exclude** fixture for an example_ where **lit CSS** and **CSS modules** are both used in the same app
* The support for [node-sass](https://github.com/sass/node-sass) has been removed from the 2.x branch and for good.
Sadly, node-sass is at a dead end and so it's 1.x. I don't exclude updates or fixes on it but it's down in the list of my priorities.
* `type` has been simplified and now accepts only a string. If you need different types in a project you can use more
than one instance of the plugin. You can have a look at the **exclude** fixture for an example_ where **lit CSS**
and **CSS modules** are both used in the same app
* The support for [node-sass](https://github.com/sass/node-sass) has been removed from the 2.x branch and for good.
Sadly, node-sass is at a dead end and so it's 1.x. I don't exclude updates or fixes on it but it's down in the list of
my priorities.
### Install
```console

@@ -32,14 +37,17 @@ $ npm i esbuild-sass-plugin

### Usage
Just add it to your esbuild plugins:
```javascript
import {sassPlugin} from "esbuild-sass-plugin";
import {sassPlugin} from 'esbuild-sass-plugin'
await esbuild.build({
...
plugins: [sassPlugin()]
});
...
plugins: [sassPlugin()]
})
```
this will use `loader: "css"` and your transpiled Sass will be included in index.css.
If you specify `type: "style"` then the stylesheet will be dynamically added to the page.
If you specify `type: "style"` then the stylesheet will be dynamically added to the page.

@@ -50,74 +58,85 @@ If you want to use the resulting css text as a string import you can use `type: "css-text"`

await esbuild.build({
...
plugins: [sassPlugin({
type: "css-text",
... // other options for sass.renderSync(...)
})]
});
...
plugins: [sassPlugin({
type: "css-text",
... // for the options availanle look at 'SassPluginOptions' in index.ts
})]
})
```
...and in your module do something like
```javascript
...
import cssText from "./styles.scss";
...
customElements.define("hello-world", class HelloWorld extends HTMLElement {
import cssText from './styles.scss'
constructor() {
super();
this.attachShadow({mode: 'open'});
this.sheet = new CSSStyleSheet();
this.sheet.replaceSync(cssText);
this.shadowRoot.adoptedStyleSheets = [this.sheet];
}
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];
}
}
```
Or you can import a **lit-element** css result using `type: "lit-css"`
```javascript
...
import styles from "./styles.scss";
...
import styles from './styles.scss'
@customElement("hello-world")
export default class HelloWorld extends LitElement {
static styles = styles
static styles = styles
render() {
...
}
render() {
...
}
}
```
Look in the `test` folder for more usage examples.
Look in `test/fixtures` folder for more usage examples.
### Options
The **options** passed to the plugin are a superset of the Sass [Options](https://sass-lang.com/documentation/js-api#options).
The **options** passed to the plugin are a superset of the
Sass [Options](https://sass-lang.com/documentation/js-api#options).
|Option| Type |Default|
|---|-------------------------------|---|
|cache| boolean or Map |true|
|type| `"css"`<br/>`"style"`<br/>`"lit-css"` |`"css"`|
|transform| function |undefined|
|exclude| regex or function |undefined|
|importer| function |undefined|
|importMapper| function |undefined|
| Option | Type | Default |
|--------------|---------------------------------------|----------------------------------------|
| cache | boolean or Map | true (one Map per namespace) |
| type | `"css"`<br/>`"style"`<br/>`"lit-css"` | `"css"` |
| transform | function | undefined |
| loadPaths | [string[]]() | collection of node_modules from cwd up |
| importer | function | undefined |
| importMapper | function | undefined |
### Exclude Option
### Exclude Option
Used to exclude paths from the plugin. It can either be **a simple regex** which applies to the path
Used to exclude paths from the plugin. It can either be **a simple regex** which applies to the path
```javascript
await esbuild.build({
...
plugins: [sassPlugin({
exclude: /^http:\/\//, // ignores urls
})]
...
plugins
:
[sassPlugin({
exclude: /^http:\/\//, // ignores urls
})]
})
```
**or a function** which receives the whole set of args that esbuild passes on resolve.
```javascript
await esbuild.build({
...
plugins: [sassPlugin({
exclude: ({resolveDir}) => !/\\lit$/.test(resolveDir), // ignores files outside lit directory
})]
...
plugins
:
[sassPlugin({
exclude: ({resolveDir}) => !/\\lit$/.test(resolveDir), // ignores files outside lit directory
})]
})

@@ -127,4 +146,8 @@ ```

### Importer Option
The default importer built in the plugin has been developed to be fast but its scope is limited. For complex import scenarios (e.g pnpm)
this can be replaced by a custom implementation like the very solid [`sass-extended-importer`](https://github.com/wessberg/sass-extended-importer)
The default importer built in the plugin has been developed to be fast but its scope is limited. For complex import
scenarios (e.g pnpm)
this can be replaced by a custom implementation like the very
solid [`sass-extended-importer`](https://github.com/wessberg/sass-extended-importer)
```javascript

@@ -134,22 +157,28 @@ const {createImporter} = require("sass-extended-importer");

await esbuild.build({
...
plugins: [sassPlugin({
importer: createImporter()
})]
...,
plugins:[sassPlugin({
importer: createImporter()
})]
})
```
There's a working example of using `pnpm` with `@material` design in [issue/38](https://github.com/glromeo/esbuild-sass-plugin/tree/main/test/issues/38)
There's a working example of using `pnpm` with `@material` design
in [issue/38](https://github.com/glromeo/esbuild-sass-plugin/tree/main/test/issues/38)
### ImportMapper Option
A function to customize/re-map the import path, both `import` statements in JavaScript/TypeScript code and `@import`
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.
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
```json
{
"compilerOptions": {
"baseUrl": ".",
"baseUrl": ".",
"paths": {
"@img/*": ["./assets/images/*"]
"@img/*": [
"./assets/images/*"
]
}

@@ -159,9 +188,11 @@ }

```
now you can resolve these paths with `importMapper`
```javascript
await esbuild.build({
...
plugins: [sassPlugin({
importMapper: (path) => path.replace(/^@img\//,"./assets/images/")
})]
...,
plugins: [sassPlugin({
importMapper: (path) => path.replace(/^@img\//, './assets/images/')
})]
})

@@ -171,5 +202,7 @@ ```

### Transform Option
```typescript
async (css:string, resolveDir:string?) => string
async (css: string, resolveDir: string?) => string
```
It's a function which will be invoked before passing the css to esbuild or wrapping it in a module.\

@@ -179,33 +212,41 @@ It can be used to do **PostCSS** processing and/or to create **modules** like in the following examples.

#### PostCSS
The simplest use case is to invoke PostCSS like this:
```javascript
const postcss = require("postcss");
const autoprefixer = require("autoprefixer");
const postcssPresetEnv = require("postcss-preset-env");
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;
}
})]
});
...,
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:
A helper function is available to do all the work of calling PostCSS to create a CSS module. The usage is something
like:
```javascript
const {sassPlugin, postcssModules} = require("esbuild-sass-plugin");
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
})
})]
});
...,
plugins: [sassPlugin({
transform: postcssModules({
// ...put here the options for postcss-modules: https://github.com/madyankin/postcss-modules
})
})]
})
```
> **NOTE:** `postcss` and `postcss-modules` have to be added to your `package.json`.

@@ -215,3 +256,4 @@

Look into [fixture/css-modules](https://github.com/glromeo/esbuild-sass-plugin/tree/main/test/fixture/css-modules) for the complete example.
Look into [fixture/css-modules](https://github.com/glromeo/esbuild-sass-plugin/tree/main/test/fixture/css-modules) for
the complete example.

@@ -221,93 +263,18 @@ > **NOTE:** Since `v1.5.0` transform can return either a string or an esbuild `LoadResult` object. \

### Use node-sass instead of Dart Sass
Remember to add the dependency
```console
$ npm i esbuild-sass-plugin node-sass
```
and to specify the implementation in the options:
```javascript
await esbuild.build({
...
plugins: [sassPlugin({
implementation: "node-sass",
... // other options for sass.renderSync(...)
})]
});
```
When compared to dart sass, node-sass is not only behind and lacking features but it's also
behaving slightly differently. If you experience [issues](https://github.com/glromeo/esbuild-sass-plugin/issues/34#issue-1029351164)
try and replace the built-in importer with [magic importer](https://www.npmjs.com/package/node-sass-magic-importer)
which is slower but better suited for node-sass
```javascript
const magicImporter = require('node-sass-magic-importer');
### Benchmarks
await esbuild.build({
...
plugins: [sassPlugin({
implementation: "node-sass",
importer: magicImporter()
})]
});
```
### Caching
Chaching greatly improves the performance in incremental builds or watch mode.
It must be enabled with `cache: true` in the options.
You can pass your own map instead of `true` if you want to recycle it across different builds.
```javascript
const pluginCache = new Map();
await esbuild.build({
...
plugins: [sassPlugin({cache: pluginCache})],
...
})
```
### Benchmarks
Given 24 × 24 = 576 lit-element files & 576 imported CSS styles plus the import of the full bootstrap 5.1
### sass (default implementation)
| | dart sass | dart sass (no cache) | node-sass* | node-sass (no cache) |
|------------------------|------------|-----------------------|-------------|----------------------|
| **initial build** | 2.946s | 2.945s | 1.903s | 1.858s |
| rebuild (.ts change) | 285.959ms | 1.950s | 797.098ms | 1.689s |
| rebuild (.ts change) | 260.791ms | 1.799s | 768.213ms | 1.790s |
| rebuild (.scss change) | 234.152ms | 1.801s | 770.619ms | 1.652s |
| rebuild (.scss change) | 267.857ms | 1.738s | 750.743ms | 1.682s |
#### cache: true
```
initial build: 3.064s
incremental build: 793.142ms
incremental build: 772.664ms
incremental build: 755.093ms
incremental build: 721.7ms
```
#### cache: false
```
initial build: 3.026s
incremental build: 1.849s
incremental build: 1.715s
incremental build: 1.665s
incremental build: 1.640s
```
(*) node-sass is here just to give a term of comparison ...those samples were taken from 1.8.x
### node-sass (just as a reference ...samples taken from 1.8.x)
#### cache: true
```
initial build: 1.903s
incremental build: 797.098ms
incremental build: 768.213ms
incremental build: 770.619ms
incremental build: 750.743ms
```
#### cache: false
```
initial build: 1.858s
incremental build: 1.689s
incremental build: 1.790s
incremental build: 1.652s
incremental build: 1.682s
```
[travis-url]: https://travis-ci.com/glromeo/esbuild-sass-plugin
[travis-image]: https://travis-ci.com/glromeo/esbuild-sass-plugin.svg?branch=main

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc