This repository has been archived by the owner on Aug 7, 2021. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathxml-namespace-loader.ts
145 lines (120 loc) · 5.76 KB
/
xml-namespace-loader.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { parse, join } from "path";
import { promisify } from "util";
import { loader } from "webpack";
import { parser, QualifiedTag } from "sax";
import { convertSlashesInPath } from "./projectHelpers";
interface NamespaceEntry {
name: string;
path: string
}
const loader: loader.Loader = function (source: string, map) {
const { ignore } = this.query;
let callbackCalled = false;
const callback = this.async();
const callbackWrapper = (error?: Error, content?: string, map?: any) => {
if (!callbackCalled) {
callbackCalled = true;
callback(error, content, map);
}
}
const resolvePromise = promisify(this.resolve);
const promises: Promise<any>[] = [];
const namespaces: NamespaceEntry[] = [];
const handleOpenTag = (namespace: string, elementName: string) => {
const moduleName = `${namespace}/${elementName}`;
if (
namespace &&
!namespace.startsWith("http") &&
!namespaces.some(n => n.name === moduleName) &&
(!ignore || !moduleName.match(ignore))
) {
const localNamespacePath = join(this.rootContext, namespace);
const localModulePath = join(localNamespacePath, elementName);
const pathResolved = (resolvedPath) => {
this.addDependency(resolvedPath);
namespaces.push({ name: namespace, path: resolvedPath });
namespaces.push({ name: moduleName, path: resolvedPath });
const { dir, name } = parse(resolvedPath);
const noExtFilename = join(dir, name);
return Promise.all([
resolvePromise(this.context, `${noExtFilename}.xml`)
.then((xml) => {
this.addDependency(xml);
namespaces.push({ name: `${moduleName}.xml`, path: xml });
})
.catch((err) => { }),
resolvePromise(this.context, `${noExtFilename}.css`)
.then((css) => {
this.addDependency(css);
namespaces.push({ name: `${moduleName}.css`, path: css });
})
.catch((err) => { })
]);
};
promises.push(resolvePromise(this.context, localNamespacePath)
.then(path => pathResolved(path))
.catch(() => {
return resolvePromise(this.context, localModulePath)
.then(path => pathResolved(path))
.catch(() => {
return Promise.all([
resolvePromise(this.context, `${localModulePath}.xml`)
.then((xml) => {
namespaces.push({ name: `${moduleName}.xml`, path: xml });
this.addDependency(xml);
})
.catch(() => {
namespaces.push({ name: namespace, path: namespace });
namespaces.push({ name: moduleName, path: namespace });
}),
resolvePromise(this.context, `${localModulePath}.css`)
.then((css) => {
namespaces.push({ name: `${moduleName}.css`, path: css });
this.addDependency(css);
})
.catch(() => { })
]);
});
})
);
}
}
const saxParser = parser(true, { xmlns: true });
// Register ios and android prefixes as namespaces to avoid "unbound xml namespace" errors
(<any>saxParser).ns["ios"] = "http://schemas.nativescript.org/tns.xsd";
(<any>saxParser).ns["android"] = "http://schemas.nativescript.org/tns.xsd";
(<any>saxParser).ns["desktop"] = "http://schemas.nativescript.org/tns.xsd";
(<any>saxParser).ns["web"] = "http://schemas.nativescript.org/tns.xsd";
saxParser.onopentag = (node: QualifiedTag) => { handleOpenTag(node.uri, node.local); };
saxParser.onerror = (err) => {
// Do only warning about invalid character "&"" for back-compatibility
// as it is common to use it in a binding expression
if (err &&
err.message.indexOf("Invalid character") >= 0 &&
err.message.indexOf("Char: &") >= 0) {
this.emitWarning(err)
} else {
callbackWrapper(err);
}
saxParser.error = null;
};
saxParser.write(source).close();
Promise.all(promises).then(() => {
const distinctNamespaces = new Map<string, string>();
namespaces.forEach(({ name, path }) => distinctNamespaces.set(name, convertSlashesInPath(path)));
const moduleRegisters: string[] = [];
distinctNamespaces.forEach((path, name) => {
moduleRegisters.push(`global.registerModule("${name}", function() { return require("${path}"); });\n`);
});
// escape special whitespace characters
// see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Issue_with_plain_JSON.stringify_for_use_as_JavaScript
const json = JSON.stringify(source)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
const wrapped = `${moduleRegisters.join("")}\nmodule.exports = ${json}`;
callbackWrapper(null, wrapped, map);
}).catch((err) => {
callbackWrapper(err);
})
}
export default loader;