source: frontend/node_modules/jake/lib/rule.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 7.7 KB
Line 
1let path = require('path');
2let fs = require('fs');
3let Task = require('./task/task').Task;
4
5// Split a task to two parts, name space and task name.
6// For example, given 'foo:bin/a%.c', return an object with
7// - 'ns' : foo
8// - 'name' : bin/a%.c
9function splitNs(task) {
10 let parts = task.split(':');
11 let name = parts.pop();
12 let ns = resolveNs(parts);
13 return {
14 'name' : name,
15 'ns' : ns
16 };
17}
18
19// Return the namespace based on an array of names.
20// For example, given ['foo', 'baz' ], return the namespace
21//
22// default -> foo -> baz
23//
24// where default is the global root namespace
25// and -> means child namespace.
26function resolveNs(parts) {
27 let ns = jake.defaultNamespace;
28 for(let i = 0, l = parts.length; ns && i < l; i++) {
29 ns = ns.childNamespaces[parts[i]];
30 }
31 return ns;
32}
33
34// Given a pattern p, say 'foo:bin/a%.c'
35// Return an object with
36// - 'ns' : foo
37// - 'dir' : bin
38// - 'prefix' : a
39// - 'suffix' : .c
40function resolve(p) {
41 let task = splitNs(p);
42 let name = task.name;
43 let ns = task.ns;
44 let split = path.basename(name).split('%');
45 return {
46 ns: ns,
47 dir: path.dirname(name),
48 prefix: split[0],
49 suffix: split[1]
50 };
51}
52
53// Test whether string a is a suffix of string b
54function stringEndWith(a, b) {
55 let l;
56 return (l = b.lastIndexOf(a)) == -1 ? false : l + a.length == b.length;
57}
58
59// Replace the suffix a of the string s with b.
60// Note that, it is assumed a is a suffix of s.
61function stringReplaceSuffix(s, a, b) {
62 return s.slice(0, s.lastIndexOf(a)) + b;
63}
64
65class Rule {
66 constructor(opts) {
67 this.pattern = opts.pattern;
68 this.source = opts.source;
69 this.prereqs = opts.prereqs;
70 this.action = opts.action;
71 this.opts = opts.opts;
72 this.desc = opts.desc;
73 this.ns = opts.ns;
74 }
75
76 // Create a file task based on this rule for the specified
77 // task-name
78 // ======
79 // FIXME: Right now this just throws away any passed-in args
80 // for the synthsized task (taskArgs param)
81 // ======
82 createTask(fullName, level) {
83 let self = this;
84 let pattern;
85 let source;
86 let action;
87 let opts;
88 let prereqs;
89 let valid;
90 let src;
91 let tNs;
92 let createdTask;
93 let name = Task.getBaseTaskName(fullName);
94 let nsPath = Task.getBaseNamespacePath(fullName);
95 let ns = this.ns.resolveNamespace(nsPath);
96
97 pattern = this.pattern;
98 source = this.source;
99
100 if (typeof source == 'string') {
101 src = Rule.getSource(name, pattern, source);
102 }
103 else {
104 src = source(name);
105 }
106
107 // TODO: Write a utility function that appends a
108 // taskname to a namespace path
109 src = nsPath.split(':').filter(function (item) {
110 return !!item;
111 }).concat(src).join(':');
112
113 // Generate the prerequisite for the matching task.
114 // It is the original prerequisites plus the prerequisite
115 // representing source file, i.e.,
116 //
117 // rule( '%.o', '%.c', ['some.h'] ...
118 //
119 // If the objective is main.o, then new task should be
120 //
121 // file( 'main.o', ['main.c', 'some.h' ] ...
122 prereqs = this.prereqs.slice(); // Get a copy to work with
123 prereqs.unshift(src);
124
125 // Prereq should be:
126 // 1. an existing task
127 // 2. an existing file on disk
128 // 3. a valid rule (i.e., not at too deep a level)
129 valid = prereqs.some(function (p) {
130 let ns = self.ns;
131 return ns.resolveTask(p) ||
132 fs.existsSync(Task.getBaseTaskName(p)) ||
133 jake.attemptRule(p, ns, level + 1);
134 });
135
136 // If any of the prereqs aren't valid, the rule isn't valid
137 if (!valid) {
138 return null;
139 }
140 // Otherwise, hunky-dory, finish creating the task for the rule
141 else {
142 // Create the action for the task
143 action = function () {
144 let task = this;
145 self.action.apply(task);
146 };
147
148 opts = this.opts;
149
150 // Insert the file task into Jake
151 //
152 // Since createTask function stores the task as a child task
153 // of currentNamespace. Here we temporariliy switch the namespace.
154 // FIXME: Should allow optional ns passed in instead of this hack
155 tNs = jake.currentNamespace;
156 jake.currentNamespace = ns;
157 createdTask = jake.createTask('file', name, prereqs, action, opts);
158 createdTask.source = src.split(':').pop();
159 jake.currentNamespace = tNs;
160
161 return createdTask;
162 }
163 }
164
165 match(name) {
166 return Rule.match(this.pattern, name);
167 }
168
169 // Test wether the a prerequisite matchs the pattern.
170 // The arg 'pattern' does not have namespace as prefix.
171 // For example, the following tests are true
172 //
173 // pattern | name
174 // bin/%.o | bin/main.o
175 // bin/%.o | foo:bin/main.o
176 //
177 // The following tests are false (trivally)
178 //
179 // pattern | name
180 // bin/%.o | foobin/main.o
181 // bin/%.o | bin/main.oo
182 static match(pattern, name) {
183 let p;
184 let task;
185 let obj;
186 let filename;
187
188 if (pattern instanceof RegExp) {
189 return pattern.test(name);
190 }
191 else if (pattern.indexOf('%') == -1) {
192 // No Pattern. No Folder. No Namespace.
193 // A Simple Suffix Rule. Just test suffix
194 return stringEndWith(pattern, name);
195 }
196 else {
197 // Resolve the dir, prefix and suffix of pattern
198 p = resolve(pattern);
199
200 // Resolve the namespace and task-name
201 task = splitNs(name);
202 name = task.name;
203
204 // Set the objective as the task-name
205 obj = name;
206
207 // Namespace is already matched.
208
209 // Check dir
210 if (path.dirname(obj) != p.dir) {
211 return false;
212 }
213
214 filename = path.basename(obj);
215
216 // Check file name length
217 if ((p.prefix.length + p.suffix.length + 1) > filename.length) {
218 // Length does not match.
219 return false;
220 }
221
222 // Check prefix
223 if (filename.indexOf(p.prefix) !== 0) {
224 return false;
225 }
226
227 // Check suffix
228 if (!stringEndWith(p.suffix, filename)) {
229 return false;
230 }
231
232 // OK. Find a match.
233 return true;
234 }
235 }
236
237 // Generate the source based on
238 // - name name for the synthesized task
239 // - pattern pattern for the objective
240 // - source pattern for the source
241 //
242 // Return the source with properties
243 // - dep the prerequisite of source
244 // (with the namespace)
245 //
246 // - file the file name of source
247 // (without the namespace)
248 //
249 // For example, given
250 //
251 // - name foo:bin/main.o
252 // - pattern bin/%.o
253 // - source src/%.c
254 //
255 // return 'foo:src/main.c',
256 //
257 static getSource(name, pattern, source) {
258 let dep;
259 let pat;
260 let match;
261 let file;
262 let src;
263
264 // Regex pattern -- use to look up the extension
265 if (pattern instanceof RegExp) {
266 match = pattern.exec(name);
267 if (match) {
268 if (typeof source == 'function') {
269 src = source(name);
270 }
271 else {
272 src = stringReplaceSuffix(name, match[0], source);
273 }
274 }
275 }
276 // Assume string
277 else {
278 // Simple string suffix replacement
279 if (pattern.indexOf('%') == -1) {
280 if (typeof source == 'function') {
281 src = source(name);
282 }
283 else {
284 src = stringReplaceSuffix(name, pattern, source);
285 }
286 }
287 // Percent-based substitution
288 else {
289 pat = pattern.replace('%', '(.*?)');
290 pat = new RegExp(pat);
291 match = pat.exec(name);
292 if (match) {
293 if (typeof source == 'function') {
294 src = source(name);
295 }
296 else {
297 file = match[1];
298 file = source.replace('%', file);
299 dep = match[0];
300 src = name.replace(dep, file);
301 }
302 }
303 }
304 }
305
306 return src;
307 }
308}
309
310
311exports.Rule = Rule;
Note: See TracBrowser for help on using the repository browser.