Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.

Commit 293eb55

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 e159403 commit 293eb55

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) {
@@ -193,6 +207,52 @@ namespace Sass {
193207

194208
}
195209

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