Skip to content

Commit 7757f61

Browse files
committed
Implement custom headers (experimental)
Custom headers are very similar to importers, but are run once at the start of each compile. This way a plugin can define custom mixins or insert css-code at the top of each output. Custom headers are excluded from `included_files`.
1 parent ae273d6 commit 7757f61

File tree

10 files changed

+137
-62
lines changed

10 files changed

+137
-62
lines changed

context.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ namespace Sass {
5151
}
5252

5353
inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
54-
{ return sass_importer_get_priority(i) < sass_importer_get_priority(j); }
54+
{ return sass_importer_get_priority(i) > sass_importer_get_priority(j); }
5555

5656
Context::Context(Context::Data initializers)
5757
: // Output(this),
58+
head_imports(0),
5859
mem(Memory_Manager<AST_Node>()),
5960
c_options (initializers.c_options()),
6061
c_compiler (initializers.c_compiler()),
@@ -65,8 +66,9 @@ namespace Sass {
6566
queue (vector<Sass_Queued>()),
6667
style_sheets (map<string, Block*>()),
6768
emitter (this),
68-
c_functions (vector<Sass_Function_Entry>()),
69+
c_headers (vector<Sass_Importer_Entry>()),
6970
c_importers (vector<Sass_Importer_Entry>()),
71+
c_functions (vector<Sass_Function_Entry>()),
7072
indent (initializers.indent()),
7173
linefeed (initializers.linefeed()),
7274
input_path (make_canonical_path(initializers.input_path())),
@@ -108,10 +110,14 @@ namespace Sass {
108110
for(auto fn : plugins.get_functions()) {
109111
c_functions.push_back(fn);
110112
}
113+
for(auto fn : plugins.get_headers()) {
114+
c_headers.push_back(fn);
115+
}
111116
for(auto fn : plugins.get_importers()) {
112117
c_importers.push_back(fn);
113118
}
114119

120+
sort (c_headers.begin(), c_headers.end(), sort_importers);
115121
sort (c_importers.begin(), c_importers.end(), sort_importers);
116122
string entry_point = initializers.entry_point();
117123
if (!entry_point.empty()) {
@@ -129,6 +135,12 @@ namespace Sass {
129135
{
130136
c_functions.push_back(function);
131137
}
138+
void Context::add_c_header(Sass_Importer_Entry header)
139+
{
140+
c_headers.push_back(header);
141+
// need to sort the array afterwards (no big deal)
142+
sort (c_headers.begin(), c_headers.end(), sort_importers);
143+
}
132144
void Context::add_c_importer(Sass_Importer_Entry importer)
133145
{
134146
c_importers.push_back(importer);
@@ -394,7 +406,9 @@ namespace Sass {
394406
std::vector<std::string> Context::get_included_files(size_t skip)
395407
{
396408
vector<string> includes = included_files;
409+
if (includes.size() == 0) return includes;
397410
std::sort( includes.begin() + skip, includes.end() );
411+
includes.erase( includes.begin(), includes.begin() + skip );
398412
includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() );
399413
// the skip solution seems more robust, as we may have real files named stdin
400414
// includes.erase( std::remove( includes.begin(), includes.end(), "stdin" ), includes.end() );

context.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace Sass {
3232

3333
class Context {
3434
public:
35+
size_t head_imports;
3536
Memory_Manager<AST_Node> mem;
3637

3738
struct Sass_Options* c_options;
@@ -54,11 +55,13 @@ namespace Sass {
5455
// SourceMap source_map;
5556
Output emitter;
5657

57-
vector<Sass_Function_Entry> c_functions;
58+
vector<Sass_Importer_Entry> c_headers;
5859
vector<Sass_Importer_Entry> c_importers;
60+
vector<Sass_Function_Entry> c_functions;
5961

60-
void add_c_function(Sass_Function_Entry function);
62+
void add_c_header(Sass_Importer_Entry header);
6163
void add_c_importer(Sass_Importer_Entry importer);
64+
void add_c_function(Sass_Function_Entry function);
6265

6366
string indent; // String to be used for indentation
6467
string linefeed; // String to be used for line feeds

parser.cpp

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ namespace Sass {
6363
block_stack.push_back(root);
6464
root->is_root(true);
6565
read_bom();
66+
67+
if (ctx.queue.size() == 1) {
68+
Import* pre = new (ctx.mem) Import(pstate);
69+
string load_path(ctx.queue[0].load_path);
70+
do_import(load_path, pre, ctx.c_headers, false);
71+
ctx.head_imports = ctx.queue.size() - 1;
72+
if (!pre->urls().empty()) (*root) << pre;
73+
if (!pre->files().empty()) {
74+
for (size_t i = 0, S = pre->files().size(); i < S; ++i) {
75+
(*root) << new (ctx.mem) Import_Stub(pstate, pre->files()[i]);
76+
}
77+
}
78+
}
79+
6680
lex< optional_spaces >();
6781
Selector_Lookahead lookahead_result;
6882
while (position < end) {
@@ -192,6 +206,52 @@ namespace Sass {
192206

193207
}
194208

209+
bool Parser::do_import(const string& import_path, Import* imp, vector<Sass_Importer_Entry> importers, bool only_one)
210+
{
211+
bool has_import = false;
212+
string load_path = unquote(import_path);
213+
for (auto importer : importers) {
214+
// int priority = sass_importer_get_priority(importer);
215+
Sass_Importer_Fn fn = sass_importer_get_function(importer);
216+
if (Sass_Import_List includes =
217+
fn(load_path.c_str(), importer, ctx.c_compiler)
218+
) {
219+
Sass_Import_List list = includes;
220+
while (*includes) {
221+
Sass_Import_Entry include = *includes;
222+
const char *file = sass_import_get_path(include);
223+
char* source = sass_import_take_source(include);
224+
size_t line = sass_import_get_error_line(include);
225+
size_t column = sass_import_get_error_column(include);
226+
const char* message = sass_import_get_error_message(include);
227+
if (message) {
228+
if (line == string::npos && column == string::npos) error(message, pstate);
229+
else error(message, ParserState(message, source, Position(line, column)));
230+
} else if (source) {
231+
if (file) {
232+
ctx.add_source(file, load_path, source);
233+
imp->files().push_back(file);
234+
} else {
235+
ctx.add_source(load_path, load_path, source);
236+
imp->files().push_back(load_path);
237+
}
238+
} else if(file) {
239+
import_single_file(imp, file);
240+
}
241+
++includes;
242+
}
243+
// deallocate returned memory
244+
sass_delete_import_list(list);
245+
// set success flag
246+
has_import = true;
247+
// break import chain
248+
if (only_one) return true;
249+
}
250+
}
251+
// return result
252+
return has_import;
253+
}
254+
195255
Import* Parser::parse_import()
196256
{
197257
lex< kwd_import >();
@@ -200,52 +260,11 @@ namespace Sass {
200260
do {
201261
while (lex< block_comment >());
202262
if (lex< quoted_string >()) {
203-
string import_path(lexed);
204-
bool has_custom_import = false;
205-
string load_path = unquote(import_path);
206-
for (auto importer : ctx.c_importers) {
207-
if (has_custom_import) break;
208-
Sass_Importer_Fn fn = sass_importer_get_function(importer);
209-
// int priority = sass_importer_get_priority(importer);
210-
if (Sass_Import_List includes =
211-
fn(load_path.c_str(), importer, ctx.c_compiler)
212-
) {
213-
Sass_Import_List list = includes;
214-
while (*includes) {
215-
Sass_Import_Entry include = *includes;
216-
const char *file = sass_import_get_path(include);
217-
char* source = sass_import_take_source(include);
218-
size_t line = sass_import_get_error_line(include);
219-
size_t column = sass_import_get_error_column(include);
220-
const char* message = sass_import_get_error_message(include);
221-
if (message) {
222-
if (line == string::npos && column == string::npos) error(message, pstate);
223-
else error(message, ParserState(message, source, Position(line, column)));
224-
} else if (source) {
225-
if (file) {
226-
ctx.add_source(file, load_path, source);
227-
imp->files().push_back(file);
228-
} else {
229-
ctx.add_source(load_path, load_path, source);
230-
imp->files().push_back(load_path);
231-
}
232-
} else if(file) {
233-
import_single_file(imp, file);
234-
}
235-
++includes;
236-
}
237-
// deallocate returned memory
238-
sass_delete_import_list(list);
239-
// break import chain
240-
has_custom_import = true;
241-
}
242-
}
243-
244-
if (!has_custom_import) {
263+
if (!do_import(lexed, imp, ctx.c_importers, true))
264+
{
245265
// push single file import
246-
import_single_file(imp, import_path);
266+
import_single_file(imp, lexed);
247267
}
248-
249268
}
250269
else if (peek< uri_prefix >()) {
251270
imp->urls().push_back(parse_value());

parser.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace Sass {
3030
class AST_Node;
3131

3232
enum Syntactic_Context { nothing, mixin_def, function_def };
33+
bool do_import(const string& import_path, Import* imp, vector<Sass_Importer_Entry> importers, bool only_one = true);
3334

3435
Context& ctx;
3536
vector<Block*> block_stack;

plugins.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ namespace Sass {
6767
Sass_Importer_List imps = plugin_load_importers();
6868
while (imps && *imps) { importers.push_back(*imps); ++ imps; }
6969
}
70+
// try to get import address for "libsass_load_headers"
71+
if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_headers, "libsass_load_headers"))
72+
{
73+
Sass_Importer_List imps = plugin_load_headers();
74+
while (imps && *imps) { headers.push_back(*imps); ++ imps; }
75+
}
7076
// success
7177
return true;
7278
}

plugins.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ namespace Sass {
4242
size_t load_plugins(const string& path);
4343

4444
public: // public accessors
45+
const vector<Sass_Importer_Entry> get_headers(void) { return headers; };
4546
const vector<Sass_Importer_Entry> get_importers(void) { return importers; };
4647
const vector<Sass_Function_Entry> get_functions(void) { return functions; };
4748

4849
private: // private vars
50+
vector<Sass_Importer_Entry> headers;
4951
vector<Sass_Importer_Entry> importers;
5052
vector<Sass_Function_Entry> functions;
5153

sass_context.cpp

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ extern "C" {
102102
// Custom functions that can be called from sccs code
103103
Sass_Function_List c_functions;
104104

105-
// Callback to overload imports
105+
// List of custom importers
106106
Sass_Importer_List c_importers;
107107

108+
// List of custom headers
109+
Sass_Importer_List c_headers;
110+
108111
};
109112

110113
// base for all contexts
@@ -185,19 +188,19 @@ extern "C" {
185188
return str == NULL ? "" : str;
186189
}
187190

188-
static void copy_strings(const std::vector<std::string>& strings, char*** array, int skip = 0) throw() {
191+
static void copy_strings(const std::vector<std::string>& strings, char*** array) throw() {
189192
int num = static_cast<int>(strings.size());
190193
char** arr = (char**) malloc(sizeof(char*) * (num + 1));
191194
if (arr == 0) throw(bad_alloc());
192195

193-
for(int i = skip; i < num; i++) {
194-
arr[i-skip] = (char*) malloc(sizeof(char) * (strings[i].size() + 1));
195-
if (arr[i-skip] == 0) throw(bad_alloc());
196-
std::copy(strings[i].begin(), strings[i].end(), arr[i-skip]);
197-
arr[i-skip][strings[i].size()] = '\0';
196+
for(int i = 0; i < num; i++) {
197+
arr[i] = (char*) malloc(sizeof(char) * (strings[i].size() + 1));
198+
if (arr[i] == 0) throw(bad_alloc());
199+
std::copy(strings[i].begin(), strings[i].end(), arr[i]);
200+
arr[i][strings[i].size()] = '\0';
198201
}
199202

200-
arr[num-skip] = 0;
203+
arr[num] = 0;
201204
*array = arr;
202205
}
203206

@@ -420,6 +423,15 @@ extern "C" {
420423
}
421424
}
422425

426+
// register our custom headers
427+
if (c_ctx->c_headers) {
428+
auto this_head_data = c_ctx->c_headers;
429+
while (this_head_data && *this_head_data) {
430+
cpp_ctx->add_c_header(*this_head_data);
431+
++this_head_data;
432+
}
433+
}
434+
423435
// register our custom importers
424436
if (c_ctx->c_importers) {
425437
auto this_imp_data = c_ctx->c_importers;
@@ -475,8 +487,11 @@ extern "C" {
475487
skip = 1; // skip first entry of includes
476488
}
477489

490+
// skip all prefixed files?
491+
skip += cpp_ctx->head_imports;
492+
478493
// copy the included files on to the context (dont forget to free)
479-
if (root) copy_strings(cpp_ctx->get_included_files(skip), &c_ctx->included_files, skip);
494+
if (root) copy_strings(cpp_ctx->get_included_files(skip), &c_ctx->included_files);
480495

481496
// return parsed block
482497
return root;
@@ -674,15 +689,23 @@ extern "C" {
674689
if (options == 0) return;
675690
// Deallocate custom functions
676691
if (options->c_functions) {
677-
struct Sass_Function** this_func_data = options->c_functions;
692+
Sass_Function_List this_func_data = options->c_functions;
678693
while (this_func_data && *this_func_data) {
679694
free(*this_func_data);
680695
++this_func_data;
681696
}
682697
}
698+
// Deallocate custom headers
699+
if (options->c_headers) {
700+
Sass_Importer_List this_head_data = options->c_headers;
701+
while (this_head_data && *this_head_data) {
702+
free(*this_head_data);
703+
++this_head_data;
704+
}
705+
}
683706
// Deallocate custom importers
684707
if (options->c_importers) {
685-
struct Sass_Importer** this_imp_data = options->c_importers;
708+
Sass_Importer_List this_imp_data = options->c_importers;
686709
while (this_imp_data && *this_imp_data) {
687710
free(*this_imp_data);
688711
++this_imp_data;
@@ -716,9 +739,11 @@ extern "C" {
716739
free(options->c_functions);
717740
// Free custom importers
718741
free(options->c_importers);
742+
free(options->c_headers);
719743
// Reset our pointers
720744
options->c_functions = 0;
721745
options->c_importers = 0;
746+
options->c_headers = 0;
722747
options->plugin_paths = 0;
723748
options->include_paths = 0;
724749
}
@@ -800,6 +825,7 @@ extern "C" {
800825
IMPLEMENT_SASS_OPTION_ACCESSOR(bool, is_indented_syntax_src);
801826
IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Function_List, c_functions);
802827
IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_importers);
828+
IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_headers);
803829
IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, indent);
804830
IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, linefeed);
805831
IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path);

sass_context.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ ADDAPI const char* ADDCALL sass_option_get_plugin_path (struct Sass_Options* opt
8181
ADDAPI const char* ADDCALL sass_option_get_include_path (struct Sass_Options* options);
8282
ADDAPI const char* ADDCALL sass_option_get_source_map_file (struct Sass_Options* options);
8383
ADDAPI const char* ADDCALL sass_option_get_source_map_root (struct Sass_Options* options);
84-
ADDAPI Sass_Function_List ADDCALL sass_option_get_c_functions (struct Sass_Options* options);
84+
ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_headers (struct Sass_Options* options);
8585
ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_importers (struct Sass_Options* options);
86+
ADDAPI Sass_Function_List ADDCALL sass_option_get_c_functions (struct Sass_Options* options);
8687

8788
// Setters for Context_Option values
8889
ADDAPI void ADDCALL sass_option_set_precision (struct Sass_Options* options, int precision);
@@ -100,8 +101,9 @@ ADDAPI void ADDCALL sass_option_set_plugin_path (struct Sass_Options* options, c
100101
ADDAPI void ADDCALL sass_option_set_include_path (struct Sass_Options* options, const char* include_path);
101102
ADDAPI void ADDCALL sass_option_set_source_map_file (struct Sass_Options* options, const char* source_map_file);
102103
ADDAPI void ADDCALL sass_option_set_source_map_root (struct Sass_Options* options, const char* source_map_root);
103-
ADDAPI void ADDCALL sass_option_set_c_functions (struct Sass_Options* options, Sass_Function_List c_functions);
104+
ADDAPI void ADDCALL sass_option_set_c_headers (struct Sass_Options* options, Sass_Importer_List c_headers);
104105
ADDAPI void ADDCALL sass_option_set_c_importers (struct Sass_Options* options, Sass_Importer_List c_importers);
106+
ADDAPI void ADDCALL sass_option_set_c_functions (struct Sass_Options* options, Sass_Function_List c_functions);
105107

106108

107109
// Getters for Sass_Context values

sass_functions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ typedef Sass_Import_List (*Sass_Importer_Fn)
3131
typedef struct Sass_Function (*Sass_Function_Entry);
3232
typedef struct Sass_Function* (*Sass_Function_List);
3333
// Typedef defining function signature and return type
34-
typedef union Sass_Value*(*Sass_Function_Fn)
34+
typedef union Sass_Value* (*Sass_Function_Fn)
3535
(const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Options* options);
3636

3737

0 commit comments

Comments
 (0)