@qooxdoo/framework 7.3.1 → 7.3.3

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.
package/Manifest.json CHANGED
@@ -79,7 +79,7 @@
79
79
  "email": "dietrich.streifert@visionet.de"
80
80
  }
81
81
  ],
82
- "version": "7.3.1",
82
+ "version": "7.3.3",
83
83
  "sourceViewUri": "https://github.com/qooxdoo/qooxdoo/blob/%{qxGitBranch}/framework/source/class/%{classFilePath}#L%{lineNumber}"
84
84
  },
85
85
  "provides": {
package/addGitHook ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ const utils = require("./bin/tools/utils");
3
+ const fs = require("fs");
4
+ (async function() {
5
+ if (fs.existsSync(".github/hooks")) {
6
+ await utils.runCommand(".", "git", "config", "core.hooksPath", ".github/hooks");
7
+ }
8
+ })();
9
+
@@ -0,0 +1,561 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const async = require("async");
4
+ const child_process = require("child_process");
5
+ //var fsPromises = require("fs").promises;
6
+ // node 8 compatibility
7
+ const {promisify} = require('util');
8
+ const stat = promisify(fs.stat);
9
+
10
+ const fsPromises = {
11
+ readFile: promisify(fs.readFile),
12
+ writeFile: promisify(fs.writeFile),
13
+ unlink: promisify(fs.unlink),
14
+ mkdir: promisify(fs.mkdir),
15
+ readdir: promisify(fs.readdir)
16
+ };
17
+
18
+ /**
19
+ * Return the path to the compiler executable, unless the "QX_JS" OS environment
20
+ * variable is set, in which case the content of this variable is returned.
21
+ *
22
+ * @param {String} buildVersion? The build version, defaults to "build"
23
+ * @return {String}
24
+ */
25
+ function getCompiler(buildVersion="build") {
26
+ let qxJs = process.env.QX_JS;
27
+ if (!qxJs) {
28
+ qxJs = path.join(__dirname, "..", buildVersion, "qx");
29
+ }
30
+ return qxJs;
31
+ }
32
+
33
+ async function runCompiler(dir, ...cmd) {
34
+ let result = await runCommand(dir, getCompiler(), "compile", "--machine-readable", ...cmd);
35
+ result.messages = [];
36
+ result.output.split("\n").forEach(line => {
37
+ let m = line.match(/^\#\#([^:]+):\[(.*)\]$/);
38
+ if (m) {
39
+ let args = m[2].match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g);
40
+ if (args) {
41
+ args = args.map(arg => {
42
+ if (arg.length && arg[0] == "\"" && arg[arg.length - 1] == "\"")
43
+ return arg.substring(1, arg.length - 1);
44
+ return arg;
45
+ });
46
+ } else {
47
+ args = [];
48
+ }
49
+ result.messages.push({
50
+ id: m[1],
51
+ args: args
52
+ });
53
+ }
54
+ });
55
+ return result;
56
+ }
57
+
58
+ async function runCommand(dir, ...args) {
59
+ return new Promise((resolve, reject) => {
60
+ let cmd = args.shift();
61
+ let proc = child_process.spawn(cmd, args, {
62
+ cwd: dir,
63
+ shell: true
64
+ });
65
+ let result = {
66
+ exitCode: null,
67
+ output: "",
68
+ error: "",
69
+ messages: null
70
+ };
71
+ proc.stdout.on('data', (data) => {
72
+ data = data.toString().trim();
73
+ console.log(data);
74
+ result.output += data;
75
+ });
76
+ proc.stderr.on('data', (data) => {
77
+ data = data.toString().trim();
78
+ console.error(data);
79
+ result.error += data;
80
+ });
81
+
82
+ proc.on('close', code => {
83
+ result.exitCode = code;
84
+ resolve(result);
85
+ });
86
+ proc.on('error', err => {
87
+ reject(err);
88
+ });
89
+ });
90
+ }
91
+
92
+ async function deleteRecursive(name) {
93
+ return new Promise((resolve, reject) => {
94
+ fs.access(name, err => {
95
+ if (err) {
96
+ return resolve();
97
+ }
98
+ deleteRecursiveImpl(name, err => {
99
+ if (err) {
100
+ reject(err);
101
+ } else {
102
+ resolve(err);
103
+ }
104
+ });
105
+ return null;
106
+ });
107
+
108
+ function deleteRecursiveImpl(name, cb) {
109
+ fs.stat(name, function (err, stat) {
110
+ if (err) {
111
+ return cb && cb(err);
112
+ }
113
+
114
+ if (stat.isDirectory()) {
115
+ fs.readdir(name, function (err, files) {
116
+ if (err) {
117
+ return cb && cb(err);
118
+ }
119
+ async.each(files,
120
+ function (file, cb) {
121
+ deleteRecursiveImpl(name + "/" + file, cb);
122
+ },
123
+ function (err) {
124
+ if (err) {
125
+ return cb && cb(err);
126
+ }
127
+ fs.rmdir(name, cb);
128
+ return null;
129
+ }
130
+ );
131
+ return null;
132
+ });
133
+ } else {
134
+ fs.unlink(name, cb);
135
+ }
136
+ return null;
137
+ });
138
+ }
139
+ });
140
+ }
141
+
142
+ async function safeDelete(filename) {
143
+ try {
144
+ await fsPromises.unlink(filename);
145
+ } catch(ex) {
146
+ if (ex.code == "ENOENT")
147
+ return;
148
+ throw ex;
149
+ }
150
+ }
151
+
152
+ function defaultOptions() {
153
+ return {
154
+ clean: true,
155
+ version: null,
156
+ target: "build",
157
+ incVersion: false
158
+ }
159
+ }
160
+
161
+
162
+ async function bootstrapCompiler(options) {
163
+ if (!options)
164
+ options = defaultOptions();
165
+ let result;
166
+
167
+ if (options.clean) {
168
+ console.log("Deleting previous bootstrap compiler");
169
+ await deleteRecursive("bootstrap");
170
+ }
171
+
172
+ // Use the compiler in node_modules to compile a temporary version
173
+ console.log("Creating temporary compiler with known-good one");
174
+ result = await runCommand("known-good", "node", "../bin/known-good/qx", "compile", "--target=" + options.target);
175
+ if (result.exitCode) {
176
+ process.exit(result.exitCode);
177
+ }
178
+
179
+ // Create a handy `qx` binary for that version
180
+ await fsPromises.writeFile("bootstrap/qx",
181
+ `#!/usr/bin/env node
182
+ const path=require("path");
183
+ require("../source/resource/qx/tool/loadsass.js");
184
+ require(path.join(__dirname, "compiled", "node", "${options.target}", "compiler"));
185
+ `, "utf8");
186
+ fs.chmodSync("bootstrap/qx", "777");
187
+ fs.copyFileSync("bin/build/qx.cmd", "bootstrap/qx.cmd");
188
+
189
+ /*
190
+ * Now use the new ./bootstrap/ compiler to compile itself again; the output goes into the
191
+ * normal `compiled` directory, ready for use.
192
+ *
193
+ * Note that we compile both source and build targets; this is because some of
194
+ * the unit tests have to refer to the compiled code and we want to be sure that
195
+ * it does not matter if they use source or build, just make sure it is up to date
196
+ */
197
+ console.log("Compiling source version");
198
+ result = await runCommand(".", "node", "./bootstrap/qx", "compile", "--clean", "--verbose");
199
+ if (result.exitCode) {
200
+ process.exit(result.exitCode);
201
+ }
202
+
203
+ console.log("Compiling build version");
204
+ result = await runCommand(".", "node", "./bootstrap/qx", "compile", "--target=build", "--clean", "--verbose");
205
+ if (result.exitCode) {
206
+ process.exit(result.exitCode);
207
+ }
208
+
209
+ console.log("Compiler successfully bootstrapped");
210
+ }
211
+
212
+ // this is simply a copy of qx.tool.utils.files.Utils
213
+ // needs to be cleaned up.
214
+ moreUtils = {
215
+ async findAllFiles(dir, fnEach) {
216
+ let filenames;
217
+ try {
218
+ filenames = await fsPromises.readdir(dir);
219
+ } catch (ex) {
220
+ if (ex.code == "ENOENT") {
221
+ return;
222
+ }
223
+ throw ex;
224
+ }
225
+ await qx.Promise.all(filenames.map(async shortName => {
226
+ let filename = path.join(dir, shortName);
227
+ let tmp = await stat(filename);
228
+ if (tmp.isDirectory()) {
229
+ await qx.tool.utils.files.Utils.findAllFiles(filename, fnEach);
230
+ } else {
231
+ await fnEach(filename);
232
+ }
233
+ }));
234
+ },
235
+
236
+ /**
237
+ * Synchronises two files or folders; files are copied from/to but only if their
238
+ * modification time or size has changed.
239
+ * @param from {String} path to copy from
240
+ * @param to {String} path to copy to
241
+ * @param filter {Function?} optional filter method to validate filenames before sync
242
+ * @async
243
+ */
244
+ sync: function(from, to, filter) {
245
+ var t = this;
246
+
247
+ function copy(statFrom, statTo) {
248
+ if (statFrom.isDirectory()) {
249
+ var p;
250
+ if (statTo === null) {
251
+ p = fsPromises.mkdir(to);
252
+ } else {
253
+ p = Promise.resolve();
254
+ }
255
+ return p.then(() => fsPromises.readdir(from)
256
+ .then(files => Promise.all(files.map(file => t.sync(path.join(from, file), path.join(to, file), filter)))));
257
+ } else if (statFrom.isFile()) {
258
+ return Promise.resolve(filter ? filter(from, to) : true)
259
+ .then(result => result && t.copyFile(from, to));
260
+ }
261
+ return undefined;
262
+ }
263
+
264
+ return new Promise((resolve, reject) => {
265
+ var statFrom = null;
266
+ var statTo = null;
267
+
268
+ stat(from)
269
+ .then(tmp => {
270
+ statFrom = tmp;
271
+ return stat(to)
272
+ .then(tmp => statTo = tmp)
273
+ .catch(err => {
274
+ if (err.code !== "ENOENT") {
275
+ throw err;
276
+ }
277
+ });
278
+ })
279
+ .then(() => {
280
+ if (!statTo || statFrom.isDirectory() != statTo.isDirectory()) {
281
+ return t.deleteRecursive(to)
282
+ .then(() => copy(statFrom, statTo));
283
+ } else if (statFrom.isDirectory() || (statFrom.mtime.getTime() > statTo.mtime.getTime() || statFrom.size != statTo.size)) {
284
+ return copy(statFrom, statTo);
285
+ }
286
+ return undefined;
287
+ })
288
+ .then(resolve)
289
+ .catch(reject);
290
+ });
291
+ },
292
+
293
+ /**
294
+ * Copies a file
295
+ * @param from {String} path to copy from
296
+ * @param to {String} path to copy to
297
+ * @async
298
+ */
299
+ copyFile: function(from, to) {
300
+ return new Promise((resolve, reject) => {
301
+ moreUtils.mkParentPath(to, function() {
302
+ var rs = fs.createReadStream(from, { flags: "r", encoding: "binary" });
303
+ var ws = fs.createWriteStream(to, { flags: "w", encoding: "binary" });
304
+ rs.on("end", function() {
305
+ resolve(from, to);
306
+ });
307
+ rs.on("error", reject);
308
+ ws.on("error", reject);
309
+ rs.pipe(ws);
310
+ });
311
+ });
312
+ },
313
+
314
+ /**
315
+ * Returns the stats for a file, or null if the file does not exist
316
+ *
317
+ * @param filename
318
+ * @returns {fs.Stat}
319
+ * @async
320
+ */
321
+ safeStat: function(filename) {
322
+ return new Promise((resolve, reject) => {
323
+ fs.stat(filename, function(err, stats) {
324
+ if (err && err.code != "ENOENT") {
325
+ reject(err);
326
+ } else {
327
+ resolve(err ? null : stats);
328
+ }
329
+ });
330
+ });
331
+ },
332
+
333
+ /**
334
+ * Deletes a file, does nothing if the file does not exist
335
+ *
336
+ * @param filename {String} file to delete
337
+ * @async
338
+ */
339
+ safeUnlink: function(filename) {
340
+ return new Promise((resolve, reject) => {
341
+ fs.unlink(filename, function(err) {
342
+ if (err && err.code != "ENOENT") {
343
+ reject(err);
344
+ } else {
345
+ resolve();
346
+ }
347
+ });
348
+ });
349
+ },
350
+
351
+ /**
352
+ * Renames a file, does nothing if the file does not exist
353
+ *
354
+ * @param from {String} file to rename
355
+ * @param to {String} new filename
356
+ * @async
357
+ */
358
+ safeRename: function(from, to) {
359
+ return new Promise((resolve, reject) => {
360
+ fs.rename(from, to, function(err) {
361
+ if (err && err.code != "ENOENT") {
362
+ reject(err);
363
+ } else {
364
+ resolve();
365
+ }
366
+ });
367
+ });
368
+ },
369
+
370
+ /**
371
+ * Normalises the path and corrects the case of the path to match what is actually on the filing system
372
+ *
373
+ * @param dir {String} the filename to normalise
374
+ * @returns {String} the new path
375
+ * @async
376
+ */
377
+ correctCase: function(dir) {
378
+ var drivePrefix = "";
379
+ if (process.platform === "win32" && dir.match(/^[a-zA-Z]:/)) {
380
+ drivePrefix = dir.substring(0, 2);
381
+ dir = dir.substring(2);
382
+ }
383
+ dir = dir.replace(/\\/g, "/");
384
+ var segs = dir.split("/");
385
+ if (!segs.length) {
386
+ return drivePrefix + dir;
387
+ }
388
+
389
+ var currentDir;
390
+ var index;
391
+ if (segs[0].length) {
392
+ currentDir = "";
393
+ index = 0;
394
+ } else {
395
+ currentDir = "/";
396
+ index = 1;
397
+ }
398
+
399
+ function bumpToNext(nextSeg) {
400
+ index++;
401
+ if (currentDir.length && currentDir !== "/") {
402
+ currentDir += "/";
403
+ }
404
+ currentDir += nextSeg;
405
+ return next();
406
+ }
407
+
408
+ function next() {
409
+ if (index == segs.length) {
410
+ if (process.platform === "win32") {
411
+ currentDir = currentDir.replace(/\//g, "\\");
412
+ }
413
+ return Promise.resolve(drivePrefix + currentDir);
414
+ }
415
+
416
+ let nextSeg = segs[index];
417
+ if (nextSeg == "." || nextSeg == "..") {
418
+ return bumpToNext(nextSeg);
419
+ }
420
+
421
+ return new Promise((resolve, reject) => {
422
+ fs.readdir(currentDir.length == 0 ? "." : drivePrefix + currentDir, { encoding: "utf8" }, (err, files) => {
423
+ if (err) {
424
+ reject(err);
425
+ return;
426
+ }
427
+
428
+ let nextLowerCase = nextSeg.toLowerCase();
429
+ let exact = false;
430
+ let insensitive = null;
431
+ for (let i = 0; i < files.length; i++) {
432
+ if (files[i] === nextSeg) {
433
+ exact = true;
434
+ break;
435
+ }
436
+ if (files[i].toLowerCase() === nextLowerCase) {
437
+ insensitive = files[i];
438
+ }
439
+ }
440
+ if (!exact && insensitive) {
441
+ nextSeg = insensitive;
442
+ }
443
+
444
+ bumpToNext(nextSeg).then(resolve);
445
+ });
446
+ });
447
+ }
448
+
449
+ return new Promise((resolve, reject) => {
450
+ fs.stat(drivePrefix + dir, err => {
451
+ if (err) {
452
+ if (err.code == "ENOENT") {
453
+ resolve(drivePrefix + dir);
454
+ } else {
455
+ reject(err);
456
+ }
457
+ } else {
458
+ next().then(resolve);
459
+ }
460
+ });
461
+ });
462
+ },
463
+
464
+ /**
465
+ * Creates a dir
466
+ * @param dir
467
+ * @param cb
468
+ */
469
+ mkpath: function mkpath(dir, cb) {
470
+ dir = path.normalize(dir);
471
+ var segs = dir.split(path.sep);
472
+ var made = "";
473
+ async.eachSeries(
474
+ segs,
475
+ function (seg, cb) {
476
+ if (made.length || !seg.length) {
477
+ made += "/";
478
+ }
479
+ made += seg;
480
+ fs.exists(made, function (exists) {
481
+ if (!exists) {
482
+ fs.mkdir(made, function (err) {
483
+ if (err && err.code === "EEXIST") {
484
+ err = null;
485
+ }
486
+ cb(err);
487
+ });
488
+ return;
489
+ }
490
+ fs.stat(made, function (err, stat) {
491
+ if (err) {
492
+ cb(err);
493
+ } else if (stat.isDirectory()) {
494
+ cb(null);
495
+ } else {
496
+ cb(new Error("Cannot create " + made + " (in " + dir + ") because it exists and is not a directory", "ENOENT"));
497
+ }
498
+ });
499
+ });
500
+ },
501
+ function (err) {
502
+ cb(err);
503
+ });
504
+ },
505
+
506
+ /**
507
+ * Creates the parent directory of a filename, if it does not already exist
508
+ */
509
+ mkParentPath: function mkParentPath(dir, cb) {
510
+ var segs = dir.split(/[\\\/]/);
511
+ segs.pop();
512
+ if (!segs.length) {
513
+ return cb && cb();
514
+ }
515
+ dir = segs.join(path.sep);
516
+ return this.mkpath(dir, cb);
517
+ },
518
+
519
+ /**
520
+ * Creates the parent directory of a filename, if it does not already exist
521
+ *
522
+ * @param {string} filename the filename to create the parent directory of
523
+ *
524
+ * @return {Promise?} the value
525
+ */
526
+ makeParentDir: function (filename) {
527
+ const mkParentPath = promisify(this.mkParentPath).bind(this);
528
+ return mkParentPath(filename);
529
+ },
530
+
531
+ /**
532
+ * Creates a directory, if it does not exist, including all intermediate paths
533
+ *
534
+ * @param {string} filename the directory to create
535
+ *
536
+ * @return {Promise?} the value
537
+ */
538
+ makeDirs: function (filename) {
539
+ const mkpath = promisify(this.mkpath);
540
+ return mkpath(filename);
541
+ }
542
+ };
543
+
544
+ module.exports = {
545
+ getCompiler,
546
+ runCompiler,
547
+ runCommand,
548
+ defaultOptions,
549
+ bootstrapCompiler,
550
+ deleteRecursive,
551
+ safeDelete,
552
+ fsPromises,
553
+ promisify,
554
+ findAllFiles: moreUtils.findAllFiles,
555
+ sync: moreUtils.sync,
556
+ copyFile: moreUtils.copyFile,
557
+ safeStat: moreUtils.safeStat,
558
+ safeRename: moreUtils.safeRename,
559
+ correctCase: moreUtils.correctCase
560
+ };
561
+