sassc 1.8.1 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/ext/libsass/Makefile +10 -6
  4. data/ext/libsass/Readme.md +4 -4
  5. data/ext/libsass/appveyor.yml +16 -1
  6. data/ext/libsass/docs/README.md +1 -1
  7. data/ext/libsass/docs/api-context-example.md +1 -1
  8. data/ext/libsass/docs/api-context.md +1 -1
  9. data/ext/libsass/docs/api-doc.md +1 -1
  10. data/ext/libsass/docs/api-function-example.md +12 -3
  11. data/ext/libsass/docs/api-function-internal.md +4 -4
  12. data/ext/libsass/docs/api-function.md +15 -13
  13. data/ext/libsass/docs/api-importer-internal.md +9 -4
  14. data/ext/libsass/docs/api-value.md +1 -1
  15. data/ext/libsass/docs/build-shared-library.md +3 -3
  16. data/ext/libsass/docs/custom-functions-internal.md +1 -1
  17. data/ext/libsass/docs/{plugins.go → plugins.md} +0 -0
  18. data/ext/libsass/script/ci-build-libsass +25 -36
  19. data/ext/libsass/script/ci-install-deps +3 -8
  20. data/ext/libsass/script/ci-report-coverage +17 -13
  21. data/ext/libsass/src/ast.cpp +102 -7
  22. data/ext/libsass/src/ast.hpp +53 -27
  23. data/ext/libsass/src/ast_def_macros.hpp +8 -0
  24. data/ext/libsass/src/ast_fwd_decl.hpp +3 -2
  25. data/ext/libsass/src/backtrace.hpp +1 -1
  26. data/ext/libsass/src/bind.cpp +28 -17
  27. data/ext/libsass/src/bind.hpp +1 -1
  28. data/ext/libsass/src/context.cpp +441 -184
  29. data/ext/libsass/src/context.hpp +79 -82
  30. data/ext/libsass/src/debugger.hpp +3 -1
  31. data/ext/libsass/src/emitter.cpp +18 -17
  32. data/ext/libsass/src/emitter.hpp +5 -2
  33. data/ext/libsass/src/error_handling.cpp +78 -7
  34. data/ext/libsass/src/error_handling.hpp +50 -9
  35. data/ext/libsass/src/eval.cpp +100 -36
  36. data/ext/libsass/src/eval.hpp +5 -5
  37. data/ext/libsass/src/expand.cpp +32 -3
  38. data/ext/libsass/src/extend.cpp +1 -1
  39. data/ext/libsass/src/file.cpp +39 -27
  40. data/ext/libsass/src/file.hpp +67 -13
  41. data/ext/libsass/src/functions.cpp +39 -32
  42. data/ext/libsass/src/inspect.cpp +21 -21
  43. data/ext/libsass/src/json.cpp +1 -1
  44. data/ext/libsass/src/lexer.hpp +33 -4
  45. data/ext/libsass/src/output.cpp +11 -11
  46. data/ext/libsass/src/parser.cpp +28 -130
  47. data/ext/libsass/src/parser.hpp +0 -4
  48. data/ext/libsass/src/prelexer.cpp +8 -5
  49. data/ext/libsass/src/prelexer.hpp +1 -3
  50. data/ext/libsass/src/sass_context.cpp +52 -241
  51. data/ext/libsass/src/sass_context.hpp +156 -0
  52. data/ext/libsass/src/sass_functions.cpp +1 -26
  53. data/ext/libsass/src/sass_functions.hpp +32 -0
  54. data/ext/libsass/src/sass_interface.cpp +14 -48
  55. data/ext/libsass/src/sass_values.cpp +3 -77
  56. data/ext/libsass/src/sass_values.hpp +81 -0
  57. data/ext/libsass/src/source_map.cpp +7 -7
  58. data/ext/libsass/src/source_map.hpp +1 -4
  59. data/ext/libsass/src/to_string.cpp +4 -3
  60. data/ext/libsass/src/to_string.hpp +2 -1
  61. data/ext/libsass/src/util.cpp +34 -16
  62. data/ext/libsass/src/util.hpp +10 -8
  63. data/lib/sassc/version.rb +1 -1
  64. data/lib/tasks/libsass.rb +1 -1
  65. data/test/custom_importer_test.rb +6 -4
  66. data/test/engine_test.rb +5 -3
  67. data/test/functions_test.rb +1 -0
  68. data/test/native_test.rb +1 -1
  69. metadata +6 -4
  70. data/ext/libsass/script/coveralls-debug +0 -32
