sass-loader
Advanced tools
Comparing version 14.1.1 to 14.2.0
@@ -32,3 +32,3 @@ "use strict"; | ||
if (shouldUseWebpackImporter) { | ||
const isModernAPI = options.api === "modern"; | ||
const isModernAPI = options.api === "modern" || options.api === "modern-compiler"; | ||
if (!isModernAPI) { | ||
@@ -40,3 +40,5 @@ const { | ||
} else { | ||
sassOptions.importers.push((0, _utils.getModernWebpackImporter)(this, implementation)); | ||
sassOptions.importers.push( | ||
// No need to pass `loadPaths`, because modern API handle them itself | ||
(0, _utils.getModernWebpackImporter)(this, implementation, [])); | ||
} | ||
@@ -46,3 +48,3 @@ } | ||
try { | ||
compile = (0, _utils.getCompileFn)(implementation, options); | ||
compile = (0, _utils.getCompileFn)(this, implementation, options); | ||
} catch (error) { | ||
@@ -54,3 +56,3 @@ callback(error); | ||
try { | ||
result = await compile(sassOptions, options); | ||
result = await compile(sassOptions); | ||
} catch (error) { | ||
@@ -57,0 +59,0 @@ // There are situations when the `file`/`span.url` property do not exist |
@@ -20,3 +20,3 @@ { | ||
"link": "https://github.com/webpack-contrib/sass-loader#sassoptions", | ||
"enum": ["legacy", "modern"] | ||
"enum": ["legacy", "modern", "modern-compiler"] | ||
}, | ||
@@ -23,0 +23,0 @@ "sassOptions": { |
@@ -148,3 +148,3 @@ "use strict"; | ||
} | ||
const isModernAPI = loaderOptions.api === "modern"; | ||
const isModernAPI = loaderOptions.api === "modern" || loaderOptions.api === "modern-compiler"; | ||
const { | ||
@@ -175,2 +175,5 @@ resourcePath | ||
} | ||
sassOptions.loadPaths = [].concat( | ||
// We use `loadPaths` in context for resolver, so it should be always absolute | ||
(sassOptions.loadPaths ? sassOptions.loadPaths.slice() : []).map(includePath => _path.default.isAbsolute(includePath) ? includePath : _path.default.join(process.cwd(), includePath))).concat(process.env.SASS_PATH ? process.env.SASS_PATH.split(process.platform === "win32" ? ";" : ":") : []); | ||
sassOptions.importers = sassOptions.importers ? Array.isArray(sassOptions.importers) ? sassOptions.importers.slice() : [sassOptions.importers] : []; | ||
@@ -351,3 +354,3 @@ } else { | ||
function getWebpackResolver(resolverFactory, implementation, includePaths = []) { | ||
const isDartSass = implementation && implementation.info.includes("dart-sass"); | ||
const isModernSass = implementation && (implementation.info.includes("dart-sass") || implementation.info.includes("sass-embedded")); | ||
// We only have one difference with the built-in sass resolution logic and out resolution logic: | ||
@@ -409,3 +412,3 @@ // First, we look at the files starting with `_`, then without `_` (i.e. `_name.sass`, `_name.scss`, `_name.css`, `name.sass`, `name.scss`, `name.css`), | ||
// custom importer may not return `{ file: '/path/to/name.ext' }` and therefore our `context` will be relative | ||
if (!isDartSass && !_path.default.isAbsolute(context)) { | ||
if (!isModernSass && !_path.default.isAbsolute(context)) { | ||
return Promise.reject(); | ||
@@ -448,3 +451,3 @@ } | ||
// `node-sass` calls our importer before `1. Filesystem imports relative to the base file.`, so we need emulate this too | ||
if (!isDartSass) { | ||
if (!isModernSass) { | ||
resolutionMap = resolutionMap.concat({ | ||
@@ -476,9 +479,53 @@ resolve: fromImport ? sassImportResolve : sassModuleResolve, | ||
const MATCH_CSS = /\.css$/i; | ||
function getModernWebpackImporter() { | ||
function getModernWebpackImporter(loaderContext, implementation, loadPaths) { | ||
const resolve = getWebpackResolver(loaderContext.getResolve, implementation, loadPaths); | ||
return { | ||
async canonicalize() { | ||
return null; | ||
async canonicalize(originalUrl, context) { | ||
const { | ||
fromImport | ||
} = context; | ||
const prev = context.containingUrl ? _url.default.fileURLToPath(context.containingUrl.toString()) : loaderContext.resourcePath; | ||
let result; | ||
try { | ||
result = await resolve(prev, originalUrl, fromImport); | ||
} catch (err) { | ||
// If no stylesheets are found, the importer should return null. | ||
return null; | ||
} | ||
loaderContext.addDependency(_path.default.normalize(result)); | ||
return _url.default.pathToFileURL(result); | ||
}, | ||
load() { | ||
// TODO implement | ||
async load(canonicalUrl) { | ||
const ext = _path.default.extname(canonicalUrl.pathname); | ||
let syntax; | ||
if (ext && ext.toLowerCase() === ".scss") { | ||
syntax = "scss"; | ||
} else if (ext && ext.toLowerCase() === ".sass") { | ||
syntax = "indented"; | ||
} else if (ext && ext.toLowerCase() === ".css") { | ||
syntax = "css"; | ||
} else { | ||
// Fallback to default value | ||
syntax = "scss"; | ||
} | ||
try { | ||
const contents = await new Promise((resolve, reject) => { | ||
// Old version of `enhanced-resolve` supports only path as a string | ||
// TODO simplify in the next major release and pass URL | ||
const canonicalPath = _url.default.fileURLToPath(canonicalUrl); | ||
loaderContext.fs.readFile(canonicalPath, "utf8", (err, content) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(content); | ||
}); | ||
}); | ||
return { | ||
contents, | ||
syntax | ||
}; | ||
} catch (err) { | ||
return null; | ||
} | ||
} | ||
@@ -513,2 +560,3 @@ }; | ||
let nodeSassJobQueue = null; | ||
const sassModernCompilers = new WeakMap(); | ||
@@ -518,2 +566,3 @@ /** | ||
* | ||
* @param {Object} loaderContext | ||
* @param {Object} implementation | ||
@@ -523,3 +572,3 @@ * @param {Object} options | ||
*/ | ||
function getCompileFn(implementation, options) { | ||
function getCompileFn(loaderContext, implementation, options) { | ||
const isNewSass = implementation.info.includes("dart-sass") || implementation.info.includes("sass-embedded"); | ||
@@ -536,2 +585,33 @@ if (isNewSass) { | ||
} | ||
if (options.api === "modern-compiler") { | ||
return async sassOptions => { | ||
// eslint-disable-next-line no-underscore-dangle | ||
const webpackCompiler = loaderContext._compiler; | ||
const { | ||
data, | ||
...rest | ||
} = sassOptions; | ||
// Some people can run the loader in a multi-threading way; | ||
// there is no webpack compiler object in such case. | ||
if (webpackCompiler) { | ||
if (!sassModernCompilers.has(implementation)) { | ||
// Create a long-running compiler process that can be reused | ||
// for compiling individual files. | ||
const compiler = await implementation.initAsyncCompiler(); | ||
// Check again because awaiting the initialization function | ||
// introduces a race condition. | ||
if (!sassModernCompilers.has(webpackCompiler)) { | ||
sassModernCompilers.set(webpackCompiler, compiler); | ||
webpackCompiler.hooks.shutdown.tap("sass-loader", () => { | ||
compiler.dispose(); | ||
}); | ||
} | ||
} | ||
return sassModernCompilers.get(webpackCompiler).compileStringAsync(data, rest); | ||
} | ||
return implementation.compileStringAsync(data, rest); | ||
}; | ||
} | ||
return sassOptions => new Promise((resolve, reject) => { | ||
@@ -547,3 +627,3 @@ implementation.render(sassOptions, (error, result) => { | ||
} | ||
if (options.api === "modern") { | ||
if (options.api === "modern" || options.api === "modern-compiler") { | ||
throw new Error("Modern API is not supported for 'node-sass'"); | ||
@@ -550,0 +630,0 @@ } |
{ | ||
"name": "sass-loader", | ||
"version": "14.1.1", | ||
"version": "14.2.0", | ||
"description": "Sass loader for webpack", | ||
@@ -73,4 +73,4 @@ "license": "MIT", | ||
"@babel/cli": "^7.23.4", | ||
"@babel/core": "^7.23.7", | ||
"@babel/preset-env": "^7.23.8", | ||
"@babel/core": "^7.24.0", | ||
"@babel/preset-env": "^7.24.0", | ||
"@commitlint/cli": "^18.4.4", | ||
@@ -84,8 +84,8 @@ "@commitlint/config-conventional": "^18.4.4", | ||
"cross-env": "^7.0.3", | ||
"cspell": "^8.3.2", | ||
"cspell": "^8.6.0", | ||
"css-loader": "^6.9.0", | ||
"del": "^6.1.1", | ||
"del-cli": "^5.1.0", | ||
"enhanced-resolve": "^5.15.0", | ||
"eslint": "^8.46.0", | ||
"enhanced-resolve": "^5.15.1", | ||
"eslint": "^8.57.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
@@ -100,3 +100,3 @@ "eslint-plugin-import": "^2.28.0", | ||
"material-components-web": "^9.0.0", | ||
"memfs": "^4.6.0", | ||
"memfs": "^4.7.7", | ||
"node-sass": "^9.0.0", | ||
@@ -106,8 +106,10 @@ "node-sass-glob-importer": "^5.3.2", | ||
"prettier": "^3.2.2", | ||
"sass": "^1.69.7", | ||
"sass-embedded": "^1.69.7", | ||
"sass": "^1.71.1", | ||
"sass-embedded": "^1.71.1", | ||
"semver": "^7.5.4", | ||
"standard-version": "^9.3.1", | ||
"style-loader": "^3.3.4", | ||
"webpack": "^5.88.2" | ||
"webpack": "^5.88.2", | ||
"webpack-cli": "^5.1.4", | ||
"webpack-dev-server": "^5.0.4" | ||
}, | ||
@@ -114,0 +116,0 @@ "keywords": [ |
@@ -660,3 +660,3 @@ <div align="center"> | ||
```ts | ||
type api = "legacy" | "modern"; | ||
type api = "legacy" | "modern" | "modern-compiler"; | ||
``` | ||
@@ -666,7 +666,7 @@ | ||
Allows you to switch between `legacy` and `modern` API. You can find more information [here](https://sass-lang.com/documentation/js-api). | ||
Allows you to switch between `legacy` and `modern` API. You can find more information [here](https://sass-lang.com/documentation/js-api). The `modern-compiler` option enables the modern API with support for [Shared Resources](https://github.com/sass/sass/blob/main/accepted/shared-resources.d.ts.md). | ||
> **Warning** | ||
> **Note** | ||
> | ||
> "modern" API is experimental, so some features may not work (known: built-in `importer` is not working and files with errors is not watching on initial run), you can follow this [here](https://github.com/webpack-contrib/sass-loader/issues/774). | ||
> Using `modern-compiler` and `sass-embedded` together significantly improve performance and decrease built time. We strongly recommend their use. We will enable them by default in a future major release. | ||
@@ -673,0 +673,0 @@ > **Warning** |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
60271
838
39
8