From c1c1a39d867c3bf1a12549d5a03103be984a153b Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Thu, 6 Dec 2018 22:15:26 +0200 Subject: [PATCH 1/3] fix: project's package.json indentation is not persisted In case project's package.json uses tabs or multiple spaces for indentation, the postinstall script of nativescript-dev-webpack overwrites it with two spaces whenever it is executed. The problem is that the plugin tries to modify the package.json and persists it on every operation. Fix the behavior by checking the indentation character and use it when stringifying the content. Also compare the current content of the file with the one we will write and skip the write operation in case they match. Add unit tests for the new methods. --- .gitignore | 2 + jasmine-config/jasmine.json | 3 +- projectHelpers.js | 22 ++++++-- projectHelpers.spec.js | 105 ++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 projectHelpers.spec.js diff --git a/.gitignore b/.gitignore index 2f9c7302..4ab4a5ea 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ jasmine-config/reporter.js.map hooks .DS_Store + +!projectHelpers.spec.js diff --git a/jasmine-config/jasmine.json b/jasmine-config/jasmine.json index 31a4a781..84db3eb2 100644 --- a/jasmine-config/jasmine.json +++ b/jasmine-config/jasmine.json @@ -1,7 +1,8 @@ { "spec_dir": ".", "spec_files": [ - "./!(node_modules)/**/*.spec.js" + "./!(node_modules)/**/*.spec.js", + "./*.spec.js" ], "helpers": [ "jasmine-config/**/*.js" diff --git a/projectHelpers.js b/projectHelpers.js index 50413157..bf11d953 100644 --- a/projectHelpers.js +++ b/projectHelpers.js @@ -1,5 +1,5 @@ const { resolve } = require("path"); -const { readFileSync, writeFileSync } = require("fs"); +const fs = require("fs"); const hook = require("nativescript-hook")(__dirname); @@ -38,13 +38,26 @@ const isVue = ({ projectDir, packageJson } = {}) => { const getPackageJson = projectDir => { const packageJsonPath = getPackageJsonPath(projectDir); - return JSON.parse(readFileSync(packageJsonPath, "utf8")); + return JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); }; const writePackageJson = (content, projectDir) => { const packageJsonPath = getPackageJsonPath(projectDir); - writeFileSync(packageJsonPath, JSON.stringify(content, null, 2)) + const currentJsonContent = fs.readFileSync(packageJsonPath); + const indentation = getIndentationCharacter(currentJsonContent); + const stringifiedContent = JSON.stringify(content, null, indentation); + const currentPackageJsonContent = JSON.parse(currentJsonContent); + + if (JSON.stringify(currentPackageJsonContent, null, indentation) !== stringifiedContent) { + fs.writeFileSync(packageJsonPath, stringifiedContent) + } } + +const getIndentationCharacter = (jsonContent) => { + const matches = jsonContent.match(/{\r*\n*(\W*)"/m); + return matches && matches[1]; +} + const getProjectDir = hook.findProjectDir; const getPackageJsonPath = projectDir => resolve(projectDir, "package.json"); @@ -96,5 +109,6 @@ module.exports = { isVue, isTypeScript, writePackageJson, - convertSlashesInPath + convertSlashesInPath, + getIndentationCharacter }; \ No newline at end of file diff --git a/projectHelpers.spec.js b/projectHelpers.spec.js new file mode 100644 index 00000000..e2a5ac75 --- /dev/null +++ b/projectHelpers.spec.js @@ -0,0 +1,105 @@ +const { getIndentationCharacter, writePackageJson } = require("./projectHelpers"); +const fs = require("fs"); + +describe('projectHelpers', () => { + const originalReadFileSync = fs.readFileSync; + const originalWriteFileSync = fs.writeFileSync; + const tab = "\t"; + const multipleSpaces = " "; + const twoSpaces = " "; + + afterEach(() => { + fs.readFileSync = originalReadFileSync; + fs.writeFileSync = originalWriteFileSync; + }); + + describe('getIndentationCharacter', () => { + [ + { + testName: 'returns two spaces when file starts with two spaces', + input: `{${twoSpaces}"abc": "1"${twoSpaces}}`, + expectedResult: twoSpaces + }, + { + testName: 'returns empty string when file starts without any indentation', + input: `{"abc": "1"}`, + expectedResult: '' + }, + { + testName: 'returns tab when file starts with tab', + input: `{${tab}"abc": "1"${tab}}`, + expectedResult: tab + }, + { + testName: 'returns two spaces when file starts with two spaces and new line before them', + input: `{\n${twoSpaces}"abc": "1"\n}`, + expectedResult: twoSpaces + }, + { + testName: 'returns tab when file starts with tab and new line before them', + input: `{\n${tab}"abc": "1"\n}`, + expectedResult: tab + }, + { + testName: 'returns multiple spaces when file starts with multiple spaces and new line before them', + input: `{\n${multipleSpaces}"abc": "1"\n}`, + expectedResult: multipleSpaces + } + ].forEach(({ testName, input, expectedResult }) => { + it(testName, () => { + expect(getIndentationCharacter(input)).toEqual(expectedResult); + }); + }); + }); + + describe('writePackageJson', () => { + const mockFileSystemApi = () => { + const data = { + isWriteFileSyncCalled: false + }; + + fs.readFileSync = (p) => { + return JSON.stringify({ a: 1 }); + }; + + fs.writeFileSync = (p, c) => { + data.isWriteFileSyncCalled = true; + }; + + return data; + }; + + it('does not write package.json when content has not changed', () => { + const data = mockFileSystemApi(); + writePackageJson({ a: 1 }, "projDir"); + expect(data.isWriteFileSyncCalled).toBe(false); + }); + + it('writes content, when the new one is different from the current one', () => { + const data = mockFileSystemApi(); + writePackageJson({ b: 2 }, "projDir"); + expect(data.isWriteFileSyncCalled).toBe(true); + }); + + it('keeps indentation of the package.json when rewriting it', () => { + let currentIndentSymbol = tab; + fs.readFileSync = (p) => { + return JSON.stringify({ a: 1 }, null, currentIndentSymbol); + }; + + let writtenContent = null; + fs.writeFileSync = (p, c) => { + writtenContent = c; + }; + + // Ensure tab indentation is persisted + writePackageJson({ b: 2 }, "projDir"); + expect(writtenContent).toBe(`{\n${tab}"b": 2\n}`); + + // Ensure spaces indentation is persisted + currentIndentSymbol = multipleSpaces; + writePackageJson({ b: 2 }, "projDir"); + expect(writtenContent).toBe(`{\n${multipleSpaces}"b": 2\n}`); + }); + }); +}); From 2caede548943199207883dd1b2e777b9b43ffc0b Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 7 Dec 2018 15:10:52 +0200 Subject: [PATCH 2/3] chore: run unit tests when preparing the package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b7366a86..f16c5033 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "scripts": { "postinstall": "node postinstall.js", "postpack": "rm -rf node_modules", - "prepare": "tsc", + "prepare": "tsc && npm run jasmine", "test": "npm run prepare && npm run jasmine", "jasmine": "jasmine --config=jasmine-config/jasmine.json", "version": "rm package-lock.json && conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md" From e511b670e648b05f350716227eec0acb10aa51b0 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Mon, 10 Dec 2018 13:26:38 +0200 Subject: [PATCH 3/3] fix: getIndentationCharacter is not working when buffer is passed --- projectHelpers.js | 2 +- projectHelpers.spec.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/projectHelpers.js b/projectHelpers.js index bf11d953..0f61b76c 100644 --- a/projectHelpers.js +++ b/projectHelpers.js @@ -54,7 +54,7 @@ const writePackageJson = (content, projectDir) => { } const getIndentationCharacter = (jsonContent) => { - const matches = jsonContent.match(/{\r*\n*(\W*)"/m); + const matches = jsonContent && jsonContent.toString().match(/{\r*\n*(\W*)"/m); return matches && matches[1]; } diff --git a/projectHelpers.spec.js b/projectHelpers.spec.js index e2a5ac75..c8a83b88 100644 --- a/projectHelpers.spec.js +++ b/projectHelpers.spec.js @@ -18,6 +18,11 @@ describe('projectHelpers', () => { { testName: 'returns two spaces when file starts with two spaces', input: `{${twoSpaces}"abc": "1"${twoSpaces}}`, + expectedResult: twoSpaces + }, + { + testName: 'returns two spaces when file starts with two spaces and binary content is passed', + input: Buffer.from(`{${twoSpaces}"abc": "1"${twoSpaces}}`), expectedResult: twoSpaces }, {