@@ -12,7 +12,7 @@ namespace Sass {
12
12
  class Eval;
13
13
  typedef Environment<AST_Node*> Env;
14
14
 
15
- void bind(std::string caller, Parameters*, Arguments*, Context&, Env*, Eval*);
15
+ void bind(std::string type, std::string name, Parameters*, Arguments*, Context*, Env*, Eval*);
16
16
  }
17
17
 
18
18
  #endif
@@ -38,88 +38,80 @@ namespace Sass {
38
38
  using namespace File;
39
39
  using namespace Sass;
40
40
 
41
- Sass_Queued::Sass_Queued(const std::string& load_path, const std::string& abs_path, const char* source)
41
+ inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
42
+ { return sass_importer_get_priority(i) > sass_importer_get_priority(j); }
43
+
44
+ static std::string safe_input(const char* in_path)
42
45
  {
43
- this->load_path = load_path;
44
- this->abs_path = abs_path;
45
- this->source = source;
46
+ // enforce some safe defaults
47
+ // used to create relative file links
48
+ std::string safe_path(in_path ? in_path : "");
49
+ return safe_path == "" ? "stdin" : safe_path;
46
50
  }
47
51
 
48
- inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
49
- { return sass_importer_get_priority(i) > sass_importer_get_priority(j); }
52
+ static std::string safe_output(const char* out_path, const std::string& input_path = "")
53
+ {
54
+ std::string safe_path(out_path ? out_path : "");
55
+ // maybe we can extract an output path from input path
56
+ if (safe_path == "" && input_path != "") {
57
+ int lastindex = static_cast<int>(input_path.find_last_of("."));
58
+ return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";
59
+ }
60
+ // enforce some safe defaults
61
+ // used to create relative file links
62
+ return safe_path == "" ? "stdout" : safe_path;
63
+ }
50
64
 
51
- Context::Context(Context::Data initializers)
52
- : // Output(this),
65
+ Context::Context(struct Sass_Context* c_ctx)
66
+ : CWD(File::get_cwd()),
67
+ entry_path(""),
53
68
  head_imports(0),
54
69
  mem(Memory_Manager()),
55
- c_options (initializers.c_options()),
56
- c_compiler (initializers.c_compiler()),
57
- source_c_str (initializers.source_c_str()),
58
- sources (std::vector<char*>()),
59
- strings (std::vector<char*>()),
60
- plugin_paths (initializers.plugin_paths()),
61
- include_paths (initializers.include_paths()),
62
- queue (std::vector<Sass_Queued>()),
63
- style_sheets (std::map<std::string, Block*>()),
64
- emitter (this),
70
+ plugins(),
71
+ emitter(this),
72
+
73
+ strings(),
74
+ resources(),
75
+ sheets(),
76
+ subset_map(),
77
+ import_stack(),
78
+
79
+ c_options (c_ctx),
80
+
65
81
  c_headers (std::vector<Sass_Importer_Entry>()),
66
82
  c_importers (std::vector<Sass_Importer_Entry>()),
67
83
  c_functions (std::vector<Sass_Function_Entry>()),
68
- indent (initializers.indent()),
69
- linefeed (initializers.linefeed()),
70
- input_path (make_canonical_path(initializers.input_path())),
71
- output_path (make_canonical_path(initializers.output_path())),
72
- source_comments (initializers.source_comments()),
73
- output_style (initializers.output_style()),
74
- source_map_file (make_canonical_path(initializers.source_map_file())),
75
- source_map_root (initializers.source_map_root()), // pass-through
76
- source_map_embed (initializers.source_map_embed()),
77
- source_map_contents (initializers.source_map_contents()),
78
- omit_source_map_url (initializers.omit_source_map_url()),
79
- is_indented_syntax_src (initializers.is_indented_syntax_src()),
80
- precision (initializers.precision()),
81
- plugins(),
82
- subset_map (Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> >())
83
- {
84
84
 
85
- cwd = get_cwd();
85
+ indent (safe_str(c_options->indent, " ")),
86
+ linefeed (safe_str(c_options->linefeed, "\n")),
86
87
 
87
- // enforce some safe defaults
88
- // used to create relative file links
89
- if (input_path == "") input_path = "stdin";
90
- if (output_path == "") output_path = "stdout";
88
+ input_path (make_canonical_path(safe_input(c_options->input_path))),
89
+ output_path (make_canonical_path(safe_output(c_options->output_path, input_path))),
90
+ source_map_file (make_canonical_path(safe_str(c_options->source_map_file, ""))),
91
+ source_map_root (make_canonical_path(safe_str(c_options->source_map_root, "")))
92
+
93
+ {
91
94
 
92
- include_paths.push_back(cwd);
93
- collect_include_paths(initializers.include_paths_c_str());
95
+ // add cwd to include paths
96
+ include_paths.push_back(CWD);
97
+
98
+ // collect more paths from different options
99
+ collect_include_paths(sass_option_get_include_path(c_options));
94
100
  // collect_include_paths(initializers.include_paths_array());
95
- collect_plugin_paths(initializers.plugin_paths_c_str());
101
+ collect_plugin_paths(sass_option_get_plugin_path(c_options));
96
102
  // collect_plugin_paths(initializers.plugin_paths_array());
97
103
 
98
- for (size_t i = 0, S = plugin_paths.size(); i < S; ++i) {
99
- plugins.load_plugins(plugin_paths[i]);
100
- }
101
-
102
- for(auto fn : plugins.get_functions()) {
103
- c_functions.push_back(fn);
104
- }
105
- for(auto fn : plugins.get_headers()) {
106
- c_headers.push_back(fn);
107
- }
108
- for(auto fn : plugins.get_importers()) {
109
- c_importers.push_back(fn);
110
- }
104
+ // load plugins and register custom behaviors
105
+ for(auto plug : plugin_paths) plugins.load_plugins(plug);
106
+ for(auto fn : plugins.get_headers()) c_headers.push_back(fn);
107
+ for(auto fn : plugins.get_importers()) c_importers.push_back(fn);
108
+ for(auto fn : plugins.get_functions()) c_functions.push_back(fn);
111
109
 
110
+ // sort the items by priority (lowest first)
112
111
  sort (c_headers.begin(), c_headers.end(), sort_importers);
113
112
  sort (c_importers.begin(), c_importers.end(), sort_importers);
114
- std::string entry_point = initializers.entry_point();
115
- if (!entry_point.empty()) {
116
- std::string result(add_file(entry_point, true));
117
- if (result.empty()) {
118
- throw "File to read not found or unreadable: " + entry_point;
119
- }
120
- }
121
113
 
122
- emitter.set_filename(resolve_relative_path(output_path, source_map_file, cwd));
114
+ emitter.set_filename(abs2rel(output_path, source_map_file, CWD));
123
115
 
124
116
  }
125
117
 
@@ -142,16 +134,35 @@ namespace Sass {
142
134
 
143
135
  Context::~Context()
144
136
  {
145
- // make sure we free the source even if not processed!
146
- if (sources.size() == 0 && source_c_str) free(source_c_str);
147
- // sources are allocated by strdup or malloc (overtaken from C code)
148
- for (size_t i = 0; i < sources.size(); ++i) free(sources[i]);
137
+ // resources were allocated by strdup or malloc
138
+ for (size_t i = 0; i < resources.size(); ++i) {
139
+ free(resources[i].contents);
140
+ free(resources[i].srcmap);
141
+ }
149
142
  // free all strings we kept alive during compiler execution
150
143
  for (size_t n = 0; n < strings.size(); ++n) free(strings[n]);
151
144
  // everything that gets put into sources will be freed by us
152
- for (size_t m = 0; m < import_stack.size(); ++m) sass_delete_import(import_stack[m]);
145
+ // this shouldn't have anything in it anyway!?
146
+ for (size_t m = 0; m < import_stack.size(); ++m) {
147
+ sass_import_take_source(import_stack[m]);
148
+ sass_import_take_srcmap(import_stack[m]);
149
+ sass_delete_import(import_stack[m]);
150
+ }
153
151
  // clear inner structures (vectors) and input source
154
- sources.clear(); import_stack.clear(); source_c_str = 0;
152
+ resources.clear(); import_stack.clear();
153
+ }
154
+
155
+ Data_Context::~Data_Context()
156
+ {
157
+ // --> this will be freed by resources
158
+ // make sure we free the source even if not processed!
159
+ // if (resources.size() == 0 && source_c_str) free(source_c_str);
160
+ // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str);
161
+ // source_c_str = 0; srcmap_c_str = 0;
162
+ }
163
+
164
+ File_Context::~File_Context()
165
+ {
155
166
  }
156
167
 
157
168
  void Context::collect_include_paths(const char* paths_str)
@@ -181,10 +192,9 @@ namespace Sass {
181
192
 
182
193
  void Context::collect_include_paths(const char** paths_array)
183
194
  {
184
- if (paths_array) {
185
- for (size_t i = 0; paths_array[i]; i++) {
186
- collect_include_paths(paths_array[i]);
187
- }
195
+ if (!paths_array) return;
196
+ for (size_t i = 0; paths_array[i]; i++) {
197
+ collect_include_paths(paths_array[i]);
188
198
  }
189
199
  }
190
200
 
@@ -215,70 +225,255 @@ namespace Sass {
215
225
 
216
226
  void Context::collect_plugin_paths(const char** paths_array)
217
227
  {
218
- if (paths_array) {
219
- for (size_t i = 0; paths_array[i]; i++) {
220
- collect_plugin_paths(paths_array[i]);
221
- }
228
+ if (!paths_array) return;
229
+ for (size_t i = 0; paths_array[i]; i++) {
230
+ collect_plugin_paths(paths_array[i]);
222
231
  }
223
232
  }
224
- void Context::add_source(std::string load_path, std::string abs_path, char* contents)
233
+
234
+
235
+ // resolve the imp_path in base_path or include_paths
236
+ // looks for alternatives and returns a list from one directory
237
+ std::vector<Include> Context::find_includes(const Importer& import)
225
238
  {
226
- sources.push_back(contents);
227
- included_files.push_back(abs_path);
228
- queue.push_back(Sass_Queued(load_path, abs_path, contents));
229
- emitter.add_source_index(sources.size() - 1);
230
- include_links.push_back(resolve_relative_path(abs_path, source_map_file, cwd));
239
+ // make sure we resolve against an absolute path
240
+ std::string base_path(rel2abs(import.base_path));
241
+ // first try to resolve the load path relative to the base path
242
+ std::vector<Include> vec(resolve_includes(base_path, import.imp_path));
243
+ // then search in every include path (but only if nothing found yet)
244
+ for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i)
245
+ {
246
+ // call resolve_includes and individual base path and append all results
247
+ std::vector<Include> resolved(resolve_includes(include_paths[i], import.imp_path));
248
+ if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end());
249
+ }
250
+ // return vector
251
+ return vec;
231
252
  }
232
253
 
233
- // Add a new import file to the context
234
- std::string Context::add_file(const std::string& file, bool delay)
254
+
255
+ // register include with resolved path and its content
256
+ // memory of the resources will be freed by us on exit
257
+ void Context::register_resource(const Include& inc, const Resource& res)
235
258
  {
236
- using namespace File;
237
- std::string path(make_canonical_path(file));
238
- std::string resolved(find_file(path, include_paths));
239
- if (resolved == "") return resolved;
240
- if (char* contents = read_file(resolved)) {
241
- add_source(path, resolved, contents);
242
- style_sheets[path] = 0;
243
- if (delay == false) {
244
- size_t i = queue.size() - 1;
245
- process_queue_entry(queue[i], i);
246
- }
247
- return path;
248
- }
249
- return std::string("");
259
+
260
+ // do not parse same resource twice
261
+ // maybe raise an error in this case
262
+ // if (sheets.count(inc.abs_path)) {
263
+ // free(res.contents); free(res.srcmap);
264
+ // throw std::runtime_error("duplicate resource registered");
265
+ // return;
266
+ // }
267
+
268
+ // get index for this resource
269
+ size_t idx = resources.size();
270
+
271
+ // tell emitter about new resource
272
+ emitter.add_source_index(idx);
273
+
274
+ // put resources under our control
275
+ // the memory will be freed later
276
+ resources.push_back(res);
277
+
278
+ // add a relative link to the working directory
279
+ included_files.push_back(inc.abs_path);
280
+ // add a relative link to the source map output file
281
+ srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD));
282
+
283
+ // get pointer to the loaded content
284
+ Sass_Import_Entry import = sass_make_import(
285
+ inc.imp_path.c_str(),
286
+ inc.abs_path.c_str(),
287
+ res.contents,
288
+ res.srcmap
289
+ );
290
+ // add the entry to the stack
291
+ import_stack.push_back(import);
292
+
293
+ // get pointer to the loaded content
294
+ const char* contents = resources[idx].contents;
295
+ // keep a copy of the path around (for parserstates)
296
+ // ToDo: we clean it, but still not very elegant!?
297
+ strings.push_back(sass_strdup(inc.abs_path.c_str()));
298
+ // create the initial parser state from resource
299
+ ParserState pstate(strings.back(), contents, idx);
300
+ // create a parser instance from the given c_str buffer
301
+ Parser p(Parser::from_c_str(contents, *this, pstate));
302
+ // do not yet dispose these buffers
303
+ sass_import_take_source(import);
304
+ sass_import_take_srcmap(import);
305
+ // then parse the root block
306
+ Block* root = p.parse();
307
+ // delete memory of current stack frame
308
+ sass_delete_import(import_stack.back());
309
+ // remove current stack frame
310
+ import_stack.pop_back();
311
+ // create key/value pair for ast node
312
+ std::pair<const std::string, const StyleSheet>
313
+ ast_pair(inc.abs_path, { res, root });
314
+ // register resulting resource
315
+ sheets.insert(ast_pair);
316
+
250
317
  }
251
318
 
252
- // Add a new import file to the context
253
- // This has some previous directory context
254
- std::string Context::add_file(const std::string& base, const std::string& file, ParserState pstate)
319
+ // Add a new import to the context (called from `import_url`)
320
+ Include Context::load_import(const Importer& imp, ParserState pstate)
255
321
  {
256
- using namespace File;
257
- std::string path(make_canonical_path(file));
258
- std::string base_file(join_paths(base, path));
259
- if (style_sheets.count(base_file)) return base_file;
260
- std::vector<Sass_Queued> resolved(resolve_file(base, path));
322
+
323
+ // search for valid imports (ie. partials) on the filesystem
324
+ // this may return more than one valid result (ambiguous imp_path)
325
+ const std::vector<Include> resolved(find_includes(imp));
326
+
327
+ // error nicely on ambiguous imp_path
261
328
  if (resolved.size() > 1) {
262
329
  std::stringstream msg_stream;
263
330
  msg_stream << "It's not clear which file to import for ";
264
- msg_stream << "'@import \"" << file << "\"'." << "\n";
331
+ msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n";
265
332
  msg_stream << "Candidates:" << "\n";
266
333
  for (size_t i = 0, L = resolved.size(); i < L; ++i)
267
- { msg_stream << " " << resolved[i].load_path << "\n"; }
334
+ { msg_stream << " " << resolved[i].imp_path << "\n"; }
268
335
  msg_stream << "Please delete or rename all but one of these files." << "\n";
269
336
  error(msg_stream.str(), pstate);
270
337
  }
271
- if (resolved.size()) {
338
+
339
+ // process the resolved entry
340
+ else if (resolved.size() == 1) {
341
+ // use cache for the resource loading
342
+ if (sheets.count(resolved[0].abs_path)) return resolved[0];
343
+ // try to read the content of the resolved file entry
344
+ // the memory buffer returned must be freed by us!
272
345
  if (char* contents = read_file(resolved[0].abs_path)) {
273
- add_source(base_file, resolved[0].abs_path, contents);
274
- style_sheets[base_file] = 0;
275
- size_t i = queue.size() - 1;
276
- process_queue_entry(queue[i], i);
277
- return base_file;
346
+ // register the newly resolved file resource
347
+ register_resource(resolved[0], { contents, 0 });
348
+ // return resolved entry
349
+ return resolved[0];
350
+ }
351
+ }
352
+
353
+ // nothing found
354
+ return { imp, "" };
355
+
356
+ }
357
+
358
+ void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) {
359
+
360
+ ParserState pstate(imp->pstate());
361
+ std::string imp_path(unquote(load_path));
362
+ std::string protocol("file");
363
+
364
+ using namespace Prelexer;
365
+ if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) {
366
+
367
+ protocol = std::string(imp_path.c_str(), proto - 3);
368
+ // std::cerr << "==================== " << protocol << "\n";
369
+ if (protocol.compare("file") && true) {
370
+
371
+ }
372
+ }
373
+
374
+ // add urls (protocol other than file) and urls without procotol to `urls` member
375
+ // ToDo: if ctx_path is already a file resource, we should not add it here?
376
+ if (imp->media_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {
377
+ imp->urls().push_back(SASS_MEMORY_NEW(mem, String_Quoted, imp->pstate(), load_path));
378
+ }
379
+ else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {
380
+ String_Constant* loc = SASS_MEMORY_NEW(mem, String_Constant, pstate, unquote(load_path));
381
+ Argument* loc_arg = SASS_MEMORY_NEW(mem, Argument, pstate, loc);
382
+ Arguments* loc_args = SASS_MEMORY_NEW(mem, Arguments, pstate);
383
+ (*loc_args) << loc_arg;
384
+ Function_Call* new_url = SASS_MEMORY_NEW(mem, Function_Call, pstate, "url", loc_args);
385
+ imp->urls().push_back(new_url);
386
+ }
387
+ else {
388
+ const Importer importer(imp_path, ctx_path);
389
+ Include include(load_import(importer, pstate));
390
+ if (include.abs_path.empty()) {
391
+ error("File to import not found or unreadable: " + imp_path + "\nParent style sheet: " + ctx_path, pstate);
392
+ }
393
+ imp->incs().push_back(include);
394
+ }
395
+
396
+ }
397
+
398
+
399
+ // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet
400
+ bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one)
401
+ {
402
+ // unique counter
403
+ size_t count = 0;
404
+ // need one correct import
405
+ bool has_import = false;
406
+ // process all custom importers (or custom headers)
407
+ for (Sass_Importer_Entry& importer : importers) {
408
+ // int priority = sass_importer_get_priority(importer);
409
+ Sass_Importer_Fn fn = sass_importer_get_function(importer);
410
+ // skip importer if it returns NULL
411
+ if (Sass_Import_List includes =
412
+ fn(load_path.c_str(), importer, c_compiler)
413
+ ) {
414
+ // get c pointer copy to iterate over
415
+ Sass_Import_List it_includes = includes;
416
+ while (*it_includes) { ++count;
417
+ // create unique path to use as key
418
+ std::string uniq_path = load_path;
419
+ if (!only_one && count) {
420
+ std::stringstream path_strm;
421
+ path_strm << uniq_path << ":" << count;
422
+ uniq_path = path_strm.str();
423
+ }
424
+ // create the importer struct
425
+ Importer importer(uniq_path, ctx_path);
426
+ // query data from the current include
427
+ Sass_Import_Entry include = *it_includes;
428
+ char* source = sass_import_take_source(include);
429
+ char* srcmap = sass_import_take_source(include);
430
+ size_t line = sass_import_get_error_line(include);
431
+ size_t column = sass_import_get_error_column(include);
432
+ const char *abs_path = sass_import_get_abs_path(include);
433
+ // handle error message passed back from custom importer
434
+ // it may (or may not) override the line and column info
435
+ if (const char* err_message = sass_import_get_error_message(include)) {
436
+ if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap });
437
+ if (line == std::string::npos && column == std::string::npos) error(err_message, pstate);
438
+ else error(err_message, ParserState(ctx_path, source, Position(line, column)));
439
+ }
440
+ // content for import was set
441
+ else if (source) {
442
+ // resolved abs_path should be set by custom importer
443
+ // use the created uniq_path as fallback (maybe enforce)
444
+ std::string path_key(abs_path ? abs_path : uniq_path);
445
+ // create the importer struct
446
+ Include include(importer, path_key);
447
+ // attach information to AST node
448
+ imp->incs().push_back(include);
449
+ // register the resource buffers
450
+ register_resource(include, { source, srcmap });
451
+ }
452
+ // only a path was retuned
453
+ // try to load it like normal
454
+ else if(abs_path) {
455
+ // checks some urls to preserve
456
+ // `http://`, `https://` and `//`
457
+ // or dispatchs to `import_file`
458
+ // which will check for a `.css` extension
459
+ // or resolves the file on the filesystem
460
+ // added and resolved via `add_file`
461
+ // finally stores everything on `imp`
462
+ import_url(imp, abs_path, ctx_path);
463
+ }
464
+ // move to next
465
+ ++it_includes;
466
+ }
467
+ // deallocate the returned memory
468
+ sass_delete_import_list(includes);
469
+ // set success flag
470
+ has_import = true;
471
+ // break out of loop
472
+ if (only_one) break;
278
473
  }
279
474
  }
280
- // now go the regular code path
281
- return add_file(path);
475
+ // return result
476
+ return has_import;
282
477
  }
