Mqx
Mqx7mo ago

ESBuild SASS Plugin

Hey, I have developed this Plugin: https://github.com/DenoPlayground/esbuild-plugin-sass But I have some problems with the import paths from the SASS module. Maybe someone can help me to debug this, since ESBuild does not have an official community server. The problem at the moment is that when the SASS module tries to resolve an import it does that by assuming that the path is relative to the root directory and not the file that is currently processed. Maybe there is a way to account for this issue. For example if we would assume that this is our file structure and we import colors.scss into style.scss.
/src/client/style.scss
/src/client/colors.scss
/src/client/style.scss
/src/client/colors.scss
When the SASS module resolves the import it is trying to load the colors.scss from:
/colors.scss
/colors.scss
Because this is where the initial task gets started. But I can not simply set the new root to:
/src/client
/src/client
Because then all my other tasks would not work.
GitHub
GitHub - DenoPlayground/esbuild-plugin-sass
Contribute to DenoPlayground/esbuild-plugin-sass development by creating an account on GitHub.
26 Replies
Deno AI Helper
Deno AI Helper7mo ago
I wasn't able to find the answer to your question in the official Deno documentation. However, based on the information you've provided, it seems like the issue is related to how the SASS module resolves import paths. One common issue with SASS or any CSS preprocessor is the handling of relative paths. If the SASS module is resolving imports relative to the root directory instead of the directory of the file that contains the import statement, you might need to adjust the import paths or the configuration of how SASS resolves the paths. Without specific code or configuration, it's difficult to provide a precise solution, but you might want to look into the options for configuring the SASS module's path resolution. This could involve setting include paths or using a custom importer function that resolves the paths correctly based on the file being processed. Please note that this advice is not based on the Deno documentation, and I would advise you to double-check the accuracy of this advice and consult the documentation or community for the SASS module or ESBuild plugin system for more detailed guidance.
Mqx
Mqx7mo ago
Is there a way to set the root dynamically?
Deno AI Helper
Deno AI Helper7mo ago
In the context of ESBuild and SASS, setting the root dynamically would typically involve configuring the plugin or the build process to understand the context of each file and resolve paths accordingly. However, as of my knowledge cutoff date, there isn't a built-in feature in ESBuild or its SASS plugin that allows you to set the root directory dynamically for each file being processed. A common approach to handle this in other systems is to use a custom importer function that can be provided to the SASS compiler. This function can take the current file being processed into account and resolve import paths relative to it. You would write a function that adjusts the import paths based on the location of the file that contains the @import statement. Here's a conceptual example of what a custom importer might look like:
const path = require('path');

function customImporter(url, prev) {
// Resolve the new path based on the directory of the importing file
const newPath = path.resolve(path.dirname(prev), url);

// Return the new path for SASS to use
return { file: newPath };
}
const path = require('path');

