From 7fa376db185157406dae720ca5d9b071271ec52a Mon Sep 17 00:00:00 2001 From: vakrilov Date: Fri, 13 Sep 2019 11:25:45 +0300 Subject: [PATCH 1/6] test: add code coverage with nyc --- .gitignore | 3 ++- .nycrc | 5 +++++ jasmine-config/jasmine.json | 2 +- package.json | 4 ++++ templates/webpack.config.spec.ts | 6 +++++- 5 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 .nycrc diff --git a/.gitignore b/.gitignore index 8f8de90d..fd60e545 100644 --- a/.gitignore +++ b/.gitignore @@ -36,5 +36,6 @@ bundle-config-loader.js hooks .DS_Store - +.nyc_output +coverage !projectHelpers.spec.js diff --git a/.nycrc b/.nycrc new file mode 100644 index 00000000..3294892a --- /dev/null +++ b/.nycrc @@ -0,0 +1,5 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["/demo/**"], + "reporter": ["text", "lcov"] +} \ No newline at end of file diff --git a/jasmine-config/jasmine.json b/jasmine-config/jasmine.json index 8d3ecdc5..3d06fa01 100644 --- a/jasmine-config/jasmine.json +++ b/jasmine-config/jasmine.json @@ -3,7 +3,7 @@ "spec_files": [ "!node_modules/**/*.spec.js", "!demo/**/*.spec.js", - "./*.spec.js" + "./**/*.spec.js" ], "helpers": [ "jasmine-config/**/*.js" diff --git a/package.json b/package.json index 5b7ff3e6..1503e671 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "prepare": "npm run tsc && npm run jasmine", "test": "npm run prepare", "jasmine": "jasmine --config=jasmine-config/jasmine.json", + "coverage": "nyc npm run test", "version": "rm package-lock.json && conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md" }, "bin": { @@ -77,6 +78,7 @@ "devDependencies": { "@angular/compiler": "8.2.0", "@angular/compiler-cli": "8.2.0", + "@istanbuljs/nyc-config-typescript": "^0.1.3", "@ngtools/webpack": "8.2.0", "@types/jasmine": "^3.3.7", "@types/loader-utils": "^1.1.3", @@ -87,7 +89,9 @@ "conventional-changelog-cli": "^1.3.22", "jasmine": "^3.2.0", "jasmine-spec-reporter": "^4.2.1", + "nyc": "^14.1.1", "proxyquire": "2.1.0", + "source-map-support": "^0.5.13", "tns-core-modules": "next", "typescript": "~3.5.3" } diff --git a/templates/webpack.config.spec.ts b/templates/webpack.config.spec.ts index 024461bd..d97535aa 100644 --- a/templates/webpack.config.spec.ts +++ b/templates/webpack.config.spec.ts @@ -32,9 +32,13 @@ const nativeScriptDevWebpack = { getEntryModule: () => 'EntryModule', getResolver: () => null, getConvertedExternals: nsWebpackIndex.getConvertedExternals, - getSourceMapFilename: nsWebpackIndex.getSourceMapFilename + getSourceMapFilename: nsWebpackIndex.getSourceMapFilename, + processAppComponents: nsWebpackIndex.processAppComponents, + getUserDefinedEntries: nsWebpackIndex.getUserDefinedEntries, }; + + const emptyObject = {}; const FakeAotTransformerFlag = "aot"; const FakeHmrTransformerFlag = "hmr"; From 659e8dd4e94a0f54ec9d6d8e4c5563b8bddb9ec1 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Mon, 16 Sep 2019 09:55:59 +0300 Subject: [PATCH 2/6] chore: add coverage files to .npmignore --- .npmignore | 2 ++ templates/webpack.config.spec.ts | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.npmignore b/.npmignore index 1c2c46d5..58343b36 100644 --- a/.npmignore +++ b/.npmignore @@ -7,6 +7,8 @@ demo *.spec.* .vscode/ .github/ +.nyc_output +coverage/ jasmine-config/ CONTRIBUTING.md CODE_OF_CONDUCT.md diff --git a/templates/webpack.config.spec.ts b/templates/webpack.config.spec.ts index d97535aa..e8ae3335 100644 --- a/templates/webpack.config.spec.ts +++ b/templates/webpack.config.spec.ts @@ -37,8 +37,6 @@ const nativeScriptDevWebpack = { getUserDefinedEntries: nsWebpackIndex.getUserDefinedEntries, }; - - const emptyObject = {}; const FakeAotTransformerFlag = "aot"; const FakeHmrTransformerFlag = "hmr"; From 7ac64fa478cec829e4e92500f1566db2afa62962 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Mon, 16 Sep 2019 09:53:08 +0300 Subject: [PATCH 3/6] test: add xml-namespace-loader tests --- xml-namespace-loader.js | 2 +- xml-namespace-loader.spec.ts | 171 +++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 xml-namespace-loader.spec.ts diff --git a/xml-namespace-loader.js b/xml-namespace-loader.js index e1294953..97360c18 100644 --- a/xml-namespace-loader.js +++ b/xml-namespace-loader.js @@ -89,7 +89,7 @@ module.exports = function (source, map) { const moduleRegisters = namespaces .map(convertPath) .map(n => - `global.registerModule("${n.name}", function() { return require("${n.path}"); });` + `global.registerModule("${n.name}", function() { return require("${n.path}"); });\n` ) .join(""); diff --git a/xml-namespace-loader.spec.ts b/xml-namespace-loader.spec.ts new file mode 100644 index 00000000..8150c479 --- /dev/null +++ b/xml-namespace-loader.spec.ts @@ -0,0 +1,171 @@ +import * as xmlNsLoader from "./xml-namespace-loader"; +import { join } from "path"; + +const CODE_FILE = ` + + + + + + + + + + +`; + +interface TestSetup { + resolveMap: { [path: string]: string }, + expectedDeps: string[], + expectedRegs: { name: string, path: string }[] +} + +function getContext( + done: DoneFn, + { resolveMap, expectedDeps, expectedRegs }: TestSetup) { + const actualDeps: string[] = []; + + const loaderContext = { + rootContext: "app", + context: "app/component", + async: () => (error, source: string) => { + expectedDeps.forEach(expectedDep => expect(actualDeps).toContain(expectedDep)); + + expectedRegs.forEach(({ name, path }) => { + const regCode = `global.registerModule("${name}", function() { return require("${path}"); });`; + expect(source).toContain(regCode); + }) + + done(); + }, + resolve: (context: string, request: string, callback: (err: Error, result: string) => void) => { + // console.log(`Resolve request: ${request}, result: ${resolveMap[request]}`); + if (resolveMap[request]) { + callback(undefined, resolveMap[request]); + } else { + callback(new Error(`Module ${request} not found`), undefined); + } + }, + addDependency: (dep: string) => { + actualDeps.push(dep); + }, + query: {} + } + + return loaderContext; +} + +fdescribe("XmlNamespaceLoader", () => { + it("with namespace pointing to files", (done) => { + const resolveMap = { + "app/nativescript-ui-chart": "app/nativescript-ui-chart.js", + "app/nativescript-ui-chart.xml": "app/nativescript-ui-chart.xml", + "app/nativescript-ui-chart.css": "app/nativescript-ui-chart.css", + }; + + const expectedDeps = [ + "app/nativescript-ui-chart.js", + "app/nativescript-ui-chart.xml", + "app/nativescript-ui-chart.css", + ]; + + const expectedRegs = [ + { name: "nativescript-ui-chart", path: "app/nativescript-ui-chart.js" }, + { name: "nativescript-ui-chart/RadCartesianChart", path: "app/nativescript-ui-chart.js" }, + { name: "nativescript-ui-chart/RadCartesianChart.xml", path: "app/nativescript-ui-chart.xml" }, + { name: "nativescript-ui-chart/RadCartesianChart.css", path: "app/nativescript-ui-chart.css" }, + ]; + + const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + + xmlNsLoader.call(loaderContext, CODE_FILE); + }) + + it("with namespace/elementName pointing to files (with package.json)", (done) => { + const resolveMap = { + "app/nativescript-ui-chart": "app/nativescript-ui-chart/RadCartesianChart.js", //simulate package.json + "app/nativescript-ui-chart/RadCartesianChart": "app/nativescript-ui-chart/RadCartesianChart.js", + "app/nativescript-ui-chart/RadCartesianChart.xml": "app/nativescript-ui-chart/RadCartesianChart.xml", + "app/nativescript-ui-chart/RadCartesianChart.css": "app/nativescript-ui-chart/RadCartesianChart.css", + } + + const expectedDeps = [ + "app/nativescript-ui-chart/RadCartesianChart.js", + "app/nativescript-ui-chart/RadCartesianChart.xml", + "app/nativescript-ui-chart/RadCartesianChart.css", + ]; + + const expectedRegs = [ + { name: "nativescript-ui-chart", path: "app/nativescript-ui-chart/RadCartesianChart.js" }, + { name: "nativescript-ui-chart/RadCartesianChart", path: "app/nativescript-ui-chart/RadCartesianChart.js" }, + { name: "nativescript-ui-chart/RadCartesianChart.xml", path: "app/nativescript-ui-chart/RadCartesianChart.xml" }, + { name: "nativescript-ui-chart/RadCartesianChart.css", path: "app/nativescript-ui-chart/RadCartesianChart.css" }, + ]; + + const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + xmlNsLoader.call(loaderContext, CODE_FILE); + }) + + it("with namespace/elementName pointing to files", (done) => { + const resolveMap = { + "app/nativescript-ui-chart/RadCartesianChart": "app/nativescript-ui-chart/RadCartesianChart.js", + "app/nativescript-ui-chart/RadCartesianChart.xml": "app/nativescript-ui-chart/RadCartesianChart.xml", + "app/nativescript-ui-chart/RadCartesianChart.css": "app/nativescript-ui-chart/RadCartesianChart.css", + } + + const expectedDeps = [ + "app/nativescript-ui-chart/RadCartesianChart.js", + "app/nativescript-ui-chart/RadCartesianChart.xml", + "app/nativescript-ui-chart/RadCartesianChart.css", + ]; + + const expectedRegs = [ + { name: "nativescript-ui-chart", path: "app/nativescript-ui-chart/RadCartesianChart.js" }, + { name: "nativescript-ui-chart/RadCartesianChart", path: "app/nativescript-ui-chart/RadCartesianChart.js" }, + { name: "nativescript-ui-chart/RadCartesianChart.xml", path: "app/nativescript-ui-chart/RadCartesianChart.xml" }, + { name: "nativescript-ui-chart/RadCartesianChart.css", path: "app/nativescript-ui-chart/RadCartesianChart.css" }, + ]; + + const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + xmlNsLoader.call(loaderContext, CODE_FILE); + }) + + it("with namespace/elementName pointing to files - only XML and CSS", (done) => { + const resolveMap = { + "app/nativescript-ui-chart/RadCartesianChart.xml": "app/nativescript-ui-chart/RadCartesianChart.xml", + "app/nativescript-ui-chart/RadCartesianChart.css": "app/nativescript-ui-chart/RadCartesianChart.css", + } + + const expectedDeps = [ + "app/nativescript-ui-chart/RadCartesianChart.xml", + "app/nativescript-ui-chart/RadCartesianChart.css", + ]; + + const expectedRegs = [ + { name: "nativescript-ui-chart/RadCartesianChart.xml", path: "app/nativescript-ui-chart/RadCartesianChart.xml" }, + { name: "nativescript-ui-chart/RadCartesianChart.css", path: "app/nativescript-ui-chart/RadCartesianChart.css" }, + ]; + + const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + xmlNsLoader.call(loaderContext, CODE_FILE); + }) + + it("with plugin path", (done) => { + const resolveMap = { + "nativescript-ui-chart": "node_module/nativescript-ui-chart/ui-chart.js", + } + + const expectedDeps = [ + ]; + + const expectedRegs = [ + { name: "nativescript-ui-chart", path: "nativescript-ui-chart" }, + { name: "nativescript-ui-chart/RadCartesianChart", path: "nativescript-ui-chart" }, + ]; + + const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + xmlNsLoader.call(loaderContext, CODE_FILE); + }) +}); + + From 253feb075b0602c45e928c0d3ff14eaf429db741 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Mon, 16 Sep 2019 16:58:47 +0300 Subject: [PATCH 4/6] chore: convert xml-ns-loader to TS --- .gitignore | 3 + xml-namespace-loader.spec.ts | 59 +++++++++++++++---- ...space-loader.js => xml-namespace-loader.ts | 13 ++-- 3 files changed, 58 insertions(+), 17 deletions(-) rename xml-namespace-loader.js => xml-namespace-loader.ts (93%) diff --git a/.gitignore b/.gitignore index fd60e545..e7cdd7db 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ jasmine-config/reporter.js bundle-config-loader.d.ts bundle-config-loader.js +xml-namespace-loader.d.ts +xml-namespace-loader.js + **/*.spec.js* **/*.spec.d.ts* diff --git a/xml-namespace-loader.spec.ts b/xml-namespace-loader.spec.ts index 8150c479..c0d3b2a7 100644 --- a/xml-namespace-loader.spec.ts +++ b/xml-namespace-loader.spec.ts @@ -1,5 +1,4 @@ -import * as xmlNsLoader from "./xml-namespace-loader"; -import { join } from "path"; +import xmlNsLoader from "./xml-namespace-loader"; const CODE_FILE = ` @@ -17,12 +16,14 @@ const CODE_FILE = ` interface TestSetup { resolveMap: { [path: string]: string }, expectedDeps: string[], - expectedRegs: { name: string, path: string }[] + expectedRegs: { name: string, path: string }[], + ignore?: RegExp, + assureNoDeps?: boolean; } function getContext( done: DoneFn, - { resolveMap, expectedDeps, expectedRegs }: TestSetup) { + { resolveMap, expectedDeps, expectedRegs, assureNoDeps, ignore }: TestSetup) { const actualDeps: string[] = []; const loaderContext = { @@ -36,6 +37,11 @@ function getContext( expect(source).toContain(regCode); }) + if (assureNoDeps) { + expect(actualDeps.length).toBe(0); + expect(source).not.toContain("global.registerModule"); + } + done(); }, resolve: (context: string, request: string, callback: (err: Error, result: string) => void) => { @@ -49,7 +55,7 @@ function getContext( addDependency: (dep: string) => { actualDeps.push(dep); }, - query: {} + query: { ignore } } return loaderContext; @@ -76,7 +82,7 @@ fdescribe("XmlNamespaceLoader", () => { { name: "nativescript-ui-chart/RadCartesianChart.css", path: "app/nativescript-ui-chart.css" }, ]; - const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs }); xmlNsLoader.call(loaderContext, CODE_FILE); }) @@ -102,7 +108,7 @@ fdescribe("XmlNamespaceLoader", () => { { name: "nativescript-ui-chart/RadCartesianChart.css", path: "app/nativescript-ui-chart/RadCartesianChart.css" }, ]; - const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs }); xmlNsLoader.call(loaderContext, CODE_FILE); }) @@ -126,7 +132,7 @@ fdescribe("XmlNamespaceLoader", () => { { name: "nativescript-ui-chart/RadCartesianChart.css", path: "app/nativescript-ui-chart/RadCartesianChart.css" }, ]; - const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs }); xmlNsLoader.call(loaderContext, CODE_FILE); }) @@ -146,7 +152,7 @@ fdescribe("XmlNamespaceLoader", () => { { name: "nativescript-ui-chart/RadCartesianChart.css", path: "app/nativescript-ui-chart/RadCartesianChart.css" }, ]; - const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs }); xmlNsLoader.call(loaderContext, CODE_FILE); }) @@ -157,15 +163,44 @@ fdescribe("XmlNamespaceLoader", () => { const expectedDeps = [ ]; - + const expectedRegs = [ { name: "nativescript-ui-chart", path: "nativescript-ui-chart" }, { name: "nativescript-ui-chart/RadCartesianChart", path: "nativescript-ui-chart" }, ]; - const loaderContext = getContext(done, {resolveMap, expectedDeps, expectedRegs }); + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs }); xmlNsLoader.call(loaderContext, CODE_FILE); }) -}); + it("with plugin path", (done) => { + const resolveMap = { + "nativescript-ui-chart": "node_module/nativescript-ui-chart/ui-chart.js", + } + const expectedDeps = [ + ]; + + const expectedRegs = [ + { name: "nativescript-ui-chart", path: "nativescript-ui-chart" }, + { name: "nativescript-ui-chart/RadCartesianChart", path: "nativescript-ui-chart" }, + ]; + + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs }); + xmlNsLoader.call(loaderContext, CODE_FILE); + }) + + it("with ignored namespace should not add deps or register calls", (done) => { + const resolveMap = { + "app/nativescript-ui-chart": "app/nativescript-ui-chart.js", + "app/nativescript-ui-chart.xml": "app/nativescript-ui-chart.xml", + "app/nativescript-ui-chart.css": "app/nativescript-ui-chart.css", + }; + const expectedDeps = []; + const expectedRegs = []; + + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs, ignore: /nativescript\-ui\-chart/, assureNoDeps: true }); + + xmlNsLoader.call(loaderContext, CODE_FILE); + }) +}); diff --git a/xml-namespace-loader.js b/xml-namespace-loader.ts similarity index 93% rename from xml-namespace-loader.js rename to xml-namespace-loader.ts index 97360c18..8359460d 100644 --- a/xml-namespace-loader.js +++ b/xml-namespace-loader.ts @@ -1,8 +1,9 @@ -const { parse, relative, join, basename, extname } = require("path"); -const { promisify } = require('util'); -const { convertSlashesInPath } = require("./projectHelpers"); +import { parse, join } from "path"; +import { promisify } from "util"; +import { loader } from "webpack"; +import { convertSlashesInPath } from "./projectHelpers"; -module.exports = function (source, map) { +const loader: loader.Loader = function (source, map) { this.value = source; const { ignore } = this.query; const callback = this.async(); @@ -55,7 +56,7 @@ module.exports = function (source, map) { promises.push(resolvePromise(this.context, localNamespacePath) .then(path => pathResolved(path)) .catch(() => { - return promise = resolvePromise(this.context, localModulePath) + return resolvePromise(this.context, localModulePath) .then(path => pathResolved(path)) .catch(() => { return Promise.all([ @@ -112,3 +113,5 @@ function convertPath(obj) { obj.path = convertSlashesInPath(obj.path); return obj; } + +export default loader; \ No newline at end of file From 18ecbec61e7d5f02b70025c9e9414068a94abf61 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Mon, 16 Sep 2019 17:20:23 +0300 Subject: [PATCH 5/6] refactor: remove duplicate registers --- xml-namespace-loader.spec.ts | 17 ----------------- xml-namespace-loader.ts | 29 +++++++++++++++-------------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/xml-namespace-loader.spec.ts b/xml-namespace-loader.spec.ts index c0d3b2a7..9fe6ff14 100644 --- a/xml-namespace-loader.spec.ts +++ b/xml-namespace-loader.spec.ts @@ -173,23 +173,6 @@ fdescribe("XmlNamespaceLoader", () => { xmlNsLoader.call(loaderContext, CODE_FILE); }) - it("with plugin path", (done) => { - const resolveMap = { - "nativescript-ui-chart": "node_module/nativescript-ui-chart/ui-chart.js", - } - - const expectedDeps = [ - ]; - - const expectedRegs = [ - { name: "nativescript-ui-chart", path: "nativescript-ui-chart" }, - { name: "nativescript-ui-chart/RadCartesianChart", path: "nativescript-ui-chart" }, - ]; - - const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs }); - xmlNsLoader.call(loaderContext, CODE_FILE); - }) - it("with ignored namespace should not add deps or register calls", (done) => { const resolveMap = { "app/nativescript-ui-chart": "app/nativescript-ui-chart.js", diff --git a/xml-namespace-loader.ts b/xml-namespace-loader.ts index 8359460d..5fcbaca5 100644 --- a/xml-namespace-loader.ts +++ b/xml-namespace-loader.ts @@ -3,6 +3,11 @@ import { promisify } from "util"; import { loader } from "webpack"; import { convertSlashesInPath } from "./projectHelpers"; +interface NamespaceEntry { + name: string; + path: string +} + const loader: loader.Loader = function (source, map) { this.value = source; const { ignore } = this.query; @@ -11,9 +16,9 @@ const loader: loader.Loader = function (source, map) { const { XmlParser } = require("tns-core-modules/xml"); const resolvePromise = promisify(this.resolve); - const promises = []; + const promises: Promise[] = []; - const namespaces = []; + const namespaces: NamespaceEntry[] = []; const parser = new XmlParser((event) => { const { namespace, elementName } = event; const moduleName = `${namespace}/${elementName}`; @@ -87,12 +92,13 @@ const loader: loader.Loader = function (source, map) { parser.parse(source); Promise.all(promises).then(() => { - const moduleRegisters = namespaces - .map(convertPath) - .map(n => - `global.registerModule("${n.name}", function() { return require("${n.path}"); });\n` - ) - .join(""); + const distinctNamespaces = new Map(); + 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 @@ -100,7 +106,7 @@ const loader: loader.Loader = function (source, map) { .replace(/\u2028/g, '\\u2028') .replace(/\u2029/g, '\\u2029'); - const wrapped = `${moduleRegisters}\nmodule.exports = ${json}`; + const wrapped = `${moduleRegisters.join("")}\nmodule.exports = ${json}`; callback(null, wrapped, map); }).catch((err) => { @@ -109,9 +115,4 @@ const loader: loader.Loader = function (source, map) { } -function convertPath(obj) { - obj.path = convertSlashesInPath(obj.path); - return obj; -} - export default loader; \ No newline at end of file From eedfa4ea18dfd3eb20065cd97b0b4d33f4fc7f0b Mon Sep 17 00:00:00 2001 From: vakrilov Date: Tue, 17 Sep 2019 11:18:26 +0300 Subject: [PATCH 6/6] refactor: use sax xml parser instead of tns --- package.json | 2 ++ xml-namespace-loader.spec.ts | 42 ++++++++++++++++++++++++++++++++---- xml-namespace-loader.ts | 33 +++++++++++++++++----------- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 1503e671..2b04ea93 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "request": "2.88.0", "resolve-url-loader": "~3.0.0", "sass-loader": "~7.1.0", + "sax": "^1.2.4", "schema-utils": "0.4.5", "semver": "^6.0.0", "shelljs": "0.6.0", @@ -84,6 +85,7 @@ "@types/loader-utils": "^1.1.3", "@types/node": "^10.12.12", "@types/proxyquire": "1.3.28", + "@types/sax": "^1.2.0", "@types/semver": "^6.0.0", "@types/webpack": "^4.4.34", "conventional-changelog-cli": "^1.3.22", diff --git a/xml-namespace-loader.spec.ts b/xml-namespace-loader.spec.ts index 9fe6ff14..28d12583 100644 --- a/xml-namespace-loader.spec.ts +++ b/xml-namespace-loader.spec.ts @@ -18,12 +18,13 @@ interface TestSetup { expectedDeps: string[], expectedRegs: { name: string, path: string }[], ignore?: RegExp, - assureNoDeps?: boolean; + assureNoDeps?: boolean, + expectError?: boolean } function getContext( done: DoneFn, - { resolveMap, expectedDeps, expectedRegs, assureNoDeps, ignore }: TestSetup) { + { resolveMap, expectedDeps, expectedRegs, assureNoDeps, ignore, expectError }: TestSetup) { const actualDeps: string[] = []; const loaderContext = { @@ -42,7 +43,13 @@ function getContext( expect(source).not.toContain("global.registerModule"); } - done(); + if (error && !expectError) { + done.fail(error) + } else if (!error && expectError) { + done.fail("Error expected here") + } else { + done(); + } }, resolve: (context: string, request: string, callback: (err: Error, result: string) => void) => { // console.log(`Resolve request: ${request}, result: ${resolveMap[request]}`); @@ -61,7 +68,7 @@ function getContext( return loaderContext; } -fdescribe("XmlNamespaceLoader", () => { +describe("XmlNamespaceLoader", () => { it("with namespace pointing to files", (done) => { const resolveMap = { "app/nativescript-ui-chart": "app/nativescript-ui-chart.js", @@ -186,4 +193,31 @@ fdescribe("XmlNamespaceLoader", () => { xmlNsLoader.call(loaderContext, CODE_FILE); }) + + it("with XML declaration and Doctype does not fail", (done) => { + const resolveMap = {}; + const expectedDeps = []; + const expectedRegs = []; + + const testXml = ` + + + + `; + + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs, assureNoDeps: true }); + + xmlNsLoader.call(loaderContext, testXml); + }) + it("with invalid XML fails", (done) => { + const resolveMap = {}; + const expectedDeps = []; + const expectedRegs = []; + + const testXml = ``; + + const loaderContext = getContext(done, { resolveMap, expectedDeps, expectedRegs, expectError: true }); + + xmlNsLoader.call(loaderContext, testXml); + }) }); diff --git a/xml-namespace-loader.ts b/xml-namespace-loader.ts index 5fcbaca5..de07e734 100644 --- a/xml-namespace-loader.ts +++ b/xml-namespace-loader.ts @@ -1,6 +1,8 @@ import { parse, join } from "path"; import { promisify } from "util"; import { loader } from "webpack"; +import { parser, QualifiedTag } from "sax"; + import { convertSlashesInPath } from "./projectHelpers"; interface NamespaceEntry { @@ -8,21 +10,18 @@ interface NamespaceEntry { path: string } -const loader: loader.Loader = function (source, map) { +const loader: loader.Loader = function (source: string, map) { this.value = source; const { ignore } = this.query; const callback = this.async(); - const { XmlParser } = require("tns-core-modules/xml"); - const resolvePromise = promisify(this.resolve); const promises: Promise[] = []; - const namespaces: NamespaceEntry[] = []; - const parser = new XmlParser((event) => { - const { namespace, elementName } = event; - const moduleName = `${namespace}/${elementName}`; + let parsingError = false; + const handleOpenTag = (namespace: string, elementName: string) => { + const moduleName = `${namespace}/${elementName}`; if ( namespace && !namespace.startsWith("http") && @@ -87,9 +86,16 @@ const loader: loader.Loader = function (source, map) { }) ); } - }, undefined, true); + } - parser.parse(source); + const saxParser = parser(true, { xmlns: true }); + saxParser.onopentag = (node: QualifiedTag) => { handleOpenTag(node.uri, node.local); }; + saxParser.onerror = (err) => { + saxParser.error = null; + parsingError = true; + callback(err); + }; + saxParser.write(source).close(); Promise.all(promises).then(() => { const distinctNamespaces = new Map(); @@ -108,11 +114,14 @@ const loader: loader.Loader = function (source, map) { const wrapped = `${moduleRegisters.join("")}\nmodule.exports = ${json}`; - callback(null, wrapped, map); + if (!parsingError) { + callback(null, wrapped, map); + } }).catch((err) => { - callback(err); + if (!parsingError) { + callback(err); + } }) - } export default loader; \ No newline at end of file