-
Notifications
You must be signed in to change notification settings - Fork 47
Source Maps
Source map links elements from compiled css file back to original less files. They are useful when troubleshooting large complicated less files. If the browser supports source maps, its debug tools works with original less files instead of compiled css version. More about source maps can be read on html5 rocks or tutplus sites.
Source maps are a new technology and are currently supported only by Chrome and nightly builds of Firefox.
This page starts with source mapping basics and then describes how to use LessCompiler
interface to generate source maps. The last section describes how to move source maps around file system once they are created. Source map generation via command line is explained on another page.
Source mapping involves three different kind of files:
- source map,
- generated css,
- original less files.
Source map, generated css and original less files can be either stored in separate files or embedded into each other. First sub-section explains the most common case - separate files. Second sub-section shows source map embedded into css and the last one shows less file embedded into source map.
Source map, generated css and original less files are linked to each other via relative references:
- compiled css contains relative path to its source map,
- source map contains relative path to compiled css,
- source map contains relative path to original less.
Unless told otherwise, less4j assumes that original less, compiled css and generated source map are all stored in the same directory and their names differ only by suffix. It is possible to supply custom name and location for compiled css file, however source map name can not be customized.
If any of these relative paths does not work correctly, source map may not work. Therefore, renaming/moving source map or generated css involves more then just copying files around. If you rename or move any of these files without updating their relative references, source map stops to work.
Compiled css links source map - see the ending comment:
.class {
margin: 1 1 1 1;
}
/*# sourceMappingURL=sampleInput.css.map */
Source map links both compiled css and original less file:
{
"version":3,
"file":"sampleInput.css", //relative path to compiled css
"lineCount":1,
"mappings":"AAAAA;",
"sources":["sampleInput.less"], //relative path to original less
"sourcesContent":[null],
"names":[".class"]
}
The sourceMappingURL
generated in the end of the file supports the data URL scheme. Whole source map can be stored inside generated css. Such map is sometimes referred to as "inline". Inline source map still contains relative paths to original less files, so if you move them someplace else, you have to update the map too.
Above source map was Base64 encoded and embedded inside css file:
.class {
margin: 1 1 1 1;
}
/*# sourceMappingURL=data:application/json;base64,ewoidmVyc2lvbiI6MywKImZpbGUiOiJzbS1pbmxpbmUuY3NzIiwKImxpbmVDb3VudCI6MSwKIm1hcHBpbmdzIjoiQUFBQUE7IiwKInNvdXJjZXMiOlsic20taW5saW5lLmxlc3MiXSwKIm5hbWVzIjpbIi5jbGFzcyJdCn0K */
Source map can contain content of compiled less files. This makes source map independent of original files locations. Following source map contains less files:
{
"version":3,
"file":"",
"lineCount":1,
"mappings":"AAAAA;",
"sources":[null],
"sourcesContent":[".class {\n margin: 1 1 1 1;\n}"], //less file content is here
"names":[".class"]
}
Source map containing original source files can be embedded into generated css. Resulting css contains source map, is fully self contained, and can be moved around at wish.
Inline source map contains original less file:
.class {
margin: 1 1 1 1;
}
/*# sourceMappingURL=data:application/json;base64,ewoidmVyc2lvbiI6MywKImZpbGUiOiIiLAoibGluZUNvdW50IjoxLAoibWFwcGluZ3MiOiJBQUFBQTsiLAoic291cmNlcyI6W251bGxdLAoic291cmNlc0NvbnRlbnQiOlsiLmNsYXNzIHtcbiAgbWFyZ2luOiAxIDEgMSAxO1xufSJdLAoibmFtZXMiOlsiLmNsYXNzIl0KfQo= */
LessCompiler
always creates source map. Keep in mind that if the compiler does not know .less and .css files locations, generated source map is not valid.
Less location uri is known from input LessSource
and css location uri is taken from cssResultLocation
property of Configuration
object. Css result location property is optional. If it is not available, the compiler will assume that generated .css file will be stored at the same location as original less file and will have the same name.
Remaining source map configuration is stored in SourceMapConfiguration
available from Configuration
object. Available properties:
linkSourceMap
- If set to
false
, generated css does not contain link to source map file. - Default:
true
. inline
- If set to
true
, whole source map is encoded and embedded into generated css. - Default:
false
. includeSourcesContent
- If set to
true
, content of compiled (source) files is included inside source map. Source map is independent of compiled less files locations. - Default:
false
. relativizePaths
- If set to
false
, final source map contains unmodified (absolute) paths to original less files. If set totrue
, generated map contains relative paths. Note that "correct" source map should contain relative paths. Use this option only if you need some kind of post processing on generated map. - Default:
true
. encodingCharset
- Source map and source map link encoding charset.
- Default:
UTF-8
. sourceMapNameGenerator
- Supply custom generator for the url of the source map file.
- Some browsers (Chrome (35) and Firefox (30)) are unable to find source map if its name does not correspond perfectly with css file name. If the system mangle sent css file name before sending it to the browser, for example adds version information, automatically generated source map url would not work correctly.
- Default: the extension of original CSS source (
.css
) is replaced by.css.map
.
Following code compiles src/sampleInput.less
file, includes content of that file into source map and embeds everything into generated css:
File lessFile = new File("src/sampleInput.less").getAbsoluteFile();
LessCompiler compiler = new DefaultLessCompiler();
Configuration configuration = new Configuration();
configuration.getSourceMapConfiguration().setInline(true);
configuration.getSourceMapConfiguration().setIncludeSourcesContent(true);
CompilationResult compilationResult = compiler.compile(lessFile, configuration);
System.out.println(compilationResult.getCss());
Css looks like this:
.class {
margin: 1 1 1 1;
}
/*# sourceMappingURL=data:application/json;base64,ewoidmVyc2lvbiI6MywKImZpbGUiOiIiLAoibGluZUNvdW50IjoxLAoibWFwcGluZ3MiOiJBQUFBQTsiLAoic291cmNlcyI6W251bGxdLAoic291cmNlc0NvbnRlbnQiOlsiLmNsYXNzIHtcbiAgbWFyZ2luOiAxIDEgMSAxO1xufSJdLAoibmFtZXMiOlsiLmNsYXNzIl0KfQo= */
Decoded source map contains original less file:
{
"version":3,
"file":"sampleInput.css",
"lineCount":1,
"mappings":"AAAAA;",
"sources":["sampleInput.less"],
"sourcesContent":[".class {\n margin: 1 1 1 1;\n}], \\ original less file is stored here
"names":[".class"]
}
Following code compiles content of src/sampleInput.less
file without specifying css file location:
File lessFile = new File("src/sampleInput.less").getAbsoluteFile();
LessCompiler compiler = new DefaultLessCompiler();
CompilationResult compilationResult = compiler.compile(lessFile);
System.out.println(compilationResult.getCss());
System.out.println(compilationResult.getSourceMap());
The compiler expects the map to be stored in src/sampleInput.css.map
file and generated .css to be stored in src/sampleInput.css
. Css looks like this:
.class {
margin: 1 1 1 1;
}
/*# sourceMappingURL=sampleInput.css.map */
source map contains links to both compiled .css and original less files:
{
"version":3,
"file":"sampleInput.css",
"lineCount":1,
"mappings":"AAAAA;",
"sources":["sampleInput.less"],
"sourcesContent":[null],
"names":[".class"]
}
Following code compiles src/sampleInput.less
file and embeds map into generated css:
File lessFile = new File("src/sampleInput.less").getAbsoluteFile();
LessCompiler compiler = new DefaultLessCompiler();
Configuration configuration = new Configuration();
configuration.getSourceMapConfiguration().setInline(true);
CompilationResult compilationResult = compiler.compile(lessFile, configuration);
System.out.println(compilationResult.getCss());
Css looks like this:
.class {
margin: 1 1 1 1;
}
/*# sourceMappingURL=data:application/json;base64,ewoidmVyc2lvbiI6MywKImZpbGUiOiJzbS1pbmxpbmUuY3NzIiwKImxpbmVDb3VudCI6MSwKIm1hcHBpbmdzIjoiQUFBQUE7IiwKInNvdXJjZXMiOlsic20taW5saW5lLmxlc3MiXSwKIm5hbWVzIjpbIi5jbGFzcyJdCn0K */
Decoded source map is exactly the same as the one in above example:
{
"version":3,
"file":"sampleInput.css",
"lineCount":1,
"mappings":"AAAAA;",
"sources":["sampleInput.less"],
"sourcesContent":[null],
"names":[".class"]
}
Using StringSource
to supply uri of less file location without having to create the file:
URI uri = (new File("src/sampleInput.less")).toURI();
StringSource lessSource = new StringSource(".class { margin: 1 1 1 1; }", "sampleInput.less", uri);
LessCompiler compiler = new DefaultLessCompiler();
CompilationResult compilationResult = compiler.compile(lessSource);
It creates exactly the same .css and .css.map files as previous example.
If you want to compile src/sampleInput.less
into dist/sampleInput.css
, then you can use following code:
File lessFile = new File("src/sampleInput.less").getAbsoluteFile();
File cssFile = new File("dist/sampleInput.css").getAbsoluteFile();
Configuration configuration = new Configuration();
configuration.setCssResultLocation(cssFile);
LessCompiler compiler = new DefaultLessCompiler();
CompilationResult compilationResult = compiler.compile(lessFile, configuration);
System.out.println(compilationResult.getCss());
System.out.println(compilationResult.getSourceMap());
The compiler expects the map to be stored in dist/sampleInput.css.map
file, so the generated css looks like this:
.class {
margin: 1 1 1 1;
}
/*# sourceMappingURL=sampleInput.css.map */
generated source map contains links to both compiled .css and original less files:
{
"version":3,
"file":"sampleInput.css",
"lineCount":1,
"mappings":"AAAAA;",
"sources":["../src/sampleInput.less"],
"sourcesContent":[null],
"names":[".class"]
}
Using StringSource
to supply uri of less file location without having to create the file:
File cssFile = new File("dist/sampleInput.css").getAbsoluteFile();
Configuration configuration = new Configuration();
configuration.setCssResultLocation(cssFile);
URI uri = (new File("src/sampleInput.less")).toURI();
StringSource lessSource = new StringSource(".class { margin: 1 1 1 1; }", "sampleInput.less", uri);
LessCompiler compiler = new DefaultLessCompiler();
CompilationResult compilationResult = compiler.compile(lessSource, configuration);
Moving .less, .css and .css.map files is straightforward as long as their relative positions do not change. However, if you move only some of these files or if you change their names, then you have to update their relative paths too:
- change relative path from css to source map inside
/*# sourceMappingURL=<name>.css.map */
comment at the end of css file, - change relative path from source map to css file inside
file
property of source map json, - change relative path from source map to less files inside
sources
property of source map json.