function customImporter(url, prev) {
// Resolve the new path based on the directory of the importing file
const newPath = path.resolve(path.dirname(prev), url);

// Return the new path for SASS to use
return { file: newPath };
}
You would then pass this importer to the SASS options in your plugin configuration. However, since this is not based on the Deno documentation or a specific Deno API, you should verify whether a similar approach is applicable and how it can be implemented in the context of your Deno-based ESBuild plugin for SASS. Remember to consult the documentation for the SASS module you are using and the ESBuild plugin API to see if they provide any mechanisms for handling dynamic root paths or custom importers.
Mqx
Mqx7mo ago
All the path resolving is done by the SASS module. I have no influence on this. All I possibly could do is to set the root every time to the new file that’s been processed.
Deno AI Helper
Deno AI Helper7mo ago
I understand that the path resolving is handled by the SASS module and that you're looking for a way to set the root for each file that's being processed. Unfortunately, I wasn't able to find the answer to your question in the official Deno documentation. However, based on your description, it seems like you might need to adjust the configuration for each file dynamically as it's being processed by the SASS module. If the SASS module allows for per-file configuration, you could potentially set the includePaths option dynamically before processing each file, ensuring that the root is correctly set relative to the file being processed. Here's a conceptual example of what that might look like in code:
// Pseudo-code for dynamically setting includePaths for each file
sass.render({
file: 'path/to/current/file.scss',
includePaths: ['path/to/current'], // Set this dynamically based on the file location
// ... other options
}, function(err, result) {
// Handle the result or error
});
// Pseudo-code for dynamically setting includePaths for each file
sass.render({
file: 'path/to/current/file.scss',
includePaths: ['path/to/current'], // Set this dynamically based on the file location
// ... other options
}, function(err, result) {
// Handle the result or error
});
In this pseudo-code, 'path/to/current' would be dynamically replaced with the directory of the current file being processed. This would ensure that the SASS module looks for imports relative to the current file's directory. Please note that this advice is not based on the Deno documentation, and I would advise you to double-check the accuracy of this advice and consult the documentation or community for the SASS module or ESBuild plugin system for more detailed guidance on how to implement this in your specific setup.
Mqx
Mqx7mo ago
The SASS module just takes in the file text content of the file that’s been processed. Everything else is handled by the SASS module.
Deno AI Helper
Deno AI Helper7mo ago
I understand that the SASS module takes in the file text content and handles everything else internally. Since you mentioned that you cannot influence the path resolving directly within the SASS module, it seems like the challenge is to work within the constraints of the module's path resolution behavior. Given that the SASS module handles the path resolution, and you can only provide the file text content, it appears that the solution would need to involve configuring the SASS module's behavior outside of the standard API, if such configuration is possible. If the SASS module does not provide a way to set the root or include paths dynamically per file, you might be limited in how you can influence the path resolution. In such a case, you may need to look into whether the SASS module has any advanced configuration options or hooks that you can use to alter the path resolution process. Since I wasn't able to find the answer to your question in the official Deno documentation, and the advice provided here is not based on the Deno documentation, I recommend double-checking the accuracy of this advice. It would be best to consult the documentation for the SASS module you are using or reach out to the community around that module for more specific guidance on how to handle dynamic path resolution in your particular scenario.
Mqx
Mqx7mo ago
This is the current plugin:
import sass from 'https://deno.land/x/denosass@1.0.6/mod.ts';
import {
BuildOptions,
OnLoadArgs,
OnLoadOptions,
OnLoadResult,
} from 'https://deno.land/x/esbuild@v0.19.11/mod.js';
import getTextContent from './get_text_content.ts';

/**
* This function registers the onLoad function and sets some initial options.
*
* @param initialOptions Initial options from ESBuild for the plugin to use
* @param onLoadFunction The `onLoad` function for the plugin
*/
export default function sassPluginSetup(
initialOptions: BuildOptions,
onLoadFunction: (
options: OnLoadOptions,
callback: (
args: OnLoadArgs,
) =>
| OnLoadResult
| null
| undefined
| Promise<OnLoadResult | null | undefined>,
) => void,
): void {
onLoadFunction(
{ filter: /\.scss$/ },
async (args) => {
const file = await Deno.readTextFile(args.path);
try {
const css = sass(
file,
{
style: initialOptions.minify ? 'compressed' : 'expanded'
}
).to_string();

return {
contents: getTextContent(css),
loader: 'css',
};
} catch (error) {
return {
errors: [ {
id: error.name,
text: error.message,
detail: error.stack
} ],
contents: '',
loader: 'css',
};
}


},
);
}
import sass from 'https://deno.land/x/denosass@1.0.6/mod.ts';
import {
BuildOptions,
OnLoadArgs,
OnLoadOptions,
OnLoadResult,
} from 'https://deno.land/x/esbuild@v0.19.11/mod.js';
import getTextContent from './get_text_content.ts';

