Skip to content
This repository was archived by the owner on Aug 7, 2021. It is now read-only.

Commit a45a45c

Browse files
fix: project's package.json indentation is not persisted (#727)
* 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. * chore: run unit tests when preparing the package * fix: getIndentationCharacter is not working when buffer is passed
1 parent 8a231b2 commit a45a45c

File tree

5 files changed

+133
-6
lines changed

5 files changed

+133
-6
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ jasmine-config/reporter.js.map
3232

3333
hooks
3434
.DS_Store
35+
36+
!projectHelpers.spec.js

Diff for: jasmine-config/jasmine.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"spec_dir": ".",
33
"spec_files": [
4-
"./!(node_modules)/**/*.spec.js"
4+
"./!(node_modules)/**/*.spec.js",
5+
"./*.spec.js"
56
],
67
"helpers": [
78
"jasmine-config/**/*.js"

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"scripts": {
6161
"postinstall": "node postinstall.js",
6262
"postpack": "rm -rf node_modules",
63-
"prepare": "tsc",
63+
"prepare": "tsc && npm run jasmine",
6464
"test": "npm run prepare && npm run jasmine",
6565
"jasmine": "jasmine --config=jasmine-config/jasmine.json",
6666
"version": "rm package-lock.json && conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"

Diff for: projectHelpers.js

+18-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { resolve } = require("path");
2-
const { readFileSync, writeFileSync } = require("fs");
2+
const fs = require("fs");
33

44
const hook = require("nativescript-hook")(__dirname);
55

@@ -38,13 +38,26 @@ const isVue = ({ projectDir, packageJson } = {}) => {
3838

3939
const getPackageJson = projectDir => {
4040
const packageJsonPath = getPackageJsonPath(projectDir);
41-
return JSON.parse(readFileSync(packageJsonPath, "utf8"));
41+
return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
4242
};
4343

4444
const writePackageJson = (content, projectDir) => {
4545
const packageJsonPath = getPackageJsonPath(projectDir);
46-
writeFileSync(packageJsonPath, JSON.stringify(content, null, 2))
46+
const currentJsonContent = fs.readFileSync(packageJsonPath);
47+
const indentation = getIndentationCharacter(currentJsonContent);
48+
const stringifiedContent = JSON.stringify(content, null, indentation);
49+
const currentPackageJsonContent = JSON.parse(currentJsonContent);
50+
51+
if (JSON.stringify(currentPackageJsonContent, null, indentation) !== stringifiedContent) {
52+
fs.writeFileSync(packageJsonPath, stringifiedContent)
53+
}
4754
}
55+
56+
const getIndentationCharacter = (jsonContent) => {
57+
const matches = jsonContent && jsonContent.toString().match(/{\r*\n*(\W*)"/m);
58+
return matches && matches[1];
59+
}
60+
4861
const getProjectDir = hook.findProjectDir;
4962

5063
const getPackageJsonPath = projectDir => resolve(projectDir, "package.json");
@@ -96,5 +109,6 @@ module.exports = {
96109
isVue,
97110
isTypeScript,
98111
writePackageJson,
99-
convertSlashesInPath
112+
convertSlashesInPath,
113+
getIndentationCharacter
100114
};

Diff for: projectHelpers.spec.js

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
const { getIndentationCharacter, writePackageJson } = require("./projectHelpers");
2+
const fs = require("fs");
3+
4+
describe('projectHelpers', () => {
5+
const originalReadFileSync = fs.readFileSync;
6+
const originalWriteFileSync = fs.writeFileSync;
7+
const tab = "\t";
8+
const multipleSpaces = " ";
9+
const twoSpaces = " ";
10+
11+
afterEach(() => {
12+
fs.readFileSync = originalReadFileSync;
13+
fs.writeFileSync = originalWriteFileSync;
14+
});
15+
16+
describe('getIndentationCharacter', () => {
17+
[
18+
{
19+
testName: 'returns two spaces when file starts with two spaces',
20+
input: `{${twoSpaces}"abc": "1"${twoSpaces}}`,
21+
expectedResult: twoSpaces
22+
},
23+
{
24+
testName: 'returns two spaces when file starts with two spaces and binary content is passed',
25+
input: Buffer.from(`{${twoSpaces}"abc": "1"${twoSpaces}}`),
26+
expectedResult: twoSpaces
27+
},
28+
{
29+
testName: 'returns empty string when file starts without any indentation',
30+
input: `{"abc": "1"}`,
31+
expectedResult: ''
32+
},
33+
{
34+
testName: 'returns tab when file starts with tab',
35+
input: `{${tab}"abc": "1"${tab}}`,
36+
expectedResult: tab
37+
},
38+
{
39+
testName: 'returns two spaces when file starts with two spaces and new line before them',
40+
input: `{\n${twoSpaces}"abc": "1"\n}`,
41+
expectedResult: twoSpaces
42+
},
43+
{
44+
testName: 'returns tab when file starts with tab and new line before them',
45+
input: `{\n${tab}"abc": "1"\n}`,
46+
expectedResult: tab
47+
},
48+
{
49+
testName: 'returns multiple spaces when file starts with multiple spaces and new line before them',
50+
input: `{\n${multipleSpaces}"abc": "1"\n}`,
51+
expectedResult: multipleSpaces
52+
}
53+
].forEach(({ testName, input, expectedResult }) => {
54+
it(testName, () => {
55+
expect(getIndentationCharacter(input)).toEqual(expectedResult);
56+
});
57+
});
58+
});
59+
60+
describe('writePackageJson', () => {
61+
const mockFileSystemApi = () => {
62+
const data = {
63+
isWriteFileSyncCalled: false
64+
};
65+
66+
fs.readFileSync = (p) => {
67+
return JSON.stringify({ a: 1 });
68+
};
69+
70+
fs.writeFileSync = (p, c) => {
71+
data.isWriteFileSyncCalled = true;
72+
};
73+
74+
return data;
75+
};
76+
77+
it('does not write package.json when content has not changed', () => {
78+
const data = mockFileSystemApi();
79+
writePackageJson({ a: 1 }, "projDir");
80+
expect(data.isWriteFileSyncCalled).toBe(false);
81+
});
82+
83+
it('writes content, when the new one is different from the current one', () => {
84+
const data = mockFileSystemApi();
85+
writePackageJson({ b: 2 }, "projDir");
86+
expect(data.isWriteFileSyncCalled).toBe(true);
87+
});
88+
89+
it('keeps indentation of the package.json when rewriting it', () => {
90+
let currentIndentSymbol = tab;
91+
fs.readFileSync = (p) => {
92+
return JSON.stringify({ a: 1 }, null, currentIndentSymbol);
93+
};
94+
95+
let writtenContent = null;
96+
fs.writeFileSync = (p, c) => {
97+
writtenContent = c;
98+
};
99+
100+
// Ensure tab indentation is persisted
101+
writePackageJson({ b: 2 }, "projDir");
102+
expect(writtenContent).toBe(`{\n${tab}"b": 2\n}`);
103+
104+
// Ensure spaces indentation is persisted
105+
currentIndentSymbol = multipleSpaces;
106+
writePackageJson({ b: 2 }, "projDir");
107+
expect(writtenContent).toBe(`{\n${multipleSpaces}"b": 2\n}`);
108+
});
109+
});
110+
});

0 commit comments

Comments
 (0)