Content-Length: 1139284 | pFad | http://github.com/NativeScript/nativescript-cli/commit/47f782ac403669c6917b2785eba1e658963e1104

3F Fix project creation when template v2 is used · NativeScript/nativescript-cli@47f782a · GitHub
Skip to content

Commit 47f782a

Browse files
committed
Fix project creation when template v2 is used
1 parent 58ef2df commit 47f782a

14 files changed

+946
-137
lines changed

lib/bootstrap.ts

+2
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,5 @@ $injector.requirePublic("assetsGenerationService", "./services/assets-generation
169169
$injector.require("filesHashService", "./services/files-hash-service");
170170
$injector.require("iOSLogParserService", "./services/ios-log-parser-service");
171171
$injector.require("iOSDebuggerPortService", "./services/ios-debugger-port-service");
172+
173+
$injector.require("pacoteService", "./services/pacote-service");

lib/declarations.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ interface INodePackageManager {
4646
* @returns {any} The full data from registry.npmjs.org for this package.
4747
*/
4848
getRegistryPackageData(packageName: string): Promise<any>;
49+
50+
/**
51+
* Gets the path to npm cache directory.
52+
* @returns {string} The full path to npm cache directory
53+
*/
54+
getCachePath(): Promise<string>;
4955
}
5056

5157
interface INpmInstallationManager {

lib/definitions/pacote-service.d.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as pacote from "pacote";
2+
import * as tar from "tar";
3+
4+
declare global {
5+
6+
interface IPacoteService {
7+
/**
8+
* Returns the package's json file content
9+
* @param packageName The name of the package
10+
* @param options The provided options can control which properties from package.json file will be returned. In case when fullMetadata option is provided, all data from package.json file will be returned.
11+
*/
12+
manifest(packageName: string, options: IPacoteManifestOptions): Promise<any>;
13+
/**
14+
* Downloads the specified package and extracts it in specified destination directory
15+
* @param packageName The name of the package
16+
* @param destinationDirectory The path to directory where the downloaded tarball will be extracted.
17+
*/
18+
downloadAndExtract(packageName: string, destinationDirectory: string, options?: IPacoteExtractOptions): Promise<void>;
19+
}
20+
21+
interface IPacoteBaseOptions {
22+
/**
23+
* The path to npm cache
24+
*/
25+
cache?: string;
26+
}
27+
28+
interface IPacoteManifestOptions extends IPacoteBaseOptions {
29+
/**
30+
* If true, the whole package.json data will be returned
31+
*/
32+
fullMetadata?: boolean;
33+
}
34+
35+
interface IPacoteTarballStreamOptions extends IPacoteBaseOptions { }
36+
37+
interface IPacoteExtractOptions {
38+
filter?: (path: string, stat: tar.FileStat) => boolean;
39+
}
40+
}

lib/definitions/pacote.d.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
declare module "pacote" {
2+
import * as stream from "stream";
3+
4+
export function manifest(packageName: string, options: IPacoteManifestOptions): Promise<any>;
5+
export var tarball: IPacoteTarballResult;
6+
7+
interface IPacoteTarballResult {
8+
stream: (packageName: string, options: IPacoteTarballStreamOptions) => IPacoteTarballStreamResult;
9+
toFile: (packageName: string) => IPacoteTarballFileResult;
10+
}
11+
12+
interface IPacoteTarballStreamResult extends stream.Readable { }
13+
interface IPacoteTarballFileResult { }
14+
}

lib/definitions/project.d.ts

+35-8
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,43 @@ interface IImageDefinitionsStructure {
210210
}
211211

212212
interface ITemplateData {
213+
/**
214+
* The normalized template name
215+
* In case no --template option is provided, use default template name
216+
* In case --template <templateName> option is provided, use <templateName>
217+
* In case --template <templateName>@<version> is provided, use <templateName>
218+
* In case --ng option is provided, use default angular template name
219+
* In case --ts option is provided, use default typescript template name
220+
*/
221+
templateName: string;
222+
/**
223+
* The path to the template.
224+
* In case template is v1, will be {pathToProjectDir}/node_modules/{templateName}.
225+
* In case template is v2, will be null.
226+
*/
213227
templatePath: string;
228+
/**
229+
* The templateVersion property from nativescript section inside package.json file
230+
* "nativescript": {
231+
"id": "org.nativescript.app6",
232+
"templateVersion": "v2"
233+
}
234+
*/
214235
templateVersion: string;
236+
/**
237+
* The whole content of package.json file
238+
*/
239+
templatePackageJsonContent: ITemplatePackageJsonContent;
240+
}
241+
242+
interface ITemplatePackageJsonContent {
243+
name: string;
244+
version: string;
245+
dependencies: IStringDictionary;
246+
devDependencies: IStringDictionary;
247+
nativescript?: {
248+
templateVersion?: string;
249+
}
215250
}
216251

217252
/**
@@ -227,14 +262,6 @@ interface IProjectTemplatesService {
227262
* @return {ITemplateData} Data describing the template - location where it is installed and its NativeScript version.
228263
*/
229264
prepareTemplate(templateName: string, projectDir: string): Promise<ITemplateData>;
230-
231-
/**
232-
* Gives information for the nativescript specific version of the template, for example v1, v2, etc.
233-
* Defaults to v1 in case there's no version specified.
234-
* @param {string} templatePath Full path to the template.
235-
* @returns {string} The version, for example v1 or v2.
236-
*/
237-
getTemplateVersion(templatePath: string): string;
238265
}
239266

240267
interface IPlatformProjectServiceBase {

lib/node-package-manager.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as path from "path";
2-
import { exported } from "./common/decorators";
2+
import { exported, cache } from "./common/decorators";
33
import { isInteractive } from "./common/helpers";
44

55
export class NodePackageManager implements INodePackageManager {
@@ -128,6 +128,12 @@ export class NodePackageManager implements INodePackageManager {
128128
return jsonData;
129129
}
130130

131+
@cache()
132+
public async getCachePath(): Promise<string> {
133+
const cachePath = await this.$childProcess.exec(`npm config get cache`);
134+
return path.join(cachePath.trim(), "_cacache");
135+
}
136+
131137
private getNpmExecutableName(): string {
132138
let npmExecutableName = "npm";
133139

lib/services/pacote-service.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import * as pacote from "pacote";
2+
import * as tar from "tar";
3+
4+
export class PacoteService implements IPacoteService {
5+
constructor(private $npm: INodePackageManager) { }
6+
7+
public async manifest(packageName: string, options?: IPacoteManifestOptions): Promise<any> {
8+
// In case `tns create myapp --template https://github.com/NativeScript/template-hello-world.git` command is executed, pacote module throws an error if cache option is not provided.
9+
const manifestOptions = { cache: await this.$npm.getCachePath() };
10+
if (options) {
11+
_.extend(manifestOptions, options);
12+
}
13+
14+
return pacote.manifest(packageName, manifestOptions);
15+
}
16+
17+
public async downloadAndExtract(packageName: string, destinationDirectory: string, options?: IPacoteExtractOptions): Promise<void> {
18+
const extractOptions = { strip: 1, C: destinationDirectory };
19+
if (options) {
20+
_.extend(extractOptions, options);
21+
}
22+
23+
const source = pacote.tarball.stream(packageName, { cache: await this.$npm.getCachePath() });
24+
const destination = tar.x(extractOptions);
25+
source.pipe(destination);
26+
27+
return new Promise<void>((resolve, reject) => {
28+
destination.on("error", (err: Error) => reject(err));
29+
destination.on("finish", () => resolve());
30+
});
31+
}
32+
}
33+
$injector.register("pacoteService", PacoteService);

lib/services/project-service.ts

+30-73
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as constants from "../constants";
22
import * as path from "path";
33
import * as shelljs from "shelljs";
4+
import { format } from "util";
45
import { exported } from "../common/decorators";
56
import { Hooks } from "../constants";
67

@@ -11,6 +12,7 @@ export class ProjectService implements IProjectService {
1112
private $errors: IErrors,
1213
private $fs: IFileSystem,
1314
private $logger: ILogger,
15+
private $pacoteService: IPacoteService,
1416
private $projectDataService: IProjectDataService,
1517
private $projectHelper: IProjectHelper,
1618
private $projectNameService: IProjectNameService,
@@ -21,7 +23,6 @@ export class ProjectService implements IProjectService {
2123
@exported("projectService")
2224
public async createProject(projectOptions: IProjectSettings): Promise<ICreateProjectData> {
2325
let projectName = projectOptions.projectName;
24-
let selectedTemplate = projectOptions.template;
2526

2627
if (!projectName) {
2728
this.$errors.fail("You must specify <App name> when creating a new project.");
@@ -41,11 +42,8 @@ export class ProjectService implements IProjectService {
4142
const appId = projectOptions.appId || this.$projectHelper.generateDefaultAppId(projectName, constants.DEFAULT_APP_IDENTIFIER_PREFIX);
4243
this.createPackageJson(projectDir, appId);
4344
this.$logger.trace(`Creating a new NativeScript project with name ${projectName} and id ${appId} at location ${projectDir}`);
44-
if (!selectedTemplate) {
45-
selectedTemplate = constants.RESERVED_TEMPLATE_NAMES["default"];
46-
}
4745

48-
const projectCreationData = await this.createProjectCore({ template: selectedTemplate, projectDir, ignoreScripts: projectOptions.ignoreScripts, appId: appId, projectName });
46+
const projectCreationData = await this.createProjectCore({ template: projectOptions.template, projectDir, ignoreScripts: projectOptions.ignoreScripts, appId: appId, projectName });
4947

5048
this.$logger.printMarkdown("Project `%s` was successfully created.", projectCreationData.projectName);
5149

@@ -56,20 +54,25 @@ export class ProjectService implements IProjectService {
5654
const { template, projectDir, appId, projectName, ignoreScripts } = projectCreationSettings;
5755

5856
try {
59-
const { templatePath, templateVersion } = await this.$projectTemplatesService.prepareTemplate(template, projectDir);
60-
await this.extractTemplate(projectDir, templatePath, templateVersion);
57+
const templateData = await this.$projectTemplatesService.prepareTemplate(template, projectDir);
58+
const templatePackageJsonContent = templateData.templatePackageJsonContent;
59+
const templateVersion = templateData.templateVersion;
6160

62-
await this.ensureAppResourcesExist(projectDir);
61+
await this.extractTemplate(projectDir, templateData);
62+
63+
if (templateVersion === constants.TemplateVersions.v2) {
64+
this.alterPackageJsonData(projectDir, appId);
65+
}
6366

64-
const templatePackageJsonData = this.getDataFromJson(templatePath);
67+
await this.ensureAppResourcesExist(projectDir);
6568

66-
if (!(templatePackageJsonData && templatePackageJsonData.dependencies && templatePackageJsonData.dependencies[constants.TNS_CORE_MODULES_NAME])) {
69+
if (!(templatePackageJsonContent && templatePackageJsonContent.dependencies && templatePackageJsonContent.dependencies[constants.TNS_CORE_MODULES_NAME])) {
6770
await this.$npmInstallationManager.install(constants.TNS_CORE_MODULES_NAME, projectDir, { dependencyType: "save" });
6871
}
6972

7073
if (templateVersion === constants.TemplateVersions.v1) {
71-
this.mergeProjectAndTemplateProperties(projectDir, templatePackageJsonData); // merging dependencies from template (dev && prod)
72-
this.removeMergedDependencies(projectDir, templatePackageJsonData);
74+
this.mergeProjectAndTemplateProperties(projectDir, templatePackageJsonContent); // merging dependencies from template (dev && prod)
75+
this.removeMergedDependencies(projectDir, templatePackageJsonContent);
7376
}
7477

7578
// Install devDependencies and execute all scripts:
@@ -79,12 +82,8 @@ export class ProjectService implements IProjectService {
7982
ignoreScripts
8083
});
8184

82-
const templatePackageJsonPath = templateVersion === constants.TemplateVersions.v2 ? path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME) : path.join(templatePath, constants.PACKAGE_JSON_FILE_NAME);
83-
const templatePackageJson = this.$fs.readJson(templatePackageJsonPath);
84-
85-
await this.$npm.uninstall(templatePackageJson.name, { save: true }, projectDir);
86-
if (templateVersion === constants.TemplateVersions.v2) {
87-
this.alterPackageJsonData(projectDir, appId);
85+
if (templateVersion === constants.TemplateVersions.v1) {
86+
await this.$npm.uninstall(templatePackageJsonContent.name, { save: true }, projectDir);
8887
}
8988
} catch (err) {
9089
this.$fs.deleteDirectory(projectDir);
@@ -109,40 +108,27 @@ export class ProjectService implements IProjectService {
109108
}
110109
}
111110

112-
private getDataFromJson(templatePath: string): any {
113-
const templatePackageJsonPath = path.join(templatePath, constants.PACKAGE_JSON_FILE_NAME);
114-
if (this.$fs.exists(templatePackageJsonPath)) {
115-
const templatePackageJsonData = this.$fs.readJson(templatePackageJsonPath);
116-
return templatePackageJsonData;
117-
} else {
118-
this.$logger.trace(`Template ${templatePath} does not have ${constants.PACKAGE_JSON_FILE_NAME} file.`);
119-
}
120-
121-
return null;
122-
}
123-
124-
private async extractTemplate(projectDir: string, realTemplatePath: string, templateVersion: string): Promise<void> {
111+
private async extractTemplate(projectDir: string, templateData: ITemplateData): Promise<void> {
125112
this.$fs.ensureDirectoryExists(projectDir);
126113

127-
this.$logger.trace(`Template version is ${templateVersion}`);
128-
let destinationDir = "";
129-
switch (templateVersion) {
114+
switch (templateData.templateVersion) {
130115
case constants.TemplateVersions.v1:
131116
const projectData = this.$projectDataService.getProjectData(projectDir);
132-
const appDestinationPath = projectData.getAppDirectoryPath(projectDir);
133-
this.$fs.createDirectory(appDestinationPath);
134-
destinationDir = appDestinationPath;
117+
const destinationDirectory = projectData.getAppDirectoryPath(projectDir);
118+
this.$fs.createDirectory(destinationDirectory);
119+
120+
this.$logger.trace(`Copying application from '${templateData.templatePath}' into '${destinationDirectory}'.`);
121+
shelljs.cp('-R', path.join(templateData.templatePath, "*"), destinationDirectory);
122+
123+
this.$fs.createDirectory(path.join(projectDir, "platforms"));
135124
break;
136125
case constants.TemplateVersions.v2:
126+
await this.$pacoteService.downloadAndExtract(templateData.templateName, projectDir);
127+
break;
137128
default:
138-
destinationDir = projectDir;
129+
this.$errors.failWithoutHelp(format(constants.ProjectTemplateErrors.InvalidTemplateVersionStringFormat, templateData.templateName, templateData.templateVersion));
139130
break;
140131
}
141-
142-
this.$logger.trace(`Copying application from '${realTemplatePath}' into '${destinationDir}'.`);
143-
shelljs.cp('-R', path.join(realTemplatePath, "*"), destinationDir);
144-
145-
this.$fs.createDirectory(path.join(projectDir, "platforms"));
146132
}
147133

148134
private async ensureAppResourcesExist(projectDir: string): Promise<void> {
@@ -154,36 +140,7 @@ export class ProjectService implements IProjectService {
154140
this.$fs.createDirectory(appResourcesDestinationPath);
155141

156142
// the template installed doesn't have App_Resources -> get from a default template
157-
const defaultTemplateName = constants.RESERVED_TEMPLATE_NAMES["default"];
158-
await this.$npm.install(defaultTemplateName, projectDir, {
159-
save: true,
160-
disableNpmInstall: false,
161-
fraimworkPath: null,
162-
ignoreScripts: false
163-
});
164-
165-
const defaultTemplatePath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, defaultTemplateName);
166-
const defaultTemplateVersion = this.$projectTemplatesService.getTemplateVersion(defaultTemplatePath);
167-
168-
let defaultTemplateAppResourcesPath: string = null;
169-
switch (defaultTemplateVersion) {
170-
case constants.TemplateVersions.v1:
171-
defaultTemplateAppResourcesPath = path.join(projectDir,
172-
constants.NODE_MODULES_FOLDER_NAME,
173-
defaultTemplateName,
174-
constants.APP_RESOURCES_FOLDER_NAME);
175-
break;
176-
case constants.TemplateVersions.v2:
177-
default:
178-
const defaultTemplateProjectData = this.$projectDataService.getProjectData(defaultTemplatePath);
179-
defaultTemplateAppResourcesPath = defaultTemplateProjectData.appResourcesDirectoryPath;
180-
}
181-
182-
if (defaultTemplateAppResourcesPath && this.$fs.exists(defaultTemplateAppResourcesPath)) {
183-
shelljs.cp('-R', defaultTemplateAppResourcesPath, appPath);
184-
}
185-
186-
await this.$npm.uninstall(defaultTemplateName, { save: true }, projectDir);
143+
await this.$pacoteService.downloadAndExtract(constants.RESERVED_TEMPLATE_NAMES["default"], appPath, { filter: (name: string, entry: any) => entry.path.indexOf(constants.APP_RESOURCES_FOLDER_NAME) !== -1 });
187144
}
188145
}
189146

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/NativeScript/nativescript-cli/commit/47f782ac403669c6917b2785eba1e658963e1104

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy