@marrs/sxml 0.3.0

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.
@@ -0,0 +1,186 @@
1
+ // Pre-processors are prefixed with `pre_` by convention.
2
+
3
+ import { resolve, dirname, basename, extname } from 'path';
4
+ import { lstat, readdir } from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ function reduce_attr(acc, arr) {
8
+ var ws = (arr[0] || ' ');
9
+ if (arr[2]) {
10
+ return acc + ws + arr[1] + '=' + arr[2];
11
+ } else if (arr[1]) {
12
+ return acc + ws + arr[1];
13
+ }
14
+ return acc + (arr[0] || '');
15
+ }
16
+
17
+
18
+ const HTML_ENTITIES = {
19
+ rsquo: 0x2019, lsquo: 0x2018, rdquo: 0x201D, ldquo: 0x201C,
20
+ mdash: 0x2014, ndash: 0x2013, hellip: 0x2026, nbsp: 0x00A0,
21
+ amp: 0x0026, lt: 0x003C, gt: 0x003E, quot: 0x0022, apos: 0x0027,
22
+ };
23
+
24
+ export function pre_html_chars(event, stackFrame, content, state, data) {
25
+ if (event === 'content') {
26
+ content = content.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
27
+ }
28
+ return [event, stackFrame, content, state, data];
29
+ }
30
+
31
+ export function pre_hex_chars(event, stackFrame, content, state, data) {
32
+ if (event === 'content') {
33
+ content = content.replace(/&/g, '&#x26;').replace(/</g, '&#x3C;').replace(/>/g, '&#x3E;');
34
+ }
35
+ return [event, stackFrame, content, state, data];
36
+ }
37
+
38
+ export function unqualified_xml(event, stackFrame, content, state, data) {
39
+ if (stackFrame) {
40
+ var ast = stackFrame.ast;
41
+ }
42
+ // Preserve whitespace for self-closing tags.
43
+ switch (event) {
44
+ case 'write-tag:no-children':
45
+ var attr = ast.slice(2);
46
+ var closingWs = attr.length && !attr[0][0]? ' ' : '';
47
+
48
+ return ast[0] +
49
+ '<' +
50
+ ast[1] +
51
+ attr.reduce(reduce_attr, '') +
52
+ closingWs +
53
+ '/>';
54
+
55
+ case 'open-tag':
56
+ return ast[0] +
57
+ '<' +
58
+ ast[1] +
59
+ ast.slice(2).reduce(reduce_attr, '') +
60
+ '>';
61
+ case 'begin-escape-bracket':
62
+ return '(';
63
+ case 'close-tag':
64
+ return ["</", ">"].join(ast[1]);
65
+ case 'end-escape-bracket':
66
+ return ')';
67
+ case 'content':
68
+ return content;
69
+ case 'entity':
70
+ return '&' + content + ';';
71
+ }
72
+ }
73
+
74
+ export function pre_html_entity(event, stackFrame, content, state, data) {
75
+ if ((event === 'open-tag' || event === 'write-tag:no-children') && stackFrame) {
76
+ var ast = stackFrame.ast;
77
+ var newAst = [ast[0], ast[1]];
78
+ for (var i = 2; i < ast.length; i++) {
79
+ var attr = ast[i];
80
+ if (attr[2] && attr[2][0] === '"') {
81
+ attr = [attr[0], attr[1], '"' + attr[2].slice(1, -1).replace(/(?<!\\)"/g, '&quot;') + '"'];
82
+ }
83
+ newAst.push(attr);
84
+ }
85
+ stackFrame = Object.assign({}, stackFrame, { ast: newAst });
86
+ }
87
+ return [event, stackFrame, content, state, data];
88
+ }
89
+
90
+ export function pre_hex_entity(event, stackFrame, content, state, data) {
91
+ if (event === 'entity') {
92
+ var code = HTML_ENTITIES[content];
93
+ var hex = code !== undefined ? '&#x' + code.toString(16) + ';' : '&' + content + ';';
94
+ return ['content', stackFrame, hex, state, data];
95
+ }
96
+ if (event === 'content') {
97
+ content = content.replace(/&([a-z]+);/g, (_, name) =>
98
+ HTML_ENTITIES[name] ? String.fromCodePoint(HTML_ENTITIES[name]) : `&${name};`);
99
+ }
100
+ if ((event === 'open-tag' || event === 'write-tag:no-children') && stackFrame) {
101
+ var ast = stackFrame.ast;
102
+ var newAst = [ast[0], ast[1]];
103
+ for (var i = 2; i < ast.length; i++) {
104
+ var attr = ast[i];
105
+ if (attr[2] && attr[2][0] === '"') {
106
+ attr = [attr[0], attr[1], '"' + attr[2].slice(1, -1).replace(/(?<!\\)"/g, '&#x22;') + '"'];
107
+ }
108
+ newAst.push(attr);
109
+ }
110
+ stackFrame = Object.assign({}, stackFrame, { ast: newAst });
111
+ }
112
+ return [event, stackFrame, content, state, data];
113
+ }
114
+
115
+ export function pre_qualified_ns(event, stackFrame, content, state, data) {
116
+ if (stackFrame && typeof stackFrame.ast[1] === 'string') {
117
+ var ast = stackFrame.ast;
118
+ var operator = ast[1] || '';
119
+ var colonIdx = operator.indexOf(':');
120
+ var ns = colonIdx > -1 ? operator.substring(0, colonIdx) : '';
121
+ var localName = colonIdx > -1 ? operator.substring(colonIdx + 1) : operator;
122
+ var tagName = ns ? operator : (stackFrame.namespace ? stackFrame.namespace + ':' + localName : localName);
123
+
124
+ if (event === 'open-tag' && ns) stackFrame.namespace = ns;
125
+
126
+ stackFrame = Object.assign({}, stackFrame, { ast: [ast[0], tagName, ...ast.slice(2)] });
127
+ }
128
+ return [event, stackFrame, content, state, data];
129
+ }
130
+
131
+ function attributes(arr) {
132
+ return arr.reduce(function(acc, val) {
133
+ if (val[1]) {
134
+ acc[val[1]] = val[2];
135
+ }
136
+ return acc;
137
+ }, {});
138
+ }
139
+
140
+ export function await_extensions() {
141
+ var dirPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../ext');
142
+ return new Promise(function(resolve, reject) {
143
+ lstat(dirPath, function(err, stat) {
144
+ if (err) { reject(err); }
145
+
146
+ if (stat.isDirectory()) {
147
+ readdir(dirPath, function(err, files) {
148
+ if (err) { reject(err); }
149
+ var extensions = {};
150
+ Promise.all(files.map(function(filename) {
151
+ return import(dirPath + '/' + filename).then(function(module) {
152
+ var extName = basename(filename, extname(filename));
153
+ extensions[extName] = function dispatch(event, stackFrame, content, processorState, data) {
154
+ if (!data) {
155
+ console.trace()
156
+ }
157
+ var operatorName = stackFrame.operator.split(':')[1];
158
+ if ('function' === typeof(module.default[operatorName])) {
159
+ module.default[operatorName](
160
+ event,
161
+ attributes(stackFrame.ast.slice(2)),
162
+ content,
163
+ processorState, // processorState is mutable by the ext
164
+ {
165
+ ast: stackFrame.ast.slice(),
166
+ cursor: data.cursor,
167
+ lineCount: data.lineCount,
168
+ source: data.source
169
+ }
170
+ );
171
+ } else {
172
+ console.error("Function not found for " + extName + ':' + operatorName);
173
+ }
174
+ }
175
+ });
176
+ })).then(function() {;
177
+ resolve({extensions: extensions});
178
+ });
179
+ });
180
+ } else {
181
+ reject(new Error("Not a directory: " + dirPath));
182
+ }
183
+ });
184
+ });
185
+ }
186
+
package/src/util.js ADDED
@@ -0,0 +1,58 @@
1
+ export function last(arr) {
2
+ return arr[arr.length -1];
3
+ }
4
+
5
+ export function last_but(num, arr, defaultReturnVal) {
6
+ return arr[arr.length - (num + 1)] || defaultReturnVal || null;
7
+ }
8
+
9
+ export function count_newlines(str) {
10
+ return (str.match(/\n/g) || []).length;
11
+ }
12
+
13
+ export function unquote(str) {
14
+ str = str.trim();
15
+ var ch1 = str[0];
16
+ if (('"' === ch1 || "'" === ch1) && ch1 === last(str)) {
17
+ return str.substring(1, str.length -1);
18
+ }
19
+ return str;
20
+ }
21
+
22
+ export const Interface = {
23
+ traits: {
24
+ new: function(data) {
25
+ data = data || {};
26
+ if (this.assertions) {
27
+ for (var x in this.assertions) {
28
+ if (data.hasOwnProperty(x)) {
29
+ if(!this.assertions[x](data[x])) {
30
+ throw new Error({
31
+ msg: "Object does not conform to interface",
32
+ object: data,
33
+ interface: this.assertions
34
+ });
35
+ }
36
+ }
37
+ }
38
+ }
39
+ return Object.assign({}, this.defaults || {}, data);
40
+ }
41
+ },
42
+
43
+ isDefined: function(prop) {
44
+ return undefined !== prop;
45
+ },
46
+
47
+ isArray: function(prop) {
48
+ return Array.isArray(prop);
49
+ },
50
+
51
+ // Creates an interface that can be used to instantiate objects
52
+ // using its `new` method. The interface will ensure tha the object's
53
+ // properties conform to the spec provided by the assertions.
54
+ create: function(data) {
55
+ return Object.assign(Object.create(Interface.traits), data);
56
+ }
57
+ };
58
+