devextreme-schematics 1.2.19 → 1.2.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/add-app-template/index.ts +30 -0
- package/src/add-app-template/index_spec.ts +73 -0
- package/src/add-layout/files/e2e/src/app.e2e-spec.ts +14 -14
- package/src/add-layout/files/e2e/src/app.po.ts +11 -11
- package/src/add-layout/files/src/app/app-navigation.ts +1 -1
- package/src/add-layout/files/src/app/layouts/index.ts +3 -3
- package/src/add-layout/files/src/app/layouts/side-nav-inner-toolbar/side-nav-inner-toolbar.component.ts +1 -1
- package/src/add-layout/files/src/app/layouts/side-nav-outer-toolbar/side-nav-outer-toolbar.component.ts +1 -1
- package/src/add-layout/files/src/app/shared/components/change-password-form/change-password-form.component.html +8 -6
- package/src/add-layout/files/src/app/shared/components/create-account-form/create-account-form.component.html +9 -7
- package/src/add-layout/files/src/app/shared/components/footer/footer.component.ts +19 -19
- package/src/add-layout/files/src/app/shared/components/login-form/login-form.component.html +8 -6
- package/src/add-layout/files/src/app/shared/components/reset-password-form/reset-password-form.component.html +8 -6
- package/src/add-layout/index.ts +386 -0
- package/src/add-layout/index_spec.ts +340 -0
- package/src/add-sample-views/files/pages/home/home.component.ts +10 -10
- package/src/add-sample-views/files/pages/profile/profile.component.ts +33 -33
- package/src/add-sample-views/index.ts +141 -0
- package/src/add-sample-views/index_spec.ts +74 -0
- package/src/add-view/index.ts +165 -0
- package/src/add-view/index_spec.ts +155 -0
- package/src/install/index.ts +86 -0
- package/src/install/index_spec.ts +106 -0
- package/src/install/schema.json +1 -1
- package/src/utility/array.ts +3 -0
- package/src/utility/change.js +1 -1
- package/src/utility/change.ts +66 -0
- package/src/utility/latest-versions.js +2 -2
- package/src/utility/latest-versions.ts +6 -0
- package/src/utility/modify-json-file.ts +13 -0
- package/src/utility/project.ts +25 -0
- package/src/utility/routing.js +4 -4
- package/src/utility/routing.ts +44 -0
- package/src/utility/source.ts +16 -0
- package/src/utility/string.ts +5 -0
- package/src/utility/styles.ts +33 -0
@@ -0,0 +1,386 @@
|
|
1
|
+
import {
|
2
|
+
Rule,
|
3
|
+
SchematicContext,
|
4
|
+
Tree,
|
5
|
+
apply,
|
6
|
+
url,
|
7
|
+
move,
|
8
|
+
chain,
|
9
|
+
filter,
|
10
|
+
forEach,
|
11
|
+
mergeWith,
|
12
|
+
callRule,
|
13
|
+
FileEntry,
|
14
|
+
template
|
15
|
+
} from '@angular-devkit/schematics';
|
16
|
+
|
17
|
+
import { of } from 'rxjs';
|
18
|
+
|
19
|
+
import {
|
20
|
+
SourceFile
|
21
|
+
} from 'typescript';
|
22
|
+
|
23
|
+
import { strings } from '@angular-devkit/core';
|
24
|
+
|
25
|
+
import { join, basename } from 'path';
|
26
|
+
|
27
|
+
import {
|
28
|
+
getApplicationPath,
|
29
|
+
getSourceRootPath,
|
30
|
+
getProjectName
|
31
|
+
} from '../utility/project';
|
32
|
+
|
33
|
+
import {
|
34
|
+
humanize
|
35
|
+
} from '../utility/string';
|
36
|
+
|
37
|
+
import {
|
38
|
+
addStylesToApp
|
39
|
+
} from '../utility/styles';
|
40
|
+
|
41
|
+
import {
|
42
|
+
modifyJSONFile
|
43
|
+
} from '../utility/modify-json-file';
|
44
|
+
|
45
|
+
import {
|
46
|
+
NodeDependencyType,
|
47
|
+
addPackageJsonDependency
|
48
|
+
} from '@schematics/angular/utility/dependencies';
|
49
|
+
|
50
|
+
import {
|
51
|
+
NodePackageInstallTask
|
52
|
+
} from '@angular-devkit/schematics/tasks';
|
53
|
+
|
54
|
+
import { getSourceFile } from '../utility/source';
|
55
|
+
|
56
|
+
import {
|
57
|
+
applyChanges,
|
58
|
+
insertItemToArray
|
59
|
+
} from '../utility/change';
|
60
|
+
|
61
|
+
import {
|
62
|
+
hasComponentInRoutes,
|
63
|
+
getRoute,
|
64
|
+
findRoutesInSource
|
65
|
+
} from '../utility/routing';
|
66
|
+
|
67
|
+
import {
|
68
|
+
addImportToModule, addProviderToModule, insertImport
|
69
|
+
} from '@schematics/angular/utility/ast-utils';
|
70
|
+
|
71
|
+
import { getWorkspace } from '@schematics/angular/utility/config';
|
72
|
+
import { Change } from '@schematics/angular/utility/change';
|
73
|
+
|
74
|
+
const projectFilesSource = './files/src';
|
75
|
+
const workspaceFilesSource = './files';
|
76
|
+
|
77
|
+
function addScriptSafe(scripts: any, name: string, value: string) {
|
78
|
+
const currentValue = scripts[name];
|
79
|
+
|
80
|
+
if (!currentValue) {
|
81
|
+
scripts[name] = value;
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
|
85
|
+
const alterName = `origin-${name}`;
|
86
|
+
const safeValue = `npm run ${alterName} && ${value}`;
|
87
|
+
|
88
|
+
if (currentValue === value || currentValue === safeValue) {
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
|
92
|
+
scripts[alterName] = currentValue;
|
93
|
+
scripts[name] = safeValue;
|
94
|
+
}
|
95
|
+
|
96
|
+
function addBuildThemeScript() {
|
97
|
+
return (host: Tree) => {
|
98
|
+
modifyJSONFile(host, './package.json', config => {
|
99
|
+
const scripts = config['scripts'];
|
100
|
+
|
101
|
+
addScriptSafe(scripts, 'build-themes', 'devextreme build');
|
102
|
+
addScriptSafe(scripts, 'postinstall', 'npm run build-themes');
|
103
|
+
|
104
|
+
return config;
|
105
|
+
});
|
106
|
+
|
107
|
+
return host;
|
108
|
+
};
|
109
|
+
}
|
110
|
+
|
111
|
+
function addCustomThemeStyles(options: any, sourcePath: string) {
|
112
|
+
return (host: Tree) => {
|
113
|
+
modifyJSONFile(host, './angular.json', config => {
|
114
|
+
const stylesList = [
|
115
|
+
`${sourcePath}/dx-styles.scss`,
|
116
|
+
`${sourcePath}/themes/generated/theme.additional.css`,
|
117
|
+
`${sourcePath}/themes/generated/theme.base.css`,
|
118
|
+
'node_modules/devextreme/dist/css/dx.common.css'
|
119
|
+
];
|
120
|
+
|
121
|
+
return addStylesToApp(host, options.project, config, stylesList);
|
122
|
+
});
|
123
|
+
|
124
|
+
return host;
|
125
|
+
};
|
126
|
+
}
|
127
|
+
|
128
|
+
function updateBudgets(options: any) {
|
129
|
+
return (host: Tree) => {
|
130
|
+
modifyJSONFile(host, './angular.json', config => {
|
131
|
+
const projectName = getProjectName(host, options.project);
|
132
|
+
const budgets: any[] = config.projects[projectName].architect.build.configurations.production.budgets;
|
133
|
+
|
134
|
+
const budget = budgets.find((item) => item.type === 'initial');
|
135
|
+
if (budget) {
|
136
|
+
budget.maximumWarning = '4mb';
|
137
|
+
budget.maximumError = '7mb';
|
138
|
+
}
|
139
|
+
|
140
|
+
return config;
|
141
|
+
});
|
142
|
+
|
143
|
+
return host;
|
144
|
+
};
|
145
|
+
}
|
146
|
+
|
147
|
+
function addViewportToBody(sourcePath: string) {
|
148
|
+
return (host: Tree) => {
|
149
|
+
const indexPath = join(sourcePath, 'index.html');
|
150
|
+
let indexContent = host.read(indexPath)!.toString();
|
151
|
+
|
152
|
+
indexContent = indexContent.replace(/<body>/, '<body class="dx-viewport">');
|
153
|
+
host.overwrite(indexPath, indexContent);
|
154
|
+
|
155
|
+
return host;
|
156
|
+
};
|
157
|
+
}
|
158
|
+
|
159
|
+
function modifyFileRule(path: string, callback: (source: SourceFile) => Change[]) {
|
160
|
+
return (host: Tree) => {
|
161
|
+
const source = getSourceFile(host, path);
|
162
|
+
|
163
|
+
if (!source) {
|
164
|
+
return host;
|
165
|
+
}
|
166
|
+
|
167
|
+
const changes = callback(source);
|
168
|
+
|
169
|
+
return applyChanges(host, changes, path);
|
170
|
+
};
|
171
|
+
}
|
172
|
+
|
173
|
+
function updateAppModule(host: Tree, sourcePath: string) {
|
174
|
+
const appModulePath = sourcePath + 'app.module.ts';
|
175
|
+
|
176
|
+
const importSetter = (importName: string, path: string) => {
|
177
|
+
return (source: SourceFile) => {
|
178
|
+
return addImportToModule(source, appModulePath, importName, path);
|
179
|
+
};
|
180
|
+
};
|
181
|
+
|
182
|
+
const providerSetter = (importName: string, path: string) => {
|
183
|
+
return (source: SourceFile) => {
|
184
|
+
return addProviderToModule(source, appModulePath, importName, path);
|
185
|
+
};
|
186
|
+
};
|
187
|
+
|
188
|
+
const rules = [
|
189
|
+
modifyFileRule(appModulePath, importSetter('SideNavOuterToolbarModule', './layouts')),
|
190
|
+
modifyFileRule(appModulePath, importSetter('SideNavInnerToolbarModule', './layouts')),
|
191
|
+
modifyFileRule(appModulePath, importSetter('SingleCardModule', './layouts')),
|
192
|
+
modifyFileRule(appModulePath, importSetter('FooterModule', './shared/components')),
|
193
|
+
modifyFileRule(appModulePath, importSetter('ResetPasswordFormModule', './shared/components')),
|
194
|
+
modifyFileRule(appModulePath, importSetter('CreateAccountFormModule', './shared/components')),
|
195
|
+
modifyFileRule(appModulePath, importSetter('ChangePasswordFormModule', './shared/components')),
|
196
|
+
modifyFileRule(appModulePath, importSetter('LoginFormModule', './shared/components')),
|
197
|
+
modifyFileRule(appModulePath, providerSetter('AuthService', './shared/services')),
|
198
|
+
modifyFileRule(appModulePath, providerSetter('ScreenService', './shared/services')),
|
199
|
+
modifyFileRule(appModulePath, providerSetter('AppInfoService', './shared/services')),
|
200
|
+
modifyFileRule(appModulePath, importSetter('UnauthenticatedContentModule', './unauthenticated-content')),
|
201
|
+
];
|
202
|
+
|
203
|
+
if (!hasRoutingModule(host, sourcePath)) {
|
204
|
+
rules.push(modifyFileRule(appModulePath, importSetter('AppRoutingModule', './app-routing.module')));
|
205
|
+
}
|
206
|
+
|
207
|
+
return chain(rules);
|
208
|
+
}
|
209
|
+
|
210
|
+
function getComponentName(host: Tree, sourcePath: string) {
|
211
|
+
let name = '';
|
212
|
+
const index = 1;
|
213
|
+
|
214
|
+
if (!host.exists(sourcePath + 'app.component.ts')) {
|
215
|
+
name = 'app';
|
216
|
+
}
|
217
|
+
|
218
|
+
while (!name) {
|
219
|
+
const componentName = `app${index}`;
|
220
|
+
if (!host.exists(`${sourcePath}${componentName}.component.ts`)) {
|
221
|
+
name = componentName;
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
return name;
|
226
|
+
}
|
227
|
+
|
228
|
+
function hasRoutingModule(host: Tree, sourcePath: string) {
|
229
|
+
return host.exists(sourcePath + 'app-routing.module.ts');
|
230
|
+
}
|
231
|
+
|
232
|
+
function addPackagesToDependency(globalNgCliVersion: string) {
|
233
|
+
return (host: Tree) => {
|
234
|
+
addPackageJsonDependency(host, {
|
235
|
+
type: NodeDependencyType.Default,
|
236
|
+
name: '@angular/cdk',
|
237
|
+
version: globalNgCliVersion
|
238
|
+
});
|
239
|
+
|
240
|
+
return host;
|
241
|
+
};
|
242
|
+
}
|
243
|
+
|
244
|
+
function modifyContentByTemplate(
|
245
|
+
sourcePath: string,
|
246
|
+
templateSourcePath: string,
|
247
|
+
filePath: string | null,
|
248
|
+
templateOptions: any = {},
|
249
|
+
modifyContent?: (templateContent: string, currentContent: string, filePath: string ) => string)
|
250
|
+
: Rule {
|
251
|
+
return (host: Tree, context: SchematicContext) => {
|
252
|
+
const modifyIfExists = (fileEntry: FileEntry) => {
|
253
|
+
const fileEntryPath = join(sourcePath, fileEntry.path.toString());
|
254
|
+
if (!host.exists(fileEntryPath)) {
|
255
|
+
return fileEntry;
|
256
|
+
}
|
257
|
+
|
258
|
+
const templateContent = fileEntry.content!.toString();
|
259
|
+
let modifiedContent = templateContent;
|
260
|
+
|
261
|
+
const currentContent = host.read(fileEntryPath)!.toString();
|
262
|
+
if (modifyContent) {
|
263
|
+
modifiedContent = modifyContent(templateContent, currentContent, fileEntryPath);
|
264
|
+
}
|
265
|
+
|
266
|
+
// NOTE: Workaround for https://github.com/angular/angular-cli/issues/11337
|
267
|
+
if (modifiedContent !== currentContent) {
|
268
|
+
host.overwrite(fileEntryPath, modifiedContent);
|
269
|
+
}
|
270
|
+
return null;
|
271
|
+
};
|
272
|
+
|
273
|
+
const rules = [
|
274
|
+
filter(path => {
|
275
|
+
return !filePath || join('./', path) === join('./', filePath);
|
276
|
+
}),
|
277
|
+
template(templateOptions),
|
278
|
+
forEach(modifyIfExists),
|
279
|
+
move(sourcePath)
|
280
|
+
];
|
281
|
+
|
282
|
+
const modifiedSource = apply(url(templateSourcePath), rules);
|
283
|
+
const resultRule = mergeWith(modifiedSource);
|
284
|
+
|
285
|
+
return callRule(resultRule, of(host), context);
|
286
|
+
};
|
287
|
+
}
|
288
|
+
|
289
|
+
function updateDevextremeConfig(sourcePath: string) {
|
290
|
+
const devextremeConfigPath = '/devextreme.json';
|
291
|
+
const templateOptions = {
|
292
|
+
engine: 'angular',
|
293
|
+
sourcePath
|
294
|
+
};
|
295
|
+
|
296
|
+
const modifyConfig = (templateContent: string, currentContent: string) => {
|
297
|
+
const oldConfig = JSON.parse(currentContent);
|
298
|
+
const newConfig = JSON.parse(templateContent);
|
299
|
+
|
300
|
+
[].push.apply(oldConfig.build.commands, newConfig.build.commands);
|
301
|
+
|
302
|
+
return JSON.stringify(oldConfig, null, ' ');
|
303
|
+
};
|
304
|
+
|
305
|
+
return modifyContentByTemplate('./', workspaceFilesSource, devextremeConfigPath, templateOptions, modifyConfig);
|
306
|
+
}
|
307
|
+
|
308
|
+
const modifyRoutingModule = (host: Tree, routingModulePath: string) => {
|
309
|
+
// TODO: Try to use the isolated host to generate the result string
|
310
|
+
let source = getSourceFile(host, routingModulePath)!;
|
311
|
+
const importChange = insertImport(source, routingModulePath, 'LoginFormComponent', './shared/components');
|
312
|
+
const providerChanges = addProviderToModule(source, routingModulePath, 'AuthGuardService', './shared/services');
|
313
|
+
applyChanges(host, [ importChange, ...providerChanges], routingModulePath);
|
314
|
+
|
315
|
+
source = getSourceFile(host, routingModulePath)!;
|
316
|
+
const routes = findRoutesInSource(source)!;
|
317
|
+
if (!hasComponentInRoutes(routes, 'login-form')) {
|
318
|
+
const loginFormRoute = getRoute('login-form');
|
319
|
+
insertItemToArray(host, routingModulePath, routes, loginFormRoute);
|
320
|
+
}
|
321
|
+
};
|
322
|
+
|
323
|
+
export default function(options: any): Rule {
|
324
|
+
return (host: Tree) => {
|
325
|
+
const project = getProjectName(host, options.project);
|
326
|
+
const workspace = getWorkspace(host);
|
327
|
+
const prefix = workspace.projects[project].prefix;
|
328
|
+
const title = humanize(project);
|
329
|
+
const appPath = getApplicationPath(host, project);
|
330
|
+
const sourcePath = getSourceRootPath(host, project);
|
331
|
+
const layout = options.layout;
|
332
|
+
const override = options.resolveConflicts === 'override';
|
333
|
+
const componentName = override ? 'app' : getComponentName(host, appPath);
|
334
|
+
const pathToCss = sourcePath.replace(/\/?(\w)+\/?/g, '../');
|
335
|
+
const templateOptions = {
|
336
|
+
name: componentName,
|
337
|
+
layout,
|
338
|
+
title,
|
339
|
+
strings,
|
340
|
+
path: pathToCss,
|
341
|
+
prefix
|
342
|
+
};
|
343
|
+
|
344
|
+
const modifyContent = (templateContent: string, currentContent: string, filePath: string) => {
|
345
|
+
if (basename(filePath) === 'styles.scss') {
|
346
|
+
return `${currentContent}\n${templateContent}`;
|
347
|
+
}
|
348
|
+
|
349
|
+
if (basename(filePath) === 'app-routing.module.ts' && hasRoutingModule(host, appPath)) {
|
350
|
+
modifyRoutingModule(host, filePath);
|
351
|
+
return currentContent;
|
352
|
+
}
|
353
|
+
|
354
|
+
return templateContent;
|
355
|
+
};
|
356
|
+
|
357
|
+
const rules = [
|
358
|
+
modifyContentByTemplate(sourcePath, projectFilesSource, null, templateOptions, modifyContent),
|
359
|
+
updateDevextremeConfig(sourcePath),
|
360
|
+
updateAppModule(host, appPath),
|
361
|
+
addBuildThemeScript(),
|
362
|
+
addCustomThemeStyles(options, sourcePath),
|
363
|
+
addViewportToBody(sourcePath),
|
364
|
+
addPackagesToDependency(options.globalNgCliVersion)
|
365
|
+
];
|
366
|
+
|
367
|
+
if (options.updateBudgets) {
|
368
|
+
rules.push(updateBudgets(options));
|
369
|
+
}
|
370
|
+
|
371
|
+
if (!options.skipInstall) {
|
372
|
+
rules.push((_: Tree, context: SchematicContext) => {
|
373
|
+
context.addTask(new NodePackageInstallTask());
|
374
|
+
});
|
375
|
+
}
|
376
|
+
|
377
|
+
if (override) {
|
378
|
+
if (project === workspace.defaultProject) {
|
379
|
+
rules.push(modifyContentByTemplate('./', workspaceFilesSource, 'e2e/src/app.e2e-spec.ts', { title }));
|
380
|
+
rules.push(modifyContentByTemplate('./', workspaceFilesSource, 'e2e/src/app.po.ts'));
|
381
|
+
}
|
382
|
+
}
|
383
|
+
|
384
|
+
return chain(rules);
|
385
|
+
};
|
386
|
+
}
|