Skip to content

Commit 54c624d

Browse files
authored
feat(generators): xplat-helper for convenient platform tooling (#83)
1 parent 1ed61f9 commit 54c624d

File tree

13 files changed

+465
-5
lines changed

13 files changed

+465
-5
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nstudio/schematics",
3-
"version": "7.2.9",
3+
"version": "7.3.0",
44
"description": "Cross-platform (xplat) tools for Nx workspaces.",
55
"readmeFilename": "README.md",
66
"scripts": {

src/collection.json

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
"schema": "./xplat/schema.json",
4040
"description": "Add xplat development setup for optimal code reuse."
4141
},
42+
"xplat-helper": {
43+
"factory": "./xplat-helper",
44+
"schema": "./xplat-helper/schema.json",
45+
"description": "xplat helpers for various tooling extras."
46+
},
4247
"app": {
4348
"factory": "./app.web",
4449
"schema": "./app.web/schema.json",

src/utils/errors.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { supportedPlatforms } from './general';
1+
import { supportedPlatforms, supportedHelpers } from './general';
22

33
export const errorMissingPrefix = `Missing --prefix flag. It's a good practice to specify a 2-3 character prefix for use with your project's component selectors and certain shared class/module names. Example: ng g xplat --prefix=foo`;
44

@@ -8,6 +8,10 @@ export function unsupportedPlatformError(platform: string) {
88
return `${platform} is not a supported platform. Currently supported: ${supportedPlatforms}`
99
}
1010

11+
export function unsupportedHelperError(helper: string) {
12+
return `${helper} is not a supported helper. Currently supported: ${supportedHelpers}`
13+
}
14+
1115
export function noPlatformError() {
1216
return `You must specify which platforms you wish to generate support for. For example: ng g xplat --prefix=foo --platforms=${supportedPlatforms.join(',')}`
1317
}

src/utils/general.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ export interface NodeDependency {
5353
type: "dependency" | "devDependency";
5454
}
5555

56+
// list of all supported helpers
57+
// TODO: add more convenient helpers (like firebase or Travis ci support files)
58+
export const supportedHelpers = ['imports'];
59+
5660
let npmScope: string;
5761
// selector prefix to use when generating various boilerplate for xplat support
5862
let prefix: string;
@@ -718,9 +722,10 @@ export function updatePackageForNgrx(
718722
export function updateTsConfig(
719723
tree: Tree,
720724
callback: (data: any) => void,
721-
targetSuffix: string = ""
725+
targetSuffix: string = '',
726+
prefixPath: string = ''
722727
) {
723-
const tsConfigPath = `tsconfig${targetSuffix ? "." + targetSuffix : ""}.json`;
728+
const tsConfigPath = `${prefixPath}tsconfig${targetSuffix ? "." + targetSuffix : ""}.json`;
724729
const tsConfig = getJsonFromFile(tree, tsConfigPath);
725730
callback(tsConfig);
726731
return updateJsonFile(tree, tsConfigPath, tsConfig);

src/xplat-helper/index.ts

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import {
2+
apply,
3+
chain,
4+
Tree,
5+
Rule,
6+
url,
7+
move,
8+
template,
9+
mergeWith,
10+
branchAndMerge,
11+
SchematicContext,
12+
SchematicsException,
13+
schematic,
14+
noop
15+
} from "@angular-devkit/schematics";
16+
import {
17+
stringUtils,
18+
prerun,
19+
getNpmScope,
20+
getPrefix,
21+
addRootDeps,
22+
updatePackageScripts,
23+
updateAngularProjects,
24+
updateNxProjects,
25+
applyAppNamingConvention,
26+
getGroupByName,
27+
getAppName,
28+
ITargetPlatforms,
29+
PlatformTypes,
30+
supportedPlatforms,
31+
unsupportedPlatformError,
32+
supportedHelpers,
33+
unsupportedHelperError,
34+
updateTsConfig
35+
} from "../utils";
36+
import { Schema as HelperOptions } from "./schema";
37+
38+
let helpers: Array<string> = [];
39+
let platforms: Array<string> = [];
40+
export default function(options: HelperOptions) {
41+
if (!options.name) {
42+
throw new SchematicsException(
43+
`Missing name argument. Provide a comma delimited list of helpers to generate. Example: ng g xplat-helper imports`
44+
);
45+
}
46+
if (!options.platforms) {
47+
throw new SchematicsException(
48+
`Missing platforms argument. Example: ng g xplat-helper imports --platforms=nativescript`
49+
);
50+
}
51+
52+
helpers = options.name.split(",");
53+
platforms = options.platforms.split(",");
54+
55+
const helperChains = [];
56+
57+
for (const platform of platforms) {
58+
if (supportedPlatforms.includes(platform)) {
59+
for (const helper of helpers) {
60+
if (supportedHelpers.includes(helper)) {
61+
const moveTo = getMoveTo(<PlatformTypes>platform, helper);
62+
helperChains.push((tree: Tree, context: SchematicContext) => {
63+
return addHelperFiles(
64+
options,
65+
<PlatformTypes>platform,
66+
helper,
67+
moveTo
68+
)(tree, context);
69+
});
70+
// aside from adding files above, process any additional modifications
71+
helperChains.push((tree: Tree, context: SchematicContext) => {
72+
return processSupportingFiles(
73+
helperChains,
74+
options,
75+
<PlatformTypes>platform,
76+
helper
77+
);
78+
});
79+
} else {
80+
throw new SchematicsException(unsupportedHelperError(helper));
81+
}
82+
}
83+
} else {
84+
throw new SchematicsException(unsupportedPlatformError(platform));
85+
}
86+
}
87+
88+
return chain([
89+
prerun(options),
90+
// add helper chains
91+
...helperChains
92+
// TODO: add refactor code to update per the helper where applicable
93+
]);
94+
}
95+
96+
function addHelperFiles(
97+
options: HelperOptions,
98+
platform: PlatformTypes,
99+
helper: string,
100+
moveTo: string
101+
): Rule {
102+
return branchAndMerge(
103+
mergeWith(
104+
apply(url(`./${platform}/${helper}/_files`), [
105+
template({
106+
...(options as any),
107+
utils: stringUtils,
108+
npmScope: getNpmScope(),
109+
prefix: getPrefix(),
110+
dot: "."
111+
}),
112+
move(moveTo)
113+
])
114+
)
115+
);
116+
}
117+
118+
function getMoveTo(platform: PlatformTypes, helper: string) {
119+
let moveTo = `xplat/${platform}/utils`; // default
120+
// TODO: define custom moveTo locations for various helpers
121+
// switch (helper) {
122+
// case "imports":
123+
// break;
124+
// }
125+
return moveTo;
126+
}
127+
128+
function processSupportingFiles(
129+
helperChains: Array<any>,
130+
options: HelperOptions,
131+
platform: PlatformTypes,
132+
helper: string
133+
) {
134+
return (tree: Tree) => {
135+
switch (helper) {
136+
case "imports":
137+
switch (platform) {
138+
case "nativescript":
139+
let pathRef = `xplat/nativescript/utils/@nativescript/*`;
140+
// update root tsconfig
141+
helperChains.push(updateTsConfig(tree, (tsConfig: any) => {
142+
const updates: any = {};
143+
updates[`@nativescript/*`] = [
144+
pathRef
145+
];
146+
if (tsConfig) {
147+
if (!tsConfig.compilerOptions) {
148+
tsConfig.compilerOptions = {};
149+
}
150+
tsConfig.compilerOptions.paths = {
151+
...(tsConfig.compilerOptions.paths || {}),
152+
...updates
153+
};
154+
}
155+
}));
156+
157+
// update all {N} app tsconfig's
158+
const appsDir = tree.getDir("apps");
159+
const appFolders = appsDir.subdirs;
160+
pathRef = `../../${pathRef}`;
161+
162+
// update {N} apps and configs
163+
for (const dir of appFolders) {
164+
// console.log(dir);
165+
if (dir.indexOf('nativescript-') === 0 || dir.indexOf('-nativescript') === 0) {
166+
const appDir = `${appsDir.path}/${dir}`;
167+
// console.log('appDir:', appDir);
168+
169+
helperChains.push(updateTsConfig(tree, (tsConfig: any) => {
170+
const updates: any = {};
171+
updates[`@nativescript/*`] = [
172+
pathRef
173+
];
174+
if (tsConfig) {
175+
if (!tsConfig.compilerOptions) {
176+
tsConfig.compilerOptions = {};
177+
}
178+
tsConfig.compilerOptions.paths = {
179+
...(tsConfig.compilerOptions.paths || {}),
180+
...updates
181+
};
182+
}
183+
}, null, `${appDir}/`));
184+
}
185+
}
186+
break;
187+
}
188+
break;
189+
}
190+
};
191+
}

src/xplat-helper/index_spec.ts

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { Tree, VirtualTree } from '@angular-devkit/schematics';
2+
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
3+
import { getFileContent } from '@schematics/angular/utility/test';
4+
import * as path from 'path';
5+
6+
import { Schema as AppOptions } from '../app.nativescript/schema';
7+
import { Schema as XPlatOptions } from '../xplat/schema';
8+
import { Schema as HelperOptions } from './schema';
9+
import { stringUtils, isInModuleMetadata, createEmptyWorkspace } from '../utils';
10+
11+
describe('xplat-helper schematic', () => {
12+
const schematicRunner = new SchematicTestRunner(
13+
'@nstudio/schematics',
14+
path.join(__dirname, '../collection.json'),
15+
);
16+
const defaultOptions: HelperOptions = {
17+
name: 'imports',
18+
platforms: 'nativescript'
19+
};
20+
21+
let appTree: Tree;
22+
23+
beforeEach(() => {
24+
appTree = new VirtualTree();
25+
appTree = createEmptyWorkspace(appTree);
26+
});
27+
28+
it('should create all files for the helper', () => {
29+
const optionsXplat: XPlatOptions = {
30+
npmScope: 'testing',
31+
prefix: 'tt',
32+
platforms: 'web,nativescript'
33+
};
34+
35+
appTree = schematicRunner.runSchematic('xplat', optionsXplat, appTree);
36+
const appOptions: AppOptions = {
37+
name: 'foo',
38+
npmScope: 'testing',
39+
sample: true,
40+
prefix: 'tt', // foo test
41+
};
42+
// console.log('appTree:', appTree);
43+
appTree = schematicRunner.runSchematic('app.nativescript', appOptions, appTree);
44+
45+
const options: HelperOptions = { ...defaultOptions };
46+
// console.log('appTree:', appTree);
47+
const tree = schematicRunner.runSchematic('xplat-helper', options, appTree);
48+
const files = tree.files;
49+
// console.log(files);
50+
51+
// xplat helpers
52+
expect(files.indexOf('/xplat/nativescript/utils/@nativescript/core.ts')).toBeGreaterThanOrEqual(0);
53+
expect(files.indexOf('/xplat/nativescript/utils/@nativescript/ui.ts')).toBeGreaterThanOrEqual(0);
54+
expect(files.indexOf('/xplat/nativescript/utils/@nativescript/angular/core.ts')).toBeGreaterThanOrEqual(0);
55+
56+
// should update tsconfig files
57+
let filePath = '/tsconfig.json';
58+
let fileContent = JSON.parse(getFileContent(tree, filePath));
59+
// console.log(fileContent);
60+
expect(fileContent.compilerOptions.paths['@nativescript/*'][0]).toBe('xplat/nativescript/utils/@nativescript/*');
61+
62+
filePath = '/apps/nativescript-foo/tsconfig.json';
63+
fileContent = JSON.parse(getFileContent(tree, filePath));
64+
// console.log(fileContent);
65+
expect(fileContent.compilerOptions.paths['@nativescript/*'][0]).toBe('../../xplat/nativescript/utils/@nativescript/*');
66+
});
67+
68+
it('generating helper for a platform where the helper is not supported should not do anything', () => {
69+
const optionsXplat: XPlatOptions = {
70+
npmScope: 'testing',
71+
prefix: 'tt',
72+
platforms: 'web,nativescript'
73+
};
74+
75+
appTree = schematicRunner.runSchematic('xplat', optionsXplat, appTree);
76+
const options: HelperOptions = {
77+
name: 'imports',
78+
platforms: 'web'
79+
};
80+
// console.log('appTree:', appTree);
81+
const tree = schematicRunner.runSchematic('xplat-helper', options, appTree);
82+
const files = tree.files;
83+
// console.log(files);
84+
85+
// xplat helpers
86+
expect(files.indexOf('/xplat/nativescript/utils/@nativescript/core.ts')).toBe(-1);
87+
expect(files.indexOf('/xplat/nativescript/utils/@nativescript/ui.ts')).toBe(-1);
88+
expect(files.indexOf('/xplat/nativescript/utils/@nativescript/angular/core.ts')).toBe(-1);
89+
});
90+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export * from 'nativescript-angular/nativescript.module';
2+
export * from 'nativescript-angular/http-client';
3+
export * from 'nativescript-angular/platform';
4+
export * from 'nativescript-angular/platform-common';
5+
export * from 'nativescript-angular/router';
6+
export * from 'nativescript-angular/directives/dialogs';
7+
export * from 'nativescript-angular/element-registry';
8+
export * from 'nativescript-angular/forms';
9+
export * from 'nativescript-angular/common';
10+
export * from 'nativescript-angular/animations';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './core';

0 commit comments

Comments
 (0)