283
478
 
284
479
  void register_function(Context&, Signature sig, Native_Function f, Env* env);
@@ -288,53 +483,148 @@ namespace Sass {
288
483
  void register_c_functions(Context&, Env* env, Sass_Function_List);
289
484
  void register_c_function(Context&, Env* env, Sass_Function_Entry);
290
485
 
291
- char* Context::compile_block(Block* root)
486
+ char* Context::render(Block* root)
292
487
  {
488
+ // check for valid block
293
489
  if (!root) return 0;
490
+ // start the render process
294
491
  root->perform(&emitter);
492
+ // finish emitter stream
295
493
  emitter.finalize();
494
+ // get the resulting buffer from stream
296
495
  OutputBuffer emitted = emitter.get_buffer();
297
- std::string output = emitted.buffer;
298
- if (!omit_source_map_url) {
299
- if (source_map_embed) {
300
- output += linefeed + format_embedded_source_map();
496
+ // should we append a source map url?
497
+ if (!c_options->omit_source_map_url) {
498
+ // generate an embeded source map
499
+ if (c_options->source_map_embed) {
500
+ emitted.buffer += linefeed;
501
+ emitted.buffer += format_embedded_source_map();
301
502
  }
503
+ // or just link the generated one
302
504
  else if (source_map_file != "") {
303
- output += linefeed + format_source_mapping_url(source_map_file);
505
+ emitted.buffer += linefeed;
506
+ emitted.buffer += format_source_mapping_url(source_map_file);
304
507
  }
305
508
  }
306
- return sass_strdup(output.c_str());
509
+ // create a copy of the resulting buffer string
510
+ // this must be freed or taken over by implementor
511
+ return sass_strdup(emitted.buffer.c_str());
307
512
  }
308
513
 
309
- void Context::process_queue_entry(Sass_Queued& entry, size_t i)
514
+ void Context::apply_custom_headers(Block* root, const char* ctx_path, ParserState pstate)
310
515
  {
311
- if (style_sheets[queue[i].load_path]) return;
516
+ // create a custom import to resolve headers
517
+ Import* imp = SASS_MEMORY_NEW(mem, Import, pstate);
518
+ // dispatch headers which will add custom functions
519
+ // custom headers are added to the import instance
520
+ call_headers(entry_path, ctx_path, pstate, imp);
521
+ // increase head count to skip later
522
+ head_imports += resources.size() - 1;
523
+ // add the statement if we have urls
524
+ if (!imp->urls().empty()) (*root) << imp;
525
+ // process all other resources (add Import_Stub nodes)
526
+ for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {
527
+ (*root) << SASS_MEMORY_NEW(mem, Import_Stub, pstate, imp->incs()[i]);
528
+ }
529
+ }
530
+
531
+ Block* File_Context::parse()
532
+ {
533
+
534
+ // check if entry file is given
535
+ if (input_path.empty()) return 0;
536
+
537
+ // create absolute path from input filename
538
+ // ToDo: this should be resolved via custom importers
539
+ std::string abs_path(rel2abs(input_path, CWD));
540
+
541
+ // try to load the entry file
542
+ char* contents = read_file(abs_path);
543
+
544
+ // alternatively also look inside each include path folder
545
+ // I think this differs from ruby sass (IMO too late to remove)
546
+ for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) {
547
+ // build absolute path for this include path entry
548
+ abs_path = rel2abs(input_path, include_paths[i]);
549
+ // try to load the resulting path
550
+ contents = read_file(abs_path);
551
+ }
552
+
553
+ // abort early if no content could be loaded (various reasons)
554
+ if (!contents) throw "File to read not found or unreadable: " + input_path;
555
+
556
+ // store entry path
557
+ entry_path = abs_path;
558
+
559
+ // create entry only for import stack
312
560
  Sass_Import_Entry import = sass_make_import(
313
- queue[i].load_path.c_str(),
314
- queue[i].abs_path.c_str(),
315
- 0, 0
561
+ input_path.c_str(),
562
+ entry_path.c_str(),
563
+ contents,
564
+ 0
316
565
  );
566
+ // add the entry to the stack
317
567
  import_stack.push_back(import);
318
- // keep a copy of the path around (for parser states)
319
- strings.push_back(sass_strdup(queue[i].abs_path.c_str()));
320
- ParserState pstate(strings.back(), queue[i].source, i);
321
- Parser p(Parser::from_c_str(queue[i].source, *this, pstate));
322
- Block* ast = p.parse();
323
- sass_delete_import(import_stack.back());
324
- import_stack.pop_back();
325
- // ToDo: we store by load_path, which can lead
326
- // to duplicates if importer reports the same path
327
- // Maybe we should add an error for duplicates!?
328
- style_sheets[queue[i].load_path] = ast;
568
+
569
+ // create the source entry for file entry
570
+ register_resource({{ input_path, "." }, abs_path }, { contents, 0 });
571
+
572
+ // create root ast tree node
573
+ return compile();
574
+
329
575
  }
330
576
 
331
- Block* Context::parse_file()
577
+ Block* Data_Context::parse()
332
578
  {
333
- Block* root = 0;
334
- for (size_t i = 0; i < queue.size(); ++i) {
335
- process_queue_entry(queue[i], i);
336
- if (i == 0) root = style_sheets[queue[i].load_path];
579
+
580
+ // check if source string is given
581
+ if (!source_c_str) return 0;
582
+
583
+ // convert indented sass syntax
584
+ if(c_options->is_indented_syntax_src) {
585
+ // call sass2scss to convert the string
586
+ char * converted = sass2scss(source_c_str,
587
+ // preserve the structure as much as possible
588
+ SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
589
+ // replace old source_c_str with converted
590
+ free(source_c_str); source_c_str = converted;
337
591
  }
592
+
593
+ // remember entry path (defaults to stdin for string)
594
+ entry_path = input_path.empty() ? "stdin" : input_path;
595
+
596
+ // ToDo: this may be resolved via custom importers
597
+ std::string abs_path(rel2abs(entry_path));
598
+ char* abs_path_c_str = sass_strdup(abs_path.c_str());
599
+ strings.push_back(abs_path_c_str);
600
+
601
+ // create entry only for the import stack
602
+ Sass_Import_Entry import = sass_make_import(
603
+ entry_path.c_str(),
604
+ abs_path_c_str,
605
+ source_c_str,
606
+ srcmap_c_str
607
+ );
608
+ // add the entry to the stack
609
+ import_stack.push_back(import);
610
+
611
+ // register a synthetic resource (path does not really exist, skip in includes)
612
+ register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str });
613
+
614
+ // create root ast tree node
615
+ return compile();
616
+ }
617
+
618
+
619
+
620
+ // parse root block from includes
621
+ Block* Context::compile()
622
+ {
623
+ // abort if there is no data
624
+ if (resources.size() == 0) return 0;
625
+ // get root block from the first style sheet
626
+ Block* root = sheets.at(entry_path).root;
627
+ // abort on invalid root
338
628
  if (root == 0) return 0;
339
629
 
340
630
  Env global; // create root environment
@@ -367,39 +657,11 @@ namespace Sass {
367
657
  // return processed tree
368
658
  return root;
369
659
  }
370
- // EO parse_file
371
-
372
- Block* Context::parse_string()
373
- {
374
- if (!source_c_str) return 0;
375
- queue.clear();
376
- if(is_indented_syntax_src) {
377
- char * contents = sass2scss(source_c_str, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
378
- add_source(input_path, input_path, contents);
379
- free(source_c_str);
380
- return parse_file();
381
- }
382
- add_source(input_path, input_path, source_c_str);
383
- size_t idx = queue.size() - 1;
384
- process_queue_entry(queue[idx], idx);
385
- return parse_file();
386
- }
387
-
388
- char* Context::compile_file()
389
- {
390
- // returns NULL if something fails
391
- return compile_block(parse_file());
392
- }
393
-
394
- char* Context::compile_string()
395
- {
396
- // returns NULL if something fails
397
- return compile_block(parse_string());
398
- }
660
+ // EO compile
399
661
 
400
662
  std::string Context::format_embedded_source_map()
401
663
  {
402
- std::string map = emitter.generate_source_map(*this);
664
+ std::string map = emitter.render_srcmap(*this);
403
665
  std::istringstream is( map );
404
666
  std::ostringstream buffer;
405
667
  base64::encoder E;
@@ -411,15 +673,15 @@ namespace Sass {
411
673
 
412
674
  std::string Context::format_source_mapping_url(const std::string& file)
413
675
  {
414
- std::string url = resolve_relative_path(file, output_path, cwd);
676
+ std::string url = abs2rel(file, output_path, CWD);
415
677
  return "/*# sourceMappingURL=" + url + " */";
416
678
  }
417
679
 
418
- char* Context::generate_source_map()
680
+ char* Context::render_srcmap()
419
681
  {
420
682
  if (source_map_file == "") return 0;
421
683
  char* result = 0;
422
- std::string map = emitter.generate_source_map(*this);
684
+ std::string map = emitter.render_srcmap(*this);
423
685
  result = sass_strdup(map.c_str());
424
686
  return result;
425
687
  }
@@ -429,7 +691,7 @@ namespace Sass {
429
691
  // we probably always want to skip the header includes?
430
692
  std::vector<std::string> Context::get_included_files(bool skip, size_t headers)
431
693
  {
432
- // create a copy of the vector for manupulations
694
+ // create a copy of the vector for manipulations
433
695
  std::vector<std::string> includes = included_files;
434
696
  if (includes.size() == 0) return includes;
435
697
  if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); }
@@ -439,11 +701,6 @@ namespace Sass {
439
701
  return includes;
440
702
  }
441
703
 
442
- std::string Context::get_cwd()
443
- {
444
- return Sass::File::get_cwd();
445
- }
446
-
447
704
  void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)
448
705
  {
449
706
  Definition* def = make_native_function(sig, f, ctx);