/**
* This function registers the onLoad function and sets some initial options.
*
* @param initialOptions Initial options from ESBuild for the plugin to use
* @param onLoadFunction The `onLoad` function for the plugin
*/
export default function sassPluginSetup(
initialOptions: BuildOptions,
onLoadFunction: (
options: OnLoadOptions,
callback: (
args: OnLoadArgs,
) =>
| OnLoadResult
| null
| undefined
| Promise<OnLoadResult | null | undefined>,
) => void,
): void {
onLoadFunction(
{ filter: /\.scss$/ },
async (args) => {
const file = await Deno.readTextFile(args.path);
try {
const css = sass(
file,
{
style: initialOptions.minify ? 'compressed' : 'expanded'
}
).to_string();

return {
contents: getTextContent(css),
loader: 'css',
};
} catch (error) {
return {
errors: [ {
id: error.name,
text: error.message,
detail: error.stack
} ],
contents: '',
loader: 'css',
};
}


},
);
}
Deno AI Helper
Deno AI Helper7mo ago
I wasn't able to find the answer to your question in the official Deno documentation. However, looking at the code you've provided, there are no obvious syntax errors or mistakes. The code appears to be a plugin setup function for ESBuild that uses the denosass module to compile SASS files to CSS. The plugin setup function registers an onLoad function that reads the content of a .scss file, compiles it using the denosass module, and returns the resulting CSS or any errors that occurred during compilation. Since the issue you're facing is related to the resolution of import paths by the SASS module, and the code provided does not show any direct handling of import path resolution, there isn't an obvious solution within the code snippet itself. The advice provided here is not based on the Deno documentation, and I advise you to double-check the accuracy of this advice. For further assistance, you may need to consult the documentation or community for the denosass module to see if there are any options or configurations that can be used to influence the resolution of import paths when compiling SASS files.
Mqx
Mqx7mo ago
Okay how can I set the root in the onLoad function relative to the file that’s been processed?
Deno AI Helper
Deno AI Helper7mo ago
I wasn't able to find the answer to your question in the official Deno documentation. However, based on the code you've provided, if you want to set the root relative to the file that's being processed within the onLoad function, you would typically use the args.path property, which contains the path to the current file. The denosass module might accept an option to set the include paths or a similar configuration that tells the compiler where to look for files. If such an option exists, you could dynamically set it based on the directory of the current file. Here's a conceptual modification to your existing onLoad function that includes setting an includePaths option:
onLoadFunction(
{ filter: /\.scss$/ },
async (args) => {
const file = await Deno.readTextFile(args.path);
try {
// Get the directory of the current file
const directory = new URL('.', args.path).pathname;

const css = sass(
file,
{
style: initialOptions.minify ? 'compressed' : 'expanded',
// Set includePaths to the directory of the current file
includePaths: [directory],
}
).to_string();

return {
contents: getTextContent(css),
loader: 'css',
};
} catch (error) {
return {
errors: [ {
id: error.name,
text: error.message,
detail: error.stack
} ],
contents: '',
loader: 'css',
};
}
},
);
onLoadFunction(
{ filter: /\.scss$/ },
async (args) => {
const file = await Deno.readTextFile(args.path);
try {
// Get the directory of the current file
const directory = new URL('.', args.path).pathname;

const css = sass(
file,
{
style: initialOptions.minify ? 'compressed' : 'expanded',
// Set includePaths to the directory of the current file
includePaths: [directory],
}
).to_string();

return {
contents: getTextContent(css),
loader: 'css',
};
} catch (error) {
return {
errors: [ {
id: error.name,
text: error.message,
detail: error.stack
} ],
contents: '',
loader: 'css',
};
}
},
);
In this example, new URL('.', args.path).pathname is used to get the directory of the current file being processed. This directory is then passed to the includePaths option of the denosass module. Please note that this advice is not based on the Deno documentation, and I advise you to
Deno AI Helper
Deno AI Helper7mo ago
double-check the accuracy of this advice. You should consult the documentation or community for the denosass module to confirm whether it supports an includePaths option or a similar mechanism for setting the root or resolving import paths dynamically.
Mqx
Mqx7mo ago
What if the module does not accept an option for includePaths?
Deno AI Helper
Deno AI Helper7mo ago
I wasn't able to find the answer to your question in the official Deno documentation. If the denosass module does not accept an includePaths option or a similar mechanism for setting the root or resolving import paths dynamically, you may be limited in how you can influence the path resolution directly. Without the ability to configure the path resolution within the denosass module, you would typically need to look for alternative approaches or workarounds. However, based on the code and information you've provided, there are no obvious syntax errors or mistakes, and without additional functionality exposed by the denosass module, there isn't a clear solution within the current constraints. The advice provided here is not based on the Deno documentation, and I advise you to double-check the accuracy of this advice. It may be necessary to consult the documentation or community for the denosass module to explore any potential workarounds or to request additional features that could help with dynamic path resolution.
Mqx
Mqx7mo ago
Is it possible to update the current working directory dynamically for the task?
Deno AI Helper
Deno AI Helper7mo ago
According to the documentation, Deno provides a way to specify the current working directory for tasks defined in deno.json using the --cwd <path> flag when running deno task. This allows you to set the current working directory to a specific path when executing a task. Here's an example from the documentation on how to use the --cwd flag with deno task:
{
"tasks": {
"my_task": "deno run script.ts"
}
}
{
"tasks": {
"my_task": "deno run script.ts"
}
}
To run my_task with a specific working directory, you would execute:
deno task --cwd some/specific/directory my_task
deno task --cwd some/specific/directory my_task
However, this sets the working directory for the entire task. If you need to change the working directory dynamically within the task itself (for example, for each file being processed), you would need to handle that within the script or command that the task is running. If the task is running a Deno script, you could potentially change the directory from within the script using Deno.chdir(). However, this would affect the entire process and is not recommended for changing the working directory on a per-file basis. For your specific use case with the denosass module, if the module does not provide a way to set the root or include paths dynamically, and you cannot change the working directory for each file being processed, you may need to look for alternative solutions or workarounds. This could involve preprocessing the SASS files to adjust the paths before they are passed to the denosass module, or it might require changes to the denosass module itself to support dynamic path resolution. Please note that the advice provided here is not based on the Deno documentation, and I advise you to double-check the accuracy of this advice and consult the documentation or community for the denosass module
cdoremus
cdoremus7mo ago
You might want to take a look at this: https://github.com/LePichu/esbuild-plugin-sass-deno
GitHub
GitHub - LePichu/esbuild-plugin-sass-deno: Made for the poor souls ...
Made for the poor souls trying to use SASS/SCSS on Deno using ESBuild as bundler who did not know esbuild-sass-plugin was not compatible with Deno due to Dart-SASS being a pain. - GitHub - LePich...
Mqx
Mqx7mo ago
Yea I already found this plugin but it had the same issue It finally works. I included Deno.cwd() and dirname(args.path) in the loads_path array.
cdoremus
cdoremus7mo ago
What is the loads_path array? I could not find that in your code snippet or LePichu's plugin code.
Mqx
Mqx7mo ago
It tells Grass what paths to search for imports.
Mqx
Mqx7mo ago
GitHub
GitHub - connorskees/grass: A Sass compiler written purely in Rust
A Sass compiler written purely in Rust. Contribute to connorskees/grass development by creating an account on GitHub.
Mqx
Mqx7mo ago
This is what’s used under the hood
Mqx
Mqx7mo ago
GitHub
denosass/src/wasm/grass.deno.js at main · hironichu/denosass
DenoSass is a complete nearly fully featured Sass compiler for Deno and Browser - hironichu/denosass
Mqx
Mqx7mo ago
The Plugin I built is far from perfect. But it does the job done
Mqx
Mqx7mo ago
GitHub
GitHub - DenoPlayground/esbuild-plugin-sass: A SASS Plugin for the ...
A SASS Plugin for the Deno version of ESBuild. Contribute to DenoPlayground/esbuild-plugin-sass development by creating an account on GitHub.