1b2e7d2016-11-23Pontus Östlund /* Author: Pontus Östlund <https://profiles.google.com/poppanator> */
8384a22016-12-03Pontus Östlund 
b9018d2016-11-29Pontus Östlund //! Sass is a scripting language that is interpreted into Cascading Style //! Sheets (CSS). This module is a glue for @tt{libsass@}. //! //! @seealso //! SASS @url{http://sass-lang.com/@}
1b2e7d2016-11-23Pontus Östlund  #pike __REAL_VERSION__
b9018d2016-11-29Pontus Östlund #require constant(Tools@module@)
1b2e7d2016-11-23Pontus Östlund 
e1370d2016-12-01Pontus Östlund //! @ignore
1b2e7d2016-11-23Pontus Östlund inherit Tools@module@;
e1370d2016-12-01Pontus Östlund //! @endignore
1b2e7d2016-11-23Pontus Östlund 
148c002016-12-04Pontus Östlund //! Sass/SCSS compiler. //!
b9018d2016-11-29Pontus Östlund //! @example //! @code
8384a22016-12-03Pontus Östlund //! Tools.Sass.Compiler compiler = Tools.Sass.Compiler();
148c002016-12-04Pontus Östlund //! //! // Allow for HTTP imports, disallowed by default. //! compiler->http_import = Tools.Sass.HTTP_IMPORT_ANY; //!
b9018d2016-11-29Pontus Östlund //! // Minify the output and create a source map file. //! compiler->set_options(([ //! "output_style" : Tools.Sass.STYLE_COMPRESSED //! "source_map_file" : "path/to/write/source.map" //! ])); //! //! if (mixed e = catch(compiler->compile_file("input.scss", "output.css"))) { //! werror("Failed compiling input.scss to output.css\n"); //! } //! @endcode
8384a22016-12-03Pontus Östlund class Compiler
1b2e7d2016-11-23Pontus Östlund {
8384a22016-12-03Pontus Östlund  //! @ignore
b9018d2016-11-29Pontus Östlund  inherit Tools@module@.Api;
8384a22016-12-03Pontus Östlund  //! @endignore
1b2e7d2016-11-23Pontus Östlund 
72603a2016-12-03Pontus Östlund  //! If a Sass file is importing an external URI this flag determines if //! thats allowed at all, or if the content type of the imported file has
8384a22016-12-03Pontus Östlund  //! to be text/scss or if anything goes. Default is @[HTTP_IMPORT_NONE].
72603a2016-12-03Pontus Östlund  //! //! @seealso
8384a22016-12-03Pontus Östlund  //! @[HTTP_IMPORT_NONE], @[HTTP_IMPORT_GREEDY] and //! @[HTTP_IMPORT_ANY].
148c002016-12-04Pontus Östlund  public int(0..2) http_import = HTTP_IMPORT_NONE; //! Should file access be tested right away when paths are set or should that //! be left to Sass to handle? The default value is @tt{true@}. public bool check_file_access = true;
72603a2016-12-03Pontus Östlund  //! @ignore protected void create() { ::__set_importer_callback(__resolve_import); } //! @endignore
4f0a5f2016-12-05Pontus Östlund 
72603a2016-12-03Pontus Östlund  //! @ignore
4f0a5f2016-12-05Pontus Östlund  //! Resolve external imports in sass/scss files.
72603a2016-12-03Pontus Östlund  protected string __resolve_import(string path) { Standards.URI uri;
148c002016-12-04Pontus Östlund  // If it's not an URI we assume it's a local import and we let Sass handle // it. This could of course be a, by mistake, malformed URI, but then Sass // will eventually throw.
72603a2016-12-03Pontus Östlund  if (catch (uri = Standards.URI(path))) { return UNDEFINED; }
148c002016-12-04Pontus Östlund  if (http_import == HTTP_IMPORT_NONE) {
8384a22016-12-03Pontus Östlund  error("Imports over HTTP not allowed!\n"); }
72603a2016-12-03Pontus Östlund  Protocols.HTTP.Query q = Protocols.HTTP.get_url(uri); if (q->status != 200) { error("Bad HTTP status (%d) for @import %q!\n", q->status, (string) uri); } array(string) ct_parts = map(q->headers["content-type"]/";", String.trim_all_whites);
148c002016-12-04Pontus Östlund  if (http_import == HTTP_IMPORT_GREEDY) {
72603a2016-12-03Pontus Östlund  if (ct_parts[0] != "text/scss") { error("Returned content type from import (%s) was %q. " "Expected \"text/scss\"!\n", uri, ct_parts[0]); } } string(8bit) data = q->data(); if (sizeof(ct_parts) > 1) { sscanf(ct_parts[1], "%*s=%s", string charset);
4f0a5f2016-12-05Pontus Östlund  // In case of charset="utf-8" or charset='utf-8', remove the "fnutts"
72603a2016-12-03Pontus Östlund  if (charset && charset[0] < 65) { charset = charset[1..<1]; } } return data; } //! @endignore
4f0a5f2016-12-05Pontus Östlund  // Documented in the CMOD void `include_path=(string(8bit) path)
148c002016-12-04Pontus Östlund  { if (check_file_access && !Stdio.exist(path)) {
4f0a5f2016-12-05Pontus Östlund  error("Include path %q does not exist!\n", path);
148c002016-12-04Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  ::include_path = path;
148c002016-12-04Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  // Documented in the CMOD string `include_path() { return ::include_path;
dd97562016-12-04Pontus Östlund  }
acafa02016-12-01Pontus Östlund  //! Compile the file @[input_file] and return the result //! //! @param input_file //! The SCSS file to compile //! //! @returns //! A mapping with the generated CSS and source mapping file if such is //! set to be generated //! //! @mapping //! @member string "css" //! The generated CSS //! @member string "map" //! The generated source mapping data //! @endmapping mapping(string:string) compile_file(string input_file) {
148c002016-12-04Pontus Östlund  if (check_file_access && !Stdio.exist(input_file)) { error("Input file %q does not exist or isn't accessible!\n", input_file); }
acafa02016-12-01Pontus Östlund  mapping(string:string) val = ::compile_file(input_file); return val; } //! Compile the file @[input_file] and write the result to @[output_file]. //! If a source mapping file is set to be generated either via //! @[set_options()] or @[set_source_map_file()] it will be written as per //! the value set in the option. //! //! @param input_file //! The SCSS file to compile //! @param output_file //! The name of the CSS file to save the result in. variant void compile_file(string input_file, string output_file) {
148c002016-12-04Pontus Östlund  if (check_file_access && !Stdio.exist(input_file)) { error("Input file %q does not exist or isn't accessible!\n", input_file); }
acafa02016-12-01Pontus Östlund  mapping(string:string) val = ::compile_file(input_file); Stdio.write_file(output_file, val->css);
4f0a5f2016-12-05Pontus Östlund  if (val->map && source_map_file) { Stdio.write_file(source_map_file, val->map);
acafa02016-12-01Pontus Östlund  } } //! Compile the string @[source] //!
148c002016-12-04Pontus Östlund  //! @note //! If the @[source] contain @tt{@@import@} directives you have to //! explicitly set the include path via @[set_include_path()]. //!
acafa02016-12-01Pontus Östlund  //! @param source //! The string to compile string(8bit) compile_string(string(8bit) source) { string(8bit) out = ::compile_string(source); return out; }
72603a2016-12-03Pontus Östlund 
dd8a1a2016-11-29Pontus Östlund  //! Set options to the SASS compiler. @[opts]
1b2e7d2016-11-23Pontus Östlund  //! //! @param opts //! @mapping //! @member int "output_style" //! Any of the @[STYLE_NESTED], @[STYLE_EXPANDED], @[STYLE_COMPACT]
4f0a5f2016-12-05Pontus Östlund  //! or @[STYLE_COMPRESSED] constants. See also @[output_style].
e1370d2016-12-01Pontus Östlund  //!
1b2e7d2016-11-23Pontus Östlund  //! @member string "include_path"
4f0a5f2016-12-05Pontus Östlund  //! Path to root of incude files. See also @[include_path].
e1370d2016-12-01Pontus Östlund  //!
1b2e7d2016-11-23Pontus Östlund  //! @member string "source_map_file"
4f0a5f2016-12-05Pontus Östlund  //! File to write source map file to. //! See also @[source_map_file].
e1370d2016-12-01Pontus Östlund  //!
1b2e7d2016-11-23Pontus Östlund  //! @member bool "source_comments" //! Turn on/off comments in the output containing info about the source
ef03332016-11-26Pontus Östlund  //! file - line numbers and such. Default of @tt{false@}. See also
4f0a5f2016-12-05Pontus Östlund  //! @[source_comments].
e1370d2016-12-01Pontus Östlund  //!
1b2e7d2016-11-23Pontus Östlund  //! @member bool "source_map_embed" //! Turn on/off if a source map should be embedded in the output or not.
4f0a5f2016-12-05Pontus Östlund  //! Default is @tt{false@}. See also @[source_map_embed].
e1370d2016-12-01Pontus Östlund  //! //! @member string "source_map_root" //! Set the root path of the source files, relative to where the //! source.map file is written.
4f0a5f2016-12-05Pontus Östlund  //! See also @[source_map_root]
e1370d2016-12-01Pontus Östlund  //!
4f0a5f2016-12-05Pontus Östlund  //! @member bool "omit_source_map_url"
e1370d2016-12-01Pontus Östlund  //! Omit the #sourceMappingURL or not.
4f0a5f2016-12-05Pontus Östlund  //! See also @[omit_source_map_url]
1b2e7d2016-11-23Pontus Östlund  //! @endmapping void set_options(mapping(string:string|int) opts) { foreach (opts; string opt; string|int val) { switch (opt) { case "output_style": if (!intp(val)) {
4f0a5f2016-12-05Pontus Östlund  error("Value to `output_style` must be an integer!\n");
1b2e7d2016-11-23Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  output_style = val;
1b2e7d2016-11-23Pontus Östlund  break; case "include_path": if (!stringp(val)) {
4f0a5f2016-12-05Pontus Östlund  error("Value to `include_path` must be a string!\n");
1b2e7d2016-11-23Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  include_path = val;
1b2e7d2016-11-23Pontus Östlund  break; case "source_map_file": if (!stringp(val)) {
4f0a5f2016-12-05Pontus Östlund  error("Value to `source_map_file` must be a string!\n");
1b2e7d2016-11-23Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  source_map_file = val;
1b2e7d2016-11-23Pontus Östlund  break; case "source_map_embed": if (!intp(val)) {
4f0a5f2016-12-05Pontus Östlund  error("Value to `source_map_embed` must be an int(0..1)!\n");
1b2e7d2016-11-23Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  source_map_embed = val;
1b2e7d2016-11-23Pontus Östlund  break;
18ad792016-11-24Pontus Östlund  case "source_map_root": if (!stringp(val)) {
4f0a5f2016-12-05Pontus Östlund  error("Value to `source_map_root` must be a string!\n");
18ad792016-11-24Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  source_map_root = val;
18ad792016-11-24Pontus Östlund  break;
4f0a5f2016-12-05Pontus Östlund  case "omit_source_map_url":
18ad792016-11-24Pontus Östlund  if (!intp(val)) {
4f0a5f2016-12-05Pontus Östlund  error("Value to `omit_source_map_url` must be an integer!\n");
18ad792016-11-24Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  omit_source_map_url = val;
18ad792016-11-24Pontus Östlund  break;
1b2e7d2016-11-23Pontus Östlund  case "source_comments": if (!intp(val)) {
4f0a5f2016-12-05Pontus Östlund  error("Value to `source_comments` must be an integer!\n");
1b2e7d2016-11-23Pontus Östlund  }
4f0a5f2016-12-05Pontus Östlund  source_comments = val;
1b2e7d2016-11-23Pontus Östlund  break;
18ad792016-11-24Pontus Östlund  default: error("Unknown option %O!\n", opt);
1b2e7d2016-11-23Pontus Östlund  } } } }