mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00
chore(core): monorepo, esm targeting, improved management (#8707)
This commit is contained in:
141
packages/webpack/helpers/xml-namespace-loader.ts
Normal file
141
packages/webpack/helpers/xml-namespace-loader.ts
Normal file
@ -0,0 +1,141 @@
|
||||
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;
|
Reference in New Issue
Block a user