Skip to content

Commit 0254825

Browse files
feat: Add after-createProject hook
Add `after-createProject` hook, so template authors will be able to execute different operations based on their requirements. The hook is called at the end of the project creation step. Also remove usage of `$projectData` and use `$projectDataService` in order to ensure correct behavior in long living process
1 parent 72c7f22 commit 0254825

File tree

4 files changed

+61
-27
lines changed

4 files changed

+61
-27
lines changed

lib/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,7 @@ export class TemplateVersions {
201201
export class ProjectTemplateErrors {
202202
public static InvalidTemplateVersionStringFormat = "The template '%s' has a NativeScript version '%s' that is not supported. Unable to create project from it.";
203203
}
204+
205+
export class Hooks {
206+
public static createProject = "createProject";
207+
}

lib/definitions/project.d.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
/**
2-
* Describes available settings when creating new NativeScript application.
3-
*/
4-
interface IProjectSettings {
1+
interface IProjectName {
2+
projectName: string;
3+
}
4+
5+
interface IProjectSettingsBase extends IProjectName {
56
/**
67
* Name of the newly created application.
78
*/
89
projectName: string;
910

11+
/**
12+
* Defines whether the `npm install` command should be executed with `--ignore-scripts` option.
13+
* When it is passed, all scripts (postinstall for example) will not be executed.
14+
*/
15+
ignoreScripts?: boolean;
16+
1017
/**
1118
* Selected template from which to create the project. If not specified, defaults to hello-world template.
1219
* Template can be any npm package, local dir, github url, .tgz file.
@@ -19,7 +26,19 @@ interface IProjectSettings {
1926
* Application identifier for the newly created application. If not specified, defaults to org.nativescript.<projectName>.
2027
*/
2128
appId?: string;
29+
}
2230

31+
/**
32+
* Describes information passed to project creation hook (createProject).
33+
*/
34+
interface IProjectCreationSettings extends IProjectSettingsBase, IProjectDir {
35+
36+
}
37+
38+
/**
39+
* Describes available settings when creating new NativeScript application.
40+
*/
41+
interface IProjectSettings extends IProjectSettingsBase {
2342
/**
2443
* Path where the project will be created. If not specified, defaults to current working dir.
2544
*/
@@ -29,17 +48,8 @@ interface IProjectSettings {
2948
* Defines if invalid application name can be used for project creation.
3049
*/
3150
force?: boolean;
32-
33-
/**
34-
* Defines whether the `npm install` command should be executed with `--ignore-scripts` option.
35-
* When it is passed, all scripts (postinstall for example) will not be executed.
36-
*/
37-
ignoreScripts?: boolean;
3851
}
3952

40-
interface IProjectName {
41-
projectName: string;
42-
}
4353

4454
interface ICreateProjectData extends IProjectDir, IProjectName {
4555

lib/services/project-service.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import * as constants from "../constants";
22
import * as path from "path";
33
import * as shelljs from "shelljs";
44
import { exported } from "../common/decorators";
5+
import { Hooks } from "../constants";
56

67
export class ProjectService implements IProjectService {
78

8-
constructor(private $npm: INodePackageManager,
9+
constructor(private $hooksService: IHooksService,
10+
private $npm: INodePackageManager,
911
private $errors: IErrors,
1012
private $fs: IFileSystem,
1113
private $logger: ILogger,
12-
private $projectData: IProjectData,
1314
private $projectDataService: IProjectDataService,
1415
private $projectHelper: IProjectHelper,
1516
private $projectNameService: IProjectNameService,
@@ -37,16 +38,25 @@ export class ProjectService implements IProjectService {
3738
this.$errors.fail("Path already exists and is not empty %s", projectDir);
3839
}
3940

40-
const projectId = projectOptions.appId || this.$projectHelper.generateDefaultAppId(projectName, constants.DEFAULT_APP_IDENTIFIER_PREFIX);
41-
this.createPackageJson(projectDir, projectId);
42-
43-
this.$logger.trace(`Creating a new NativeScript project with name ${projectName} and id ${projectId} at location ${projectDir}`);
41+
const appId = projectOptions.appId || this.$projectHelper.generateDefaultAppId(projectName, constants.DEFAULT_APP_IDENTIFIER_PREFIX);
42+
this.createPackageJson(projectDir, appId);
43+
this.$logger.trace(`Creating a new NativeScript project with name ${projectName} and id ${appId} at location ${projectDir}`);
4444
if (!selectedTemplate) {
4545
selectedTemplate = constants.RESERVED_TEMPLATE_NAMES["default"];
4646
}
4747

48+
const projectCreationData = await this.createProjectCore({ template: selectedTemplate, projectDir, ignoreScripts: projectOptions.ignoreScripts, appId: appId, projectName });
49+
50+
this.$logger.printMarkdown("Project `%s` was successfully created.", projectCreationData.projectName);
51+
52+
return projectCreationData;
53+
}
54+
55+
private async createProjectCore(projectCreationSettings: IProjectCreationSettings): Promise<ICreateProjectData> {
56+
const { template, projectDir, appId, projectName, ignoreScripts } = projectCreationSettings;
57+
4858
try {
49-
const { templatePath, templateVersion } = await this.$projectTemplatesService.prepareTemplate(selectedTemplate, projectDir);
59+
const { templatePath, templateVersion } = await this.$projectTemplatesService.prepareTemplate(template, projectDir);
5060
await this.extractTemplate(projectDir, templatePath, templateVersion);
5161

5262
await this.ensureAppResourcesExist(projectDir);
@@ -68,19 +78,22 @@ export class ProjectService implements IProjectService {
6878
await this.$npm.install(projectDir, projectDir, {
6979
disableNpmInstall: false,
7080
frameworkPath: null,
71-
ignoreScripts: projectOptions.ignoreScripts
81+
ignoreScripts
7282
});
7383

7484
await this.$npm.uninstall(templatePackageJson.name, { save: true }, projectDir);
7585
if (templateVersion === constants.TemplateVersions.v2) {
76-
this.alterPackageJsonData(projectDir, projectId);
86+
this.alterPackageJsonData(projectDir, appId);
7787
}
7888
} catch (err) {
7989
this.$fs.deleteDirectory(projectDir);
8090
throw err;
8191
}
8292

83-
this.$logger.printMarkdown("Project `%s` was successfully created.", projectName);
93+
this.$hooksService.executeAfterHooks(Hooks.createProject, {
94+
hookArgs: projectCreationSettings
95+
});
96+
8497
return { projectName, projectDir };
8598
}
8699

@@ -114,7 +127,8 @@ export class ProjectService implements IProjectService {
114127
let destinationDir = "";
115128
switch (templateVersion) {
116129
case constants.TemplateVersions.v1:
117-
const appDestinationPath = this.$projectData.getAppDirectoryPath(projectDir);
130+
const projectData = this.$projectDataService.getProjectData(projectDir);
131+
const appDestinationPath = projectData.getAppDirectoryPath(projectDir);
118132
this.$fs.createDirectory(appDestinationPath);
119133
destinationDir = appDestinationPath;
120134
break;
@@ -131,8 +145,9 @@ export class ProjectService implements IProjectService {
131145
}
132146

133147
private async ensureAppResourcesExist(projectDir: string): Promise<void> {
134-
const appPath = this.$projectData.getAppDirectoryPath(projectDir);
135-
const appResourcesDestinationPath = this.$projectData.getAppResourcesDirectoryPath(projectDir);
148+
const projectData = this.$projectDataService.getProjectData(projectDir);
149+
const appPath = projectData.getAppDirectoryPath(projectDir);
150+
const appResourcesDestinationPath = projectData.getAppResourcesDirectoryPath(projectDir);
136151

137152
if (!this.$fs.exists(appResourcesDestinationPath)) {
138153
this.$fs.createDirectory(appResourcesDestinationPath);
@@ -172,7 +187,8 @@ export class ProjectService implements IProjectService {
172187
}
173188

174189
private removeMergedDependencies(projectDir: string, templatePackageJsonData: any): void {
175-
const extractedTemplatePackageJsonPath = path.join(this.$projectData.getAppDirectoryPath(projectDir), constants.PACKAGE_JSON_FILE_NAME);
190+
const appDirectoryPath = this.$projectDataService.getProjectData(projectDir).appDirectoryPath;
191+
const extractedTemplatePackageJsonPath = path.join(appDirectoryPath, constants.PACKAGE_JSON_FILE_NAME);
176192
for (const key in templatePackageJsonData) {
177193
if (constants.PackageJsonKeysToKeep.indexOf(key) === -1) {
178194
delete templatePackageJsonData[key];

test/stubs.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,10 @@ export class ProjectTemplatesService implements IProjectTemplatesService {
478478
async prepareTemplate(templateName: string): Promise<ITemplateData> {
479479
return Promise.resolve(<any>{});
480480
}
481+
482+
getTemplateVersion(templatePath: string): string {
483+
return constants.TemplateVersions.v1;
484+
}
481485
}
482486

483487
export class HooksServiceStub implements IHooksService {

0 commit comments

Comments
 (0)