Skip to content

Commit 562bb96

Browse files
committed
refactor: clean up config handling and migration
1 parent 06c1ecc commit 562bb96

File tree

9 files changed

+191
-126
lines changed

9 files changed

+191
-126
lines changed

lib/commands/migrate.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class MigrateCommand implements ICommand {
2626

2727
if (!shouldMigrateResult) {
2828
this.$logger.printMarkdown(
29-
'__Project is compatible with NativeScript "v6.0.0". To get the latest NativeScript packages execute "ns update".__'
29+
'__Project is compatible with NativeScript "v7.0.0". To get the latest NativeScript packages execute "ns update".__'
3030
);
3131
return;
3232
}

lib/controllers/migrate-controller.ts

+65-52
Original file line numberDiff line numberDiff line change
@@ -243,13 +243,14 @@ Running this command will ${MigrateController.COMMON_MIGRATE_MESSAGE}`;
243243

244244
try {
245245
this.$logger.info("Backup project configuration.");
246-
const backupFolders = MigrateController.folders;
247-
const embeddedPackagePath = path.join(
248-
projectData.getAppDirectoryRelativePath(),
249-
"package.json"
246+
this.backup(
247+
[
248+
...MigrateController.folders,
249+
path.join(projectData.getAppDirectoryRelativePath(), "package.json"),
250+
],
251+
backupDir,
252+
projectData.projectDir
250253
);
251-
backupFolders.push(embeddedPackagePath);
252-
this.backup(backupFolders, backupDir, projectData.projectDir);
253254
this.$logger.info("Backup project configuration complete.");
254255
} catch (error) {
255256
this.$logger.error(MigrateController.backupFailMessage);
@@ -342,8 +343,8 @@ Running this command will ${MigrateController.COMMON_MIGRATE_MESSAGE}`;
342343
platforms,
343344
allowInvalidVersions,
344345
}: IMigrationData): Promise<boolean> {
346+
const isMigrate = _.get(this.$options, "argv._[0]") === "migrate";
345347
const projectData = this.$projectDataService.getProjectData(projectDir);
346-
const isMigrate = this.$options.argv._[0] === "migrate";
347348
const projectInfo = this.$projectConfigService.detectInfo(
348349
projectData.projectDir
349350
);
@@ -803,76 +804,88 @@ Running this command will ${MigrateController.COMMON_MIGRATE_MESSAGE}`;
803804
}
804805

805806
private async migrateConfig(projectData: IProjectData) {
806-
const embeddedPackagePath = path.resolve(
807+
this.$logger.info(
808+
`Migrating project to use ${constants.CONFIG_FILE_NAME_TS}...`
809+
);
810+
811+
// app/package.json or src/package.json usually
812+
const embeddedPackageJsonPath = path.resolve(
807813
projectData.projectDir,
808814
projectData.getAppDirectoryRelativePath(),
809815
constants.PACKAGE_JSON_FILE_NAME
810816
);
817+
this.$logger.debug(`embeddedPackageJsonPath: ${embeddedPackageJsonPath}`);
818+
// nsconfig.json
811819
const legacyNsConfigPath = path.resolve(
812820
projectData.projectDir,
813821
constants.CONFIG_NS_FILE_NAME
814822
);
823+
this.$logger.debug(`legacyNsConfigPath: ${legacyNsConfigPath}`);
824+
// package.json
825+
const rootPackageJsonPath: any = path.resolve(
826+
projectData.projectDir,
827+
constants.PACKAGE_JSON_FILE_NAME
828+
);
829+
this.$logger.debug(`rootPackageJsonPath: ${rootPackageJsonPath}`);
830+
815831
let embeddedPackageData: any = {};
816-
if (this.$fs.exists(embeddedPackagePath)) {
817-
embeddedPackageData = this.$fs.readJson(embeddedPackagePath);
832+
if (this.$fs.exists(embeddedPackageJsonPath)) {
833+
embeddedPackageData = this.$fs.readJson(embeddedPackageJsonPath);
818834
}
835+
819836
let legacyNsConfigData: any = {};
820837
if (this.$fs.exists(legacyNsConfigPath)) {
821838
legacyNsConfigData = this.$fs.readJson(legacyNsConfigPath);
822839
}
823-
const legacyData: any = {
840+
841+
let rootPackageJsonData: any = {};
842+
if (this.$fs.exists(rootPackageJsonPath)) {
843+
rootPackageJsonData = this.$fs.readJson(rootPackageJsonPath);
844+
}
845+
846+
const dataToMigrate: any = {
824847
...embeddedPackageData,
825848
...legacyNsConfigData,
826849
};
827-
const packageJsonPath: any = path.resolve(
828-
projectData.projectDir,
829-
constants.PACKAGE_JSON_FILE_NAME
830-
);
831-
const packageJsonData: any = this.$fs.readFile(packageJsonPath);
832-
if (legacyData.main) {
833-
packageJsonPath.main = legacyData.main;
834-
delete legacyData.main;
850+
851+
// move main key into root packagejson
852+
if (dataToMigrate.main) {
853+
rootPackageJsonData.main = dataToMigrate.main;
854+
delete dataToMigrate.main;
835855
}
836-
if (
837-
legacyData &&
838-
legacyData.android &&
839-
typeof legacyData.android.codeCache === "string"
840-
) {
841-
legacyData.android.codeCache = legacyData.android.codeCache === "true";
856+
857+
// migrate data into nativescript.config.ts
858+
const success = this.$projectConfigService.setValue("", dataToMigrate);
859+
860+
if (!success) {
861+
this.$errors.fail(
862+
`Failed to migrate project to use ${constants.CONFIG_FILE_NAME_TS}. One or more values could not be updated.`
863+
);
842864
}
843-
const flattenObjectToPaths = (obj: any, basePath?: string): any => {
844-
const toPath = (key: any) => [basePath, key].filter(Boolean).join(".");
845-
return Object.keys(obj).reduce((all: any, key) => {
846-
if (typeof obj[key] === "object") {
847-
return [...all, ...flattenObjectToPaths(obj[key], toPath(key))];
848-
}
849-
return [
850-
...all,
851-
{
852-
key: toPath(key),
853-
value: obj[key],
854-
},
855-
];
856-
}, []);
857-
};
858-
const dotNotationPaths = flattenObjectToPaths(legacyData);
859-
dotNotationPaths.forEach((p: any) => {
860-
// this.$logger.info(p.key, p.value);
861-
this.$projectConfigService.setValue(p.key, p.value);
862-
});
865+
866+
// move app id into nativescript.config.ts
863867
if (
864-
packageJsonData &&
865-
packageJsonData.nativescript &&
866-
packageJsonData.nativescript.id
868+
rootPackageJsonData &&
869+
rootPackageJsonData.nativescript &&
870+
rootPackageJsonData.nativescript.id
867871
) {
868872
this.$projectConfigService.setValue(
869873
"id",
870-
packageJsonData.nativescript.id
874+
rootPackageJsonData.nativescript.id
871875
);
872-
delete packageJsonData.nativescript;
876+
delete rootPackageJsonData.nativescript;
873877
}
874-
this.$fs.writeJson(packageJsonPath, packageJsonData);
875-
this.$logger.info(`Migrated to ${constants.CONFIG_FILE_NAME_TS}`);
878+
879+
// save root package.json
880+
this.$fs.writeJson(rootPackageJsonPath, rootPackageJsonData);
881+
882+
// delete migrated files
883+
this.$fs.deleteFile(embeddedPackageJsonPath);
884+
this.$fs.deleteFile(legacyNsConfigPath);
885+
886+
this.$logger.info(
887+
`Migrated project to use ${constants.CONFIG_FILE_NAME_TS}`
888+
);
876889
}
877890

878891
private async migrateUnitTestRunner(

lib/controllers/update-controller-base.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ export class UpdateControllerBase {
3535
for (const folder of folders) {
3636
this.$fs.deleteDirectory(path.join(projectDir, folder));
3737

38-
const folderToCopy = path.join(backupDir, folder);
38+
const fileName = folder.replace(path.sep, "_");
39+
const folderToCopy = path.join(backupDir, fileName);
3940

4041
if (this.$fs.exists(folderToCopy)) {
41-
this.$fs.copyFile(folderToCopy, projectDir);
42+
this.$fs.copyFile(folderToCopy, path.resolve(projectDir, folder));
4243
}
4344
}
4445
}
@@ -52,9 +53,9 @@ export class UpdateControllerBase {
5253
this.$fs.createDirectory(backupDir);
5354
for (const folder of folders) {
5455
const fileName = folder.replace(path.sep, "_");
55-
const folderToCopy = path.join(projectDir, fileName);
56+
const folderToCopy = path.join(projectDir, folder);
5657
if (this.$fs.exists(folderToCopy)) {
57-
this.$fs.copyFile(folderToCopy, backupDir);
58+
this.$fs.copyFile(folderToCopy, path.resolve(backupDir, fileName));
5859
}
5960
}
6061
}

lib/services/project-config-service.ts

+64-23
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import {
1616
SupportedConfigValues,
1717
} from "../tools/config-manipulation/config-transformer";
1818
import { IBasePluginData } from "../definitions/plugins";
19-
import semver = require("semver/preload");
2019
import { injector } from "../common/yok";
2120
import { EOL } from "os";
2221
import { IOptions } from "../declarations";
22+
import semver = require("semver/preload");
2323

2424
export class ProjectConfigService implements IProjectConfigService {
2525
private _hasWarnedLegacy = false;
@@ -90,7 +90,7 @@ export default {
9090
this.$logger.debug(
9191
"the value of this.$options.argv._[0] is: " + this.$options.argv._[0]
9292
);
93-
const isMigrate = this.$options.argv._[0] === "migrate";
93+
const isMigrate = _.get(this.$options, "argv._[0]") === "migrate";
9494
if (!isMigrate) {
9595
this.$logger.warn(
9696
`You are using the deprecated ${CONFIG_NS_FILE_NAME} file. Just be aware that NativeScript 7 has an improved ${CONFIG_FILE_NAME_DISPLAY} file for when you're ready to upgrade this project.`
@@ -132,6 +132,7 @@ export default {
132132
if (usesLegacyConfig) {
133133
return this._readAndUpdateLegacyConfig(configNSConfigFilePath);
134134
}
135+
135136
if (hasTS) {
136137
const rawSource = this.$fs.readText(configTSFilePath);
137138
const transpiledSource = ts.transpileModule(rawSource, {
@@ -160,12 +161,26 @@ export default {
160161
return _.get(this.readConfig(), key);
161162
}
162163

163-
public setValue(key: string, value: SupportedConfigValues) {
164+
public setValue(key: string, value: SupportedConfigValues): boolean {
164165
const { hasTS, configJSFilePath, configTSFilePath } = this.detectInfo();
165166
const configFilePath = configTSFilePath || configJSFilePath;
167+
166168
if (!this.$fs.exists(configFilePath)) {
167169
this.writeDefaultConfig(this.projectHelper.projectDir);
168170
}
171+
172+
if (typeof value === "object") {
173+
let allSuccessful = true;
174+
175+
this.flattenObjectToPaths(value).forEach((prop) => {
176+
if (!this.setValue(prop.key, prop.value)) {
177+
allSuccessful = false;
178+
}
179+
});
180+
181+
return allSuccessful;
182+
}
183+
169184
const configContent = this.$fs.readText(configFilePath);
170185

171186
try {
@@ -192,7 +207,9 @@ export default {
192207

193208
// restore original content
194209
this.$fs.writeFile(configFilePath, configContent);
210+
return false;
195211
}
212+
return true;
196213
}
197214
}
198215

@@ -206,35 +223,40 @@ export default {
206223
}
207224

208225
private _readAndUpdateLegacyConfig(configNSConfigFilePath: string) {
209-
let configNs: any = this.$fs.readJson(configNSConfigFilePath);
210-
if (!this._handledLegacyId) {
211-
this._handledLegacyId = true;
212-
const packageJson = this.$fs.readJson(
213-
path.join(this.projectHelper.projectDir, "package.json")
214-
);
215-
if (
216-
packageJson &&
217-
packageJson.nativescript &&
218-
packageJson.nativescript.id
219-
) {
220-
configNs = {
221-
id: packageJson.nativescript.id,
222-
...configNs,
223-
};
224-
this.$fs.writeJson(configNSConfigFilePath, configNs);
225-
}
226+
let nsConfig: any = this.$fs.readJson(configNSConfigFilePath);
227+
if (this._handledLegacyId) {
228+
return;
229+
}
230+
this._handledLegacyId = true;
231+
232+
const packageJson = this.$fs.readJson(
233+
path.join(this.projectHelper.projectDir, "package.json")
234+
);
235+
236+
if (
237+
packageJson &&
238+
packageJson.nativescript &&
239+
packageJson.nativescript.id
240+
) {
241+
nsConfig = {
242+
id: packageJson.nativescript.id,
243+
...nsConfig,
244+
};
245+
this.$fs.writeJson(configNSConfigFilePath, nsConfig);
226246
}
227-
return configNs;
247+
248+
return nsConfig;
228249
}
229250

230251
public writeLegacyNSConfigIfNeeded(
231252
projectDir: string,
232253
runtimePackage: IBasePluginData
233254
) {
234-
const projectInfo = this.detectInfo(
255+
const { usesLegacyConfig } = this.detectInfo(
235256
projectDir || this.projectHelper.projectDir
236257
);
237-
if (projectInfo.usesLegacyConfig) {
258+
259+
if (usesLegacyConfig) {
238260
return;
239261
}
240262

@@ -268,6 +290,25 @@ You may add \`nsconfig.json\` to \`.gitignore\` as the CLI will regenerate it as
268290
appResourcesPath: this.getValue("appResourcesPath"),
269291
});
270292
}
293+
294+
private flattenObjectToPaths(
295+
obj: any,
296+
basePath?: string
297+
): Array<{ key: string; value: any }> {
298+
const toPath = (key: any) => [basePath, key].filter(Boolean).join(".");
299+
return Object.keys(obj).reduce((all: any, key) => {
300+
if (typeof obj[key] === "object") {
301+
return [...all, ...this.flattenObjectToPaths(obj[key], toPath(key))];
302+
}
303+
return [
304+
...all,
305+
{
306+
key: toPath(key),
307+
value: obj[key],
308+
},
309+
];
310+
}, []);
311+
}
271312
}
272313

273314
injector.register("projectConfigService", ProjectConfigService);

lib/tools/config-manipulation/config-transformer.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ import {
1717
SyntaxKind,
1818
} from "ts-morph";
1919

20-
export type SupportedConfigValues = string | number | boolean;
20+
export type SupportedConfigValues =
21+
| string
22+
| number
23+
| boolean
24+
| { [key: string]: SupportedConfigValues };
2125

2226
export interface IConfigTransformer {
2327
/**

test/controllers/update-controller.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,17 @@ describe("update controller method tests", () => {
141141
});
142142

143143
assert.isTrue(
144-
copyFileStub.calledWith(path.join(tempDir, "package.json"), projectFolder)
144+
copyFileStub.calledWith(
145+
path.join(tempDir, "package.json"),
146+
path.resolve(projectFolder, "package.json")
147+
)
145148
);
146149
for (const folder of UpdateController.folders) {
147150
assert.isTrue(
148-
copyFileStub.calledWith(path.join(tempDir, folder), projectFolder)
151+
copyFileStub.calledWith(
152+
path.join(tempDir, folder),
153+
path.resolve(projectFolder, folder)
154+
)
149155
);
150156
}
151157
});

0 commit comments

Comments
